Develop and test OpticID for Vision Pro

Marco EidingerMarco Eidinger
3 min read

In this blog post, I'll explain how you, a Swift developer, can leverage Optic ID for authentication and how to test OpticID in the Vision Pro simulator.

Optic ID is a new, secure biometric system to authenticate Apple headset users. But this should not scare you. Because if you ever built an iOS app using FaceID, then you will recognize the APIs to interact with Optic ID 😊

Develop with OpticID

Apple's LocalAuthentication framework provides a universal API independent of which biometric authentication type is used. Therefore, you can use the existing APIs that you are familiar with.

First, you'll need to check if biometric authentication is possible on the device. It might be that the user did not enroll in OpticID. Do this by calling the canEvaluatePolicy(_:error:) method.

var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
    print(error?.localizedDescription ?? "Can't evaluate policy")
    // Fallback to ask for username and password.
    // ...
    return
}

Then, you can trigger the authentication by calling evaluatePolicy(\_:localizedReason:reply:).

do {
    try await context.evaluatePolicy(
        .deviceOwnerAuthenticationWithBiometrics,
        localizedReason: "Use Biometric Authentication to access the app")
    return "\(context.biometryType.name) authentication successful"
} catch LAError.userCancel {
    return "User canceled authentication"
} catch LAError.userFallback {
    return "User chose to use fallback authentication method"
} catch LAError.biometryLockout {
    return "Biometry locked due to too many failed attempts"
} catch {
    return "Authentication failed: \(error.localizedDescription)"
}

All the APIs you know from working with TouchID or FaceID also apply to OpticID.

The only addition I found was on LABiometryType. In the example above, I created an extension on this type to return a "localized" description. No surprise that you'll find a new enum value .opticID

extension LABiometryType {
    var name: String {
        switch self {
        case .none:
            return "None"
        case .touchID:
            return "Touch ID"
        case .faceID:
            return "Face ID"
        case .opticID:
            return "Optic ID"
        @unknown default:
            return "Unknown"
        }
    }
}

Here is a complete SwiftUI application example.

import LocalAuthentication
import SwiftUI

@main
struct OpticIDTestingApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var authenticationResult: String?
    let context = LAContext()

    var body: some View {
        VStack {
            Text("Biometric Authentication")
                .font(.title)
                .padding()

            Button("Authenticate") {
                Task {
                    authenticationResult = await authenticateWithBiometric()
                }
            }
            .padding()

            if let authenticationResult {
                Text(authenticationResult)
            }
        }
    }

    func authenticateWithBiometric() async -> String {
        let isSupportingBiometrics = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
        guard isSupportingBiometrics else {
            return "Biometric authentication not available on this device"
        }
        do {
            try await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Use Biometric Authentication to access the app")
            return "\(context.biometryType.name) authentication successful"
        } catch LAError.userCancel {
            return "User canceled authentication"
        } catch LAError.userFallback {
            return "User chose to use fallback authentication method"
        } catch LAError.biometryLockout {
            return "Biometry locked due to too many failed attempts"
        } catch {
            return "Authentication failed: \(error.localizedDescription)"
        }
    }
}

extension LABiometryType {
    var name: String {
        switch self {
        case .none:
            return "None"
        case .touchID:
            return "Touch ID"
        case .faceID:
            return "Face ID"
        case .opticID:
            return "Optic ID"
        @unknown default:
            return "Unknown"
        }
    }
}

Test OpticID in VisionPro simulator

The VisionOS simulator allows you to test OpticID.

Xcode 15 is still in beta, and you'll have to use the latest Beta 8 as it resolved several issues, e.g. even though Optic ID appears in the Simulator, it wasn't possible to simulate Optic ID enrollment. (112460069)

Test OpticID with the VisionPro simulator

Setting "Enrolled" before calling canEvaluatePolicy(_:error:) will ensure that true will be returned.

Choosing "Matching Eye" or "Non-matching Eye" after calling evaluatePolicy(\_:localizedReason:reply:) will complete the authentication process accordingly.

I created a YouTube video to illustrate the testing process better.

In the future, OpticID might even become relevant on iOS 🀯

9
Subscribe to my newsletter

Read articles from Marco Eidinger directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Marco Eidinger
Marco Eidinger

I am a Software Engineer working on open source and enterprise mobile SDKs for iOS and MacOS developers written in Swift. From πŸ‡©πŸ‡ͺ and happily living in πŸ‡ΊπŸ‡Έ