Native iOS (Swift)
Integrate FaceGuard into native iOS apps using WKWebView.
Setup
Add NSCameraUsageDescription to your Info.plist:
Info.plist
<key>NSCameraUsageDescription</key>
<string>FaceGuard needs camera access for face verification</string>
Implementation
FaceGuardViewController.swift
import WebKit
class FaceGuardViewController: UIViewController, WKScriptMessageHandler {
private var webView: WKWebView!
func startVerification(token: String) {
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = []
// Listen for postMessage events
config.userContentController.add(self, name: "surtHandler")
// Inject bridge: forward postMessage to native handler
let script = WKUserScript(
source: """
window.addEventListener('message', function(e) {
if (e.data && (e.data.type === 'surt:ready' || e.data.action === 'close')) {
window.webkit.messageHandlers.surtHandler.postMessage(JSON.stringify(e.data));
}
});
""",
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
config.userContentController.addUserScript(script)
webView = WKWebView(frame: view.bounds, configuration: config)
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(webView)
let url = URL(string: "https://faceguard.surt.com/intro?token=\(token)")!
webView.load(URLRequest(url: url))
}
func userContentController(
_ controller: WKUserContentController,
didReceive message: WKScriptMessage
) {
guard let body = message.body as? String,
let data = body.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
else { return }
if let action = json["action"] as? String, action == "close" {
let reason = json["reason"] as? String ?? ""
let confidence = json["confidence"] as? Double ?? 0
switch reason {
case "approved", "bypass_active":
handleApproved(confidence: confidence)
case "rejected":
handleRejected(confidence: confidence)
case "canceled":
handleCanceled()
case "error", "no_base_photo":
handleError(message: json["error"] as? String ?? reason)
default:
break
}
}
}
func handleApproved(confidence: Double) { /* Grant access */ }
func handleRejected(confidence: Double) { /* Deny access */ }
func handleCanceled() { /* User closed */ }
func handleError(message: String) { /* Show error */ }
}
How the Bridge Works
WKWebView doesn't natively receive postMessage events from iframes. The injected JavaScript script listens for message events and forwards them to the native WKScriptMessageHandler via window.webkit.messageHandlers.
Safe Areas
Use Auto Layout with safeAreaLayoutGuide to avoid the notch and home indicator.