mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Add support for Face ID/Touch ID to app lock. (#1966)
* Fix biometrics with low grace period and backgrounding before unlocked. * Trigger permissions alert when enabling Face ID.
This commit is contained in:
@@ -32,6 +32,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
||||
let userIndicatorController: UserIndicatorController
|
||||
let navigationCoordinator: NavigationRootCoordinator
|
||||
|
||||
/// A task used to await biometric unlock before showing the PIN screen.
|
||||
@CancellableTask private var unlockTask: Task<Void, Never>?
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
private let actionsSubject: PassthroughSubject<AppLockFlowCoordinatorAction, Never> = .init()
|
||||
@@ -44,6 +46,9 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.navigationCoordinator = navigationCoordinator
|
||||
|
||||
// Set the initial background state.
|
||||
showPlaceholder()
|
||||
|
||||
appLockService.disabledPublisher
|
||||
.sink { [weak self] _ in
|
||||
// When the service is disabled via a force logout, we need to remove the activity indicator.
|
||||
@@ -71,6 +76,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
||||
// MARK: - App unlock
|
||||
|
||||
private func applicationDidEnterBackground() {
|
||||
unlockTask = nil
|
||||
|
||||
guard appLockService.isEnabled else { return }
|
||||
|
||||
appLockService.applicationDidEnterBackground()
|
||||
@@ -80,12 +87,33 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
||||
private func applicationWillEnterForeground() {
|
||||
guard appLockService.isEnabled else { return }
|
||||
|
||||
if appLockService.computeNeedsUnlock(willEnterForegroundAt: .now) {
|
||||
showUnlockScreen()
|
||||
} else {
|
||||
guard appLockService.computeNeedsUnlock(willEnterForegroundAt: .now) else {
|
||||
// Reveal the app again if within the grace period.
|
||||
actionsSubject.send(.unlockApp)
|
||||
return
|
||||
}
|
||||
|
||||
// Show the relevant unlock mechanism.
|
||||
unlockTask = Task { [weak self] in
|
||||
guard let self else { return }
|
||||
await startUnlockFlow()
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the unlock flow, showing Touch ID/Face ID if available, transitioning to PIN unlock if it fails or isn't available.
|
||||
private func startUnlockFlow() async {
|
||||
if appLockService.biometricUnlockEnabled, appLockService.biometricUnlockTrusted {
|
||||
showPlaceholder() // For the unlock background.
|
||||
|
||||
if await appLockService.unlockWithBiometrics() {
|
||||
actionsSubject.send(.unlockApp)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
guard !Task.isCancelled else { return }
|
||||
|
||||
showUnlockScreen()
|
||||
}
|
||||
|
||||
/// Displays the unlock flow with the app's placeholder view to hide obscure the view hierarchy in the app switcher.
|
||||
|
||||
@@ -140,6 +140,9 @@ class AppLockSetupFlowCoordinator: FlowCoordinatorProtocol {
|
||||
showBiometricsPrompt()
|
||||
case (.createPIN, .settings):
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
// The above is fine for change pin, but not create PIN when biometrics are unavailable.
|
||||
// Need to track the two flows differently to call the alternative below.
|
||||
// showSettings()
|
||||
case (.biometricsPrompt, .settings):
|
||||
showSettings()
|
||||
case (.settings, .createPIN):
|
||||
|
||||
Reference in New Issue
Block a user