mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
App Lock: Don't rely on the app becoming active at startup. (#2175)
This commit is contained in:
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -124,8 +127,6 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
||||
self?.stateMachine.tryEvent(isEnabled ? .serviceEnabled : .serviceDisabled)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
configureStateMachine()
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
@@ -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)):
|
||||
@@ -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() }
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user