Swift Package Manager library for protecting selected WKWebView traffic with Approov on iOS.
The package injects a JavaScript bridge into the page, intercepts protected fetch, optional XMLHttpRequest, and form submissions, executes those requests through ApproovURLSession, and returns the response to the page. Requests outside the configured allowlist stay on the normal WebKit networking stack.
Important
Only endpoints declared in protectedEndpoints are proxied through native networking and protected by Approov.
| Item | Value |
|---|---|
| Platform | iOS 15+ |
| Package manager | Swift Package Manager |
| Library product | ApproovServiceWebView |
- Open your app project in Xcode.
- Choose
File->Add Package Dependencies... - Enter
https://github.com/approov/approov-service-ios-webview.git - Select the branch or version you want to pin.
- Add the
ApproovServiceWebViewproduct to your app target.
dependencies: [
.package(
url: "https://github.com/approov/approov-service-ios-webview.git",
branch: "main"
)
]Then add the product dependency to your target:
.target(
name: "MyApp",
dependencies: [
.product(
name: "ApproovServiceWebView",
package: "approov-service-ios-webview"
)
]
)Import the package in your app target:
import ApproovServiceWebView| Type | Purpose |
|---|---|
ApproovWebView |
SwiftUI host for a protected WKWebView. |
ApproovWebViewController |
UIKit host for the same bridge and request pipeline. |
ApproovWebViewFactory |
Low-level API for creating or installing an Approov bridge on a raw WKWebView. |
ApproovWebViewConfiguration |
Main integration surface for Approov setup, endpoint allowlisting, optional XHR interception, token header settings, fail-open policy, and native request mutation. |
ApproovWebViewProtectedEndpoint |
Scheme, host, path-prefix, and excluded-path-prefix matcher for protected traffic. |
ApproovWebViewContent |
Initial content source for the web view: request, HTML string, or file URL. |
ApproovServiceWebView re-exports the shared core types, so a single import ApproovServiceWebView is sufficient in app code.
Protected requests follow this path:
- The package injects its bridge script at document start.
- Matching page requests are serialized from JavaScript and passed to native code.
- Native execution runs through
ApproovURLSession, including your existing Approov mutator chain. - The response is marshalled back into the page runtime.
Unmatched requests continue through WebKit unchanged.
ApproovWebViewConfiguration is the main contract between the host app and the package.
| Property | Purpose |
|---|---|
approovConfig |
Approov onboarding string for ApproovService.initialize(...). |
protectedEndpoints |
Strict allowlist for traffic that should be routed through native networking. |
approovTokenHeaderName |
Header name used for the Approov token. Default: approov-token. |
approovTokenHeaderPrefix |
Optional header prefix, for example Bearer . |
approovDevelopmentKey |
Optional development key applied after initialization. |
allowRequestsWithoutApproovToken |
Controls fail-open vs fail-closed behavior when Approov cannot produce a token. |
interceptXMLHttpRequests |
Enables or disables JavaScript XMLHttpRequest interception. Default: true. |
configureApproovService |
Hook for one-time Approov setup beyond the default initialization path. |
mutateRequest |
Native-only request mutation point for secrets or headers that must not be exposed to page JavaScript. |
debugLoggingEnabled |
Enables opt-in debug OSLog output for the native bridge lifecycle. |
bridgeHandlerName |
WebKit message handler name used by the injected bridge. |
import ApproovServiceWebView
import SwiftUI
struct ProtectedWebExperience: View {
private let configuration = ApproovWebViewConfiguration(
approovConfig: "<your-approov-config>",
protectedEndpoints: [
ApproovWebViewProtectedEndpoint(
host: "api.example.com",
pathPrefix: "/v1/private"
)
],
debugLoggingEnabled: true,
approovDevelopmentKey: "<your-development-key>",
mutateRequest: { request in
var request = request
request.setValue("<api-key>", forHTTPHeaderField: "Api-Key")
return request
}
)
var body: some View {
ApproovWebView(
content: .request(
URLRequest(url: URL(string: "https://app.example.com")!)
),
configuration: configuration
)
}
}import ApproovServiceWebView
let controller = ApproovWebViewController(
content: .request(URLRequest(url: URL(string: "https://app.example.com")!)),
configuration: configuration
)Use ApproovWebViewFactory when your app wants the protected WKWebView
instance directly instead of going through the SwiftUI or UIKit host wrappers.
import ApproovServiceWebView
import WebKit
let configuration = ApproovWebViewConfiguration(
approovConfig: "<your-approov-config>",
protectedEndpoints: [
ApproovWebViewProtectedEndpoint(
host: "api.example.com",
pathPrefix: "/v1/private"
)
]
)
let webView = ApproovWebViewFactory.makeWebView(
configuration: configuration
)
// Load content whenever your app is ready.
webView.load(
URLRequest(url: URL(string: "https://app.example.com")!)
)import ApproovServiceWebView
import WebKit
let baseConfiguration = WKWebViewConfiguration()
baseConfiguration.userContentController = WKUserContentController()
baseConfiguration.websiteDataStore = .nonPersistent()
baseConfiguration.defaultWebpagePreferences.allowsContentJavaScript = true
let webView = WKWebView(frame: .zero, configuration: baseConfiguration)
webView.scrollView.bounces = true
webView.backgroundColor = .clear
ApproovWebViewFactory.install(
on: webView,
configuration: configuration
)
webView.load(
URLRequest(url: URL(string: "https://app.example.com")!)
)Note
If you attach Approov to an existing WKWebView, do it before the first
protected page load, or reload after installation. The bridge is injected at
document start and cannot retrofit a page that already finished loading.
Set debugLoggingEnabled: true in ApproovWebViewConfiguration to emit
debug-level OSLog entries for:
- bridge installation and reuse
- request receipt and native routing
- cookie synchronization boundaries
- lazy Approov initialization
- protected request execution and response handling
The debug logs intentionally avoid request bodies, cookie values, token values, and URL query strings.
- Use
protectedEndpointsto keep the native interception scope explicit and reviewable. - Use
excludedPathPrefixeswhen you want to protect an entire host or broad path while allowing static assets or other public subpaths to stay on the normal WebKit stack. - Set
interceptXMLHttpRequeststofalseif the host web app depends on native WebKit XHR behavior and only needs Approov protection forfetchor forms. - Keep secrets, API keys, and tenant-specific headers inside
mutateRequest, not in page JavaScript. - Use
configureApproovServiceif your app needs one-time Approov setup beyond a development key. - For reused base web views, install the bridge before the first protected navigation, or reload after installation.
- Keep
bridgeHandlerNameunique if your app already registers page-worldWKScriptMessageHandlers on the sameWKUserContentController. - Default behavior is fail-closed. Set
allowRequestsWithoutApproovTokenonly when your use case explicitly requires fail-open handling.
Example:
ApproovWebViewProtectedEndpoint(
host: "store.example.com",
pathPrefix: "/",
excludedPathPrefixes: [
"/assets/static"
]
)This package resolves and uses: