Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/signing-sdk/face-auth-ios/llms.txt

Use this file to discover all available pages before exploring further.

TAD Signing SDK integrates into your existing iOS app through a single Swift Package dependency and a modal view controller. This guide walks you through adding the package, creating your configuration, presenting the biometric flow, and handling the result.
1

Add TadSigningSDK as a Swift Package dependency

Open your Xcode project, go to File → Add Package Dependencies, and add TadSigningSDK.If you received the SDK as a local package, add it using a path-based reference in your Package.swift or project.yml:
packages:
  TadSigningSDK:
    path: /path/to/tad-core-signing-ios

targets:
  YourApp:
    dependencies:
      - package: TadSigningSDK
        product: TadSigningSDK
You also need two entries in your Info.plist:
<key>NSCameraUsageDescription</key>
<string>Required for face liveness check during biometric sign-in</string>

<key>NSFaceIDUsageDescription</key>
<string>Used to secure your Passkey during registration and sign-in</string>
And an Associated Domains entitlement for WebAuthn passkey binding:
webcredentials:signing.tadi.uz
Replace signing.tadi.uz with your own relying party domain.
2

Create TadSigningConfig

Create a shared configuration object that holds your API endpoint, public key, and relying party settings. The example below is taken directly from the demo app:
import Foundation
import TadSigningSDK

// Replace publicKeyPem with the ES512 public key provided by the backend team.
// Replace rpId and apiBaseUrl with the production values from your .env config.
enum SDKConfig {
    static let shared = TadSigningConfig(
        apiBaseUrl: URL(string: "https://signing.tadi.uz")!,
        publicKeyPem: """
        -----BEGIN PUBLIC KEY-----
        MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQANTC0w0ACO79+hPYfK5fEF9nAAztI
        zpD8M0UTyR4ON5DeT3nKY12noi9PVVCIK1uwImeqsWx56cc7kMmWC99RKV0Az3JC
        Zq5gRExuUzk+aWcoG3DppFy2hCwEVeuDTENz0P5Rhx/BBJ8Q4jWVOM2AM2W3SQ/q
        1nG5s8ixxX2BnPBTQ7w=
        -----END PUBLIC KEY-----
        """,
        rpId: "signing.tadi.uz",
        serviceName: "tad-signing-demo",
        blockProxy: true
    )
}
Replace publicKeyPem with the actual ES512 public key from your backend team before testing or deploying. The key above is the demo key and will not work with a production signing service.
Keep this configuration in a dedicated file (for example, SDKConfig.swift) and reference it as a singleton. See Configure TadSigningConfig for a full parameter reference.
3

Present TadSigningViewController in .register mode

Import TadSigningSDK in your SwiftUI view or UIViewController, then instantiate and present TadSigningViewController. The example below shows the registration flow:
import SwiftUI
import TadSigningSDK

struct ContentView: View {
    @State private var bankId    = "demo-user-001"
    @State private var otp       = "12345"
    @State private var isLoading = false

    var body: some View {
        Button("Register") {
            present(mode: .register)
        }
        .disabled(isLoading || bankId.isEmpty)
    }

    private func present(mode: TadSigningMode) {
        guard let vc = topViewController() else { return }
        isLoading = true

        let dto = otp.isEmpty ? [:] : ["otp": otp]

        let signingVC = TadSigningViewController(
            config: SDKConfig.shared,
            bankId: bankId,
            dto:    dto,
            mode:   mode
        ) { signingResult in
            isLoading = false
            // Handle result — see step 4
        }

        vc.present(signingVC, animated: true)
    }
}
To trigger signing instead of registration, pass mode: .sign. Everything else stays the same.
4

Handle the completion callback

The completion block receives a TadSigningResult with two cases. Handle both in your callback:
let signingVC = TadSigningViewController(
    config: SDKConfig.shared,
    bankId: bankId,
    dto:    dto,
    mode:   mode
) { signingResult in
    isLoading = false

    switch signingResult {
    case .success(let jwt, let requestId):
        if mode == .register {
            // Registration complete — no JWT is returned for register
            print("Registered. requestId: \(requestId)")
        } else {
            // Signing complete — send jwt to your backend for verification
            print("Signed. requestId: \(requestId)")
            print("JWT: \(jwt)")
        }

    case .failure(let code, let message):
        // Display the error to the user or log it
        print("Error \(code.rawValue): \(message)")
    }
}
On success, requestId is always present. The jwt is non-empty only in .sign mode — in .register mode it is an empty string. On failure, code is a typed error code and message is a human-readable description.

Helper: finding the top view controller

When presenting from SwiftUI, use a helper to get the top UIViewController from the current scene:
private func topViewController() -> UIViewController? {
    guard let scene = UIApplication.shared.connectedScenes
        .compactMap({ $0 as? UIWindowScene })
        .first(where: { $0.activationState == .foregroundActive }),
          let root = scene.windows.first(where: { $0.isKeyWindow })?.rootViewController
    else { return nil }

    var top = root
    while let presented = top.presentedViewController { top = presented }
    return top
}

Next steps

Configuration reference

Learn about all five TadSigningConfig parameters and how to manage them per environment.

API reference

Full reference for TadSigningViewController, modes, and result types.

Build docs developers (and LLMs) love