## Embed the Capture App in a native iOS App

The implementation:

- Launches the hosted Capture App inside your app
- Handles camera permissions correctly
- Grants WebKit-level media access (iOS 15+)
- Preserves a seamless user experience

## How it works

1. App screen loads.
2. Camera permission is checked.
3. If granted:
   - WebView is initialized.
   - Capture URL is loaded.
4. If denied:
   - User is directed to enable permission in Settings.
5. Web capture experience runs within the app.

## Requirements

### Minimum platform

- Xcode 14+
- iOS 14+ (iOS 15+ recommended)

### Required frameworks

Swift

```swift
import UIKit
import WebKit
import AVFoundation
```

### Required Info.plist Key

Add the following key:

Text

```text
NSCameraUsageDescription
```

Example:

```undefined
Camera access is required to complete the verification process.
```

Failure to include this key will cause runtime crashes when requesting camera permission.

## Step 1 - Request camera permission

Camera permission must be granted before loading the Capture App.

Swift

```swift
private func requestCameraPermission(completion: @escaping (Bool) -> Void) {
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized:
        completion(true)

case .notDetermined:
        AVCaptureDevice.requestAccess(for: .video) { granted in
            completion(granted)
        }

case .denied, .restricted:
        completion(false)

@unknown default:
        completion(false)
    }
}
```

### Behavior

- If authorized → continues immediately
- If first request → iOS permission prompt appears
- If denied → must redirect user to Settings

## Step 2 - Configure WKWebView

Configure the WebView for mobile rendering and inline media playback.

Swift

```swift
private func setupWebView() {
    let configuration = WKWebViewConfiguration()

let preferences = WKWebpagePreferences()
    preferences.preferredContentMode = .mobile
    configuration.defaultWebpagePreferences = preferences
    configuration.allowsInlineMediaPlayback = true

let webView = WKWebView(frame: .zero, configuration: configuration)
    webView.uiDelegate = self
    self.webView = webView

view.addSubview(webView)

webView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([\
        webView.topAnchor.constraint(equalTo: view.topAnchor),\
        webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\
        webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\
        webView.trailingAnchor.constraint(equalTo: view.trailingAnchor)\
    ])
}
```

### Key configuration details

- `preferredContentMode = .mobile` ensures mobile layout
- `allowsInlineMediaPlayback = true` prevents fullscreen media hijacking
- Fullscreen constraints provide consistent UX

## Step 3 - Load the Capture App URL

Swift

```swift
private func loadWebPage() {
    guard let webView = self.webView,
          let url = URL(string: "https://CAPTURE_APP_URL") else { return }

let request = URLRequest(url: url)
    webView.load(request)
}
```

### Requirements

- Must use HTTPS
- Replace "https://YOUR_CAPTURE_URL" with the provided Capture App URL
- Avoid force-unwrapping in production

## Step 4 - Handle denied permission

If camera access is denied, direct the user to Settings.

Swift

```swift
private func showPermissionAlert() {
    let alert = UIAlertController(
        title: "Camera Access Required",
        message: "Please enable camera access in Settings to continue.",
        preferredStyle: .alert
    )

alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { _ in
        self.openAppSettings()
    })

alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))

present(alert, animated: true)
}

private func openAppSettings() {
    guard let url = URL(string: UIApplication.openSettingsURLString),
          UIApplication.shared.canOpenURL(url) else { return }

UIApplication.shared.open(url)
}
```

## Step 5 - Grant WebKit media permission (iOS 15+)

On iOS 15+, WebKit requires explicit handling of media capture permissions.

Swift

```swift
@available(iOS 15.0, *)
func webView(_ webView: WKWebView,
             decideMediaCapturePermissionsFor origin: WKSecurityOrigin,
             initiatedBy frame: WKFrameInfo,
             type: WKMediaCaptureType) async -> WKPermissionDecision {
    return .grant
}
```

### Recommended security restriction

Restrict permission to trusted domains:

Swift

```swift
if origin.host == "verify.socure.com" {
    return .grant
}
return .deny
```

Never auto-grant permissions for unknown origins.

## Complete example ViewController

Swift

```swift
import UIKit
import WebKit
import AVFoundation

class ViewController: UIViewController, WKUIDelegate {

var webView: WKWebView?

override func viewDidLoad() {
        super.viewDidLoad()

requestCameraPermission { [weak self] granted in
            guard let self = self else { return }

DispatchQueue.main.async {
                if granted {
                    self.setupWebView()
                    self.loadWebPage()
                } else {
                    self.showPermissionAlert()
                }
            }
        }
    }

private func requestCameraPermission(completion: @escaping (Bool) -> Void) {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            completion(true)
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { granted in
                completion(granted)
            }
        default:
            completion(false)
        }
    }

private func setupWebView() {
        let config = WKWebViewConfiguration()
        let preferences = WKWebpagePreferences()
        preferences.preferredContentMode = .mobile
        config.defaultWebpagePreferences = preferences
        config.allowsInlineMediaPlayback = true

let webView = WKWebView(frame: .zero, configuration: config)
        webView.uiDelegate = self
        self.webView = webView
        view.addSubview(webView)

webView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([\
            webView.topAnchor.constraint(equalTo: view.topAnchor),\
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),\
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor)\
        ])
    }

private func loadWebPage() {
        guard let webView = webView,
              let url = URL(string: "https://YOUR_CAPTURE_URL") else { return }

webView.load(URLRequest(url: url))
    }

private func showPermissionAlert() {
        let alert = UIAlertController(
            title: "Camera Access Required",
            message: "Please enable camera access in Settings to continue.",
            preferredStyle: .alert
        )

alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { _ in
            self.openAppSettings()
        })

alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(alert, animated: true)
    }

private func openAppSettings() {
        guard let url = URL(string: UIApplication.openSettingsURLString),
              UIApplication.shared.canOpenURL(url) else { return }

UIApplication.shared.open(url)
    }

@available(iOS 15.0, *)
    func webView(_ webView: WKWebView,
                 decideMediaCapturePermissionsFor origin: WKSecurityOrigin,
                 initiatedBy frame: WKFrameInfo,
                 type: WKMediaCaptureType) async -> WKPermissionDecision {
        return .grant
    }
}
```

## Best Practices

- Restrict media permissions to trusted domains
- Persist evaluation identifiers before launching capture
- Validate redirect handling server-side
- Use in-app browsers for a seamless UX
