Developing Touch ID Biometric Authorization in iOS App
Touch ID is older than Face ID, but still relevant: iPhone SE 3rd gen, iPad mini 6, iPad 10th gen with side sensor — all active devices in real user base. LAContext is the same, but behavior differs in details that cause production issues.
Touch ID specifics vs Face ID
Main difference — multi-finger registration. User can have up to five fingerprints. With biometryType == .touchID and successful authentication, you don't know which finger was used — API doesn't provide this. For most scenarios normal, but in enterprise apps with audit logs sometimes want to log "which device was used". Touch ID doesn't allow this by design.
Second difference — fallback degradation speed. Wet fingers, gloves, cuts — Touch ID more often goes to fallback than Face ID. This means .userFallback must be handled well, not just show "Enter password" button without explanation.
Common error: developer checks biometryType once at startup and caches result. User adds new fingerprint in settings — cache stale, Keychain record with .biometryCurrentSet invalidated, app crashes with errSecItemNotFound (-25300) when trying to get token. Correct: create new LAContext before each authentication attempt and don't store context longer than one transaction.
Implementation
Policy same — .deviceOwnerAuthenticationWithBiometrics. But for Touch ID especially important localizedFallbackTitle parameter on LAContext. If set empty string "" — fallback button hidden completely. If not set — shows "Enter Password" (system text). Set custom: "Login with PIN code" or "Use app password" — depending on what you implemented.
Secret storage in Keychain under Touch ID:
var error: Unmanaged<CFError>?
guard let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
[.biometryCurrentSet, .privateKeyUsage],
&error
) else { /* handle */ }
Flag .privateKeyUsage added if secret used for crypto operations (request signing). For simple token storage .biometryCurrentSet enough.
Side Touch ID (iPad, iPhone SE)
On iPad mini 6 and iPad 10th gen sensor built into power button. Unlock animation different — user applies finger to side button, not bottom. This affects how you position UI hints. biometryType returns .touchID in both cases — no API differences, only UX copywriting.
Testing and edge cases
Mandatory test:
- Fingerprint not recognized three times → lockout → correct fallback transition
- Touch ID disabled in device settings →
.biometryNotAvailable - Passcode not set →
canEvaluatePolicyreturnsfalsewith.passcodeNotSeterror - App goes to background while waiting for Touch ID →
.systemCancel
On simulator emulate via Features > Touch ID > Matching Touch / Non-matching Touch.
Timeframe
Touch ID authentication implementation with Keychain storage, all error states handling, and unit tests — 2–5 business days. If need integration with existing auth module or migration from UserDefaults to Keychain — add 1–2 days for audit and refactoring.







