> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withpersona.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withpersona.com/_mcp/server.

# Quickstart: iOS WKWebView

> **Warning**
>
> #### Native SDKs Recommended
>
> We strongly recommend using our native mobile SDKs (Android, iOS) instead of WebView where possible. WebView integrations are more difficult to support due to performance constraints of WebView technology
>
> For more information, see [Native mobile integration vs. WebView](/webview-vs-native-mobile-integration).

Below are some code snippets to get you integrating with the WebView API as soon as possible.

![webview](https://assets.withpersona.com/f_auto,q_auto/developer-docs/images/webview-ios.png)

## Demo App

If you just want to kick the tires on the WebView, we have an open source example app available at [persona-id/persona-ios-webview](https://github.com/persona-id/persona-ios-webview) that you can clone and open in Xcode.

## Example Code

To use WKWebView to launch the inquiry flow from a `UIViewController` with the options laid out on [WebView Flow](/webview-flow).

**`PersonaViewController.swift`**

```swift PersonaViewController.swift
// Create the web view and show it
private lazy var webView: WKWebView = {
  let config = WKWebViewConfiguration()
  // allowsInlineMediaPlayback is required, as it defaults to false on some devices like iPads.
  // If unset, will cause the camera capture flow to be fullscreen instead of inline.
  config.allowsInlineMediaPlayback = true

  let webView = WKWebView(frame: .zero, configuration: config)
  return webView
}()
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = false
webView.scrollView.bounces = false
// Add the web view and set up its contstraints
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    webView.topAnchor.constraint(equalTo: view.topAnchor),
    webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])

// Add the Persona configuration options as query items.
// See the Persona docs (<http://documentation.withpersona.com)> for full documentation.
var components = URLComponents(string: "<https://withpersona.com/verify")>
components?.queryItems = [
    URLQueryItem(name: "is-webview", value: "true"),
    URLQueryItem(name: "environment-id", value: "<your template ID starting with env_>"),
    URLQueryItem(name: "template-id", value: "<your template ID starting with itmpl_>"),
    URLQueryItem(name: "reference-id", value: "myReference"),
    URLQueryItem(name: "redirect-uri", value: "<https://myRedirectUri.com>"),
    // optional - specify theme for new inquiry
    // URLQueryItem(name: "theme-id", value: "the_Z7S2Ltor9fp2oEGXMLXBPsHa"),
]

// Create and load the Persona URL request
guard let urlString = components?.string, let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
```

When the WebView Flow is completed the `WKWebView` is redirected to the `redirect-uri` provided above. On a successful completion, the `inquiry-id` and `reference-id` (if set) will be appended as query string parameters More information in on these attributes can be found in [Redirecting On Complete or Cancel](/docs/redirecting-on-complete).

**`swift`**

```swift swift
extension MyViewController: WKNavigationDelegate {

    /// Handle navigation actions from the web view.
    func webView(_ webView: WKWebView, 
        decidePolicyFor navigationAction: WKNavigationAction, 
        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        // Check if we are being redirected to our `redirectUri`. This happens once verification is completed.
        guard let redirectUri = navigationAction.request.url?.absoluteString, redirectUri.starts(with: "<https://myRedirectUri.com")> else {
            // We're not being redirected, so load the URL.
            decisionHandler(.allow)
            return
        }

        // At this point we're done, so we don't need to load the URL.
        decisionHandler(.cancel)

        // Get the inquiryId from the query string parameters.
        guard let queryParams = parseQueryParameters(url: navigationAction.request.url),
            let inquiryId = queryParams["inquiry-id"] else {

            // If we do not have an inquiry ID we know we have failed verification.
            // You will likely want to transition the view here to show this.
            return
        }

        // If we have an inquiry ID we know we have passed verification.
        // You will likely want to transition the view here to show this.
    }
}
```

## Common Issues

> **Warning**
>
> #### Webkit Inline Media Playback
>
> Please ensure `allowsInlineMediaPlayback` is enabled when creating a webview on a webkit browser (mobile Safari). This defaults to false and the camera preview will incorrectly open as a fullscreen live broadcast.

> **Warning**
>
> #### Camera Configuration
>
> The WebView Flow requires access to the device camera. Please include the `NSCameraUsageDescription` key your app's `Info.plist` file as described in the [Apple Developer documentation](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios).

> **Warning**
>
> #### Allow External Network Requests
>
> Persona makes external network calls within the Inquiry Flow that need to be allowlisted for the flow to properly function. Certain frameworks such as [Cordova](https://cordova.apache.org/docs/en/11.x/guide/appdev/allowlist/index.html#network-request-allow-list) require such requests to be allow listed. Please include `*.withpersona.com/*` in such an allow list if needed.