Mobile

iOS

Present the verification flow from UIKit or SwiftUI (iOS 15+) and await a typed OkycResult.

View as Markdown

Version0.1.1

Install

No third-party dependencies (UIKit, WebKit, AVFoundation, SwiftUI).

Swift Package Manager: in Xcode, choose File → Add Package Dependencies…, enter https://github.com/okyc-team/okyc-ios and add the OKYC product. In a Package.swift:

Package.swift
// Package.swift of your app (or Xcode → File → Add Package Dependencies… → https://github.com/okyc-team/okyc-ios).
dependencies: [
    .package(url: "https://github.com/okyc-team/okyc-ios.git", from: "0.1.0"),
],
targets: [
    .target(name: "YourApp", dependencies: [.product(name: "OKYC", package: "okyc-ios")]),
]

CocoaPods:

Podfile
pod 'OKYC', '~> 0.1'

Permissions

The flow uses the camera (documents, selfie) and may use the microphone. Add both usage strings to your Info.plist:

Info.plist
<!-- The flow uses the camera (documents, selfie) and may ask for the microphone (liveness). -->
<key>NSCameraUsageDescription</key>
<string>We use the camera to capture your documents and a selfie to verify your identity.</string>
<key>NSMicrophoneUsageDescription</key>
<string>The verification flow may request microphone access for the liveness check.</string>

Start the flow

Create the session on your backend and pass its url to the app.

OkycExampleApp.swift
let config = OkycConfig(
    sessionURL: url,        // the `url` your backend got from POST /v1/sessions
    locale: nil,            // "en" or "hi"; nil keeps the session's language
    theme: theme,           // .light, .dark or .system (default)
    blockScreenshots: true, // cover the flow in the app switcher and while recording (default)
    autoCloseDelay: 2,      // seconds the result screen stays visible after `complete`; nil = stay open
    presentation: .sheet    // or .fullScreen
)
OkycExampleApp.swift
.okycVerification(
    isPresented: $presenting, // set to true to present the flow
    config: config,
    onEvent: { event in handleEvent(event) },
    onResult: { result in handleResult(result) } // called exactly once per presentation
)

Also available: OkycViewController(config:) with a delegate (okyc(_:didReceive:), okyc(_:didFinishWith:)), the SwiftUI modifier .okycVerification(isPresented:config:onEvent:onResult:), and OkycView to embed the flow in your own container.

Options

OptionTypeDefaultDescription
sessionURLstringrequiredThe url returned by POST /v1/sessions. Never embed a secret API key in an app.
localestringnullFlow language (en, hi); must be one of the workflow's languages. Sent as ?locale=.
themelight | dark | system"system"Colour scheme of the flow. Sent as ?theme=.
allowedHostsstring[][]Extra hosts the WebView may load (self-hosting).
blockScreenshotsbooleantrueAndroid: FLAG_SECURE. iOS: cover the flow in the app switcher and while the screen is recorded.
autoCloseDelayduration_ms2000 msHow long the result screen stays visible after complete before the SDK closes and returns completed. 0 or null = return the result immediately (no result screen).

iOS also has: presentation (see the SDK README).

Results and events

OkycExampleApp.swift
private func handleResult(_ result: OkycResult) {
    switch result {
    case let .completed(sessionId, status):
        // The applicant finished. Confirm the decision on your backend (webhook or GET /v1/sessions/:id).
        show("completed \(sessionId) \(status)")
    case let .cancelled(sessionId):
        // Closed before finishing; the same session URL can be opened again.
        show("cancelled \(sessionId ?? "-")")
    case let .failed(code, message, _):
        // `code` is one of OkycErrorCode (network_error, load_failed, …).
        show("failed \(code) \(message)")
    }
}
OkycExampleApp.swift
private func handleEvent(_ event: OkycEvent) {
    // Progress only (ids and statuses, never personal data). The result arrives in handleResult.
    switch event.kind {
    case .stepStarted, .stepCompleted: log.append("\(event.kind.rawValue) \(event.payload["step"] ?? "")")
    case .error: log.append("error \(event.payload["code"] ?? "")") // recoverable; the flow keeps running
    default: log.append(event.kind.rawValue)
    }
}

OkycResult is .completed(sessionId, status), .cancelled(sessionId) or .failed(code, message, sessionId) (sessionId is nil if the flow never loaded). Swipe-down on the sheet, ✕, or the flow's own exit returns .cancelled. Event payload values are strings.

Failure codes on iOS:

CodeMeaning
invalid_configMissing or malformed session URL or configuration.
load_failedThe flow page failed to load (HTTP error or blank page).
network_errorNo connection or DNS/TLS failure while loading.
navigation_blockedRepeated navigation to non-allowlisted hosts.
permission_deniedThe camera permission was permanently denied and the flow cannot continue.
flow_errorThe flow reported an unrecoverable error.
webview_unavailableThe WebView renderer crashed or is unavailable.
presentation_failedNo view controller was available to present the flow.

Theming and locale

theme (.light, .dark, .system) is sent as ?theme= and applied to the native chrome. locale is sent as ?locale= before the #t= fragment and must be one of the workflow's languages. presentation is .sheet (swipe down cancels) or .fullScreen.

Security defaults

  • The session URL must use https; plain http is accepted only for localhost, 127.0.0.1 and ::1 (otherwise invalid_config).
  • Messages are accepted only from the main frame of the session origin and must match protocol v1.
  • The flow origin and DigiLocker stay inside the web view; other https links open in Safari; everything else is blocked.
  • Camera and microphone are granted only to the flow origin, after the iOS permission.
  • A non-persistent website data store, wiped when the flow finishes. The session URL is never logged.
  • Web Inspector only in DEBUG builds.
  • iOS can't block screenshots; blockScreenshots hides the flow in the app switcher and while the screen is recorded or mirrored.

Troubleshooting

SymptomFix
OkycConfig(sessionURLString:) returns nilThe string isn't a valid session url.
presentation_failedStart from a view controller that is in the window hierarchy.
Camera prompt never appearsNSCameraUsageDescription is missing from Info.plist.

Going live

  • Create sessions with a sk_live_… key on your backend; nothing changes in the app.
  • Decide on the webhook, not on .completed.

On this page