Skip to content

Commit

Permalink
App Lock: Don't rely on the app becoming active at startup. (#2175)
Browse files Browse the repository at this point in the history
  • Loading branch information
pixlwave authored Nov 27, 2023
1 parent 36224a3 commit 45e4cbc
Showing 1 changed file with 12 additions and 6 deletions.
18 changes: 12 additions & 6 deletions ElementX/Sources/FlowCoordinators/AppLockFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {

/// States the flow can find itself in
enum State: StateType {
/// The initial state before the app has launched.
/// The initial state before the app has launched. If the user hasn't enabled
/// App Lock, then the flow will continue to remain in this state after launch.
case initial
/// The app is in the foreground and visible to the user.
case unlocked
Expand All @@ -56,6 +57,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {

/// Events that can be triggered on the flow state machine
enum Event: EventType {
/// Starts the flow while the app is launching in the background.
case start
/// The app is resigning active (going into the app switcher, showing system UI like Face ID, permissions prompt etc).
case willResignActive
/// The app is now backgrounded and not visible to the user.
Expand Down Expand Up @@ -99,7 +102,7 @@ class AppLockFlowCoordinator: CoordinatorProtocol {

// Set the initial state and start with the placeholder screen as the root view.
stateMachine = .init(state: initialState)
showPlaceholder()
configureStateMachine()

notificationCenter.publisher(for: UIApplication.willResignActiveNotification)
.sink { [weak self] _ in
Expand All @@ -124,8 +127,6 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
self?.stateMachine.tryEvent(isEnabled ? .serviceEnabled : .serviceDisabled)
}
.store(in: &cancellables)

configureStateMachine()
}

func toPresentable() -> AnyView {
Expand All @@ -139,13 +140,16 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
guard let self, appLockService.isEnabled else { return fromState }

switch (fromState, event) {
case (.initial, .start):
return .backgrounded

case (.unlocked, .willResignActive):
return .appObscured
case (.appObscured, .didBecomeActive):
return .unlocked
case (_, .didEnterBackground):
return .backgrounded
case (.backgrounded, .didBecomeActive), (.initial, .didBecomeActive):
case (.backgrounded, .didBecomeActive):
guard appLockService.computeNeedsUnlock(didBecomeActiveAt: .now) else { return .unlocked }
return isBiometricUnlockAvailable ? .attemptingBiometricUnlock : .attemptingPINUnlock
case (.attemptingBiometricUnlock, .didFinishBiometricUnlock(let result)):
Expand Down Expand Up @@ -183,7 +187,7 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
showPlaceholder()
case (_, .backgrounded):
appLockService.applicationDidEnterBackground()
showPlaceholder() // Double call but just to be safe.
showPlaceholder() // Double call but just to be safe. Useful at app launch.
case (_, .attemptingBiometricUnlock):
showPlaceholder() // For the unlock background. Triple call but just to be safe.
Task { await self.attemptBiometricUnlock() }
Expand All @@ -203,6 +207,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
stateMachine.addErrorHandler { context in
fatalError("Unexpected transition from `\(context.fromState)` to `\(context.toState)` with event `\(String(describing: context.event))`.")
}

stateMachine.tryEvent(.start)
}

// MARK: - App unlock
Expand Down

0 comments on commit 45e4cbc

Please sign in to comment.