mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +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
|
/// States the flow can find itself in
|
||||||
enum State: StateType {
|
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
|
case initial
|
||||||
/// The app is in the foreground and visible to the user.
|
/// The app is in the foreground and visible to the user.
|
||||||
case unlocked
|
case unlocked
|
||||||
@@ -56,6 +57,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
/// Events that can be triggered on the flow state machine
|
/// Events that can be triggered on the flow state machine
|
||||||
enum Event: EventType {
|
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).
|
/// The app is resigning active (going into the app switcher, showing system UI like Face ID, permissions prompt etc).
|
||||||
case willResignActive
|
case willResignActive
|
||||||
/// The app is now backgrounded and not visible to the user.
|
/// 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.
|
// Set the initial state and start with the placeholder screen as the root view.
|
||||||
stateMachine = .init(state: initialState)
|
stateMachine = .init(state: initialState)
|
||||||
showPlaceholder()
|
configureStateMachine()
|
||||||
|
|
||||||
notificationCenter.publisher(for: UIApplication.willResignActiveNotification)
|
notificationCenter.publisher(for: UIApplication.willResignActiveNotification)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
@@ -124,8 +127,6 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
|||||||
self?.stateMachine.tryEvent(isEnabled ? .serviceEnabled : .serviceDisabled)
|
self?.stateMachine.tryEvent(isEnabled ? .serviceEnabled : .serviceDisabled)
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
configureStateMachine()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPresentable() -> AnyView {
|
func toPresentable() -> AnyView {
|
||||||
@@ -139,13 +140,16 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
|||||||
guard let self, appLockService.isEnabled else { return fromState }
|
guard let self, appLockService.isEnabled else { return fromState }
|
||||||
|
|
||||||
switch (fromState, event) {
|
switch (fromState, event) {
|
||||||
|
case (.initial, .start):
|
||||||
|
return .backgrounded
|
||||||
|
|
||||||
case (.unlocked, .willResignActive):
|
case (.unlocked, .willResignActive):
|
||||||
return .appObscured
|
return .appObscured
|
||||||
case (.appObscured, .didBecomeActive):
|
case (.appObscured, .didBecomeActive):
|
||||||
return .unlocked
|
return .unlocked
|
||||||
case (_, .didEnterBackground):
|
case (_, .didEnterBackground):
|
||||||
return .backgrounded
|
return .backgrounded
|
||||||
case (.backgrounded, .didBecomeActive), (.initial, .didBecomeActive):
|
case (.backgrounded, .didBecomeActive):
|
||||||
guard appLockService.computeNeedsUnlock(didBecomeActiveAt: .now) else { return .unlocked }
|
guard appLockService.computeNeedsUnlock(didBecomeActiveAt: .now) else { return .unlocked }
|
||||||
return isBiometricUnlockAvailable ? .attemptingBiometricUnlock : .attemptingPINUnlock
|
return isBiometricUnlockAvailable ? .attemptingBiometricUnlock : .attemptingPINUnlock
|
||||||
case (.attemptingBiometricUnlock, .didFinishBiometricUnlock(let result)):
|
case (.attemptingBiometricUnlock, .didFinishBiometricUnlock(let result)):
|
||||||
@@ -183,7 +187,7 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
|||||||
showPlaceholder()
|
showPlaceholder()
|
||||||
case (_, .backgrounded):
|
case (_, .backgrounded):
|
||||||
appLockService.applicationDidEnterBackground()
|
appLockService.applicationDidEnterBackground()
|
||||||
showPlaceholder() // Double call but just to be safe.
|
showPlaceholder() // Double call but just to be safe. Useful at app launch.
|
||||||
case (_, .attemptingBiometricUnlock):
|
case (_, .attemptingBiometricUnlock):
|
||||||
showPlaceholder() // For the unlock background. Triple call but just to be safe.
|
showPlaceholder() // For the unlock background. Triple call but just to be safe.
|
||||||
Task { await self.attemptBiometricUnlock() }
|
Task { await self.attemptBiometricUnlock() }
|
||||||
@@ -203,6 +207,8 @@ class AppLockFlowCoordinator: CoordinatorProtocol {
|
|||||||
stateMachine.addErrorHandler { context in
|
stateMachine.addErrorHandler { context in
|
||||||
fatalError("Unexpected transition from `\(context.fromState)` to `\(context.toState)` with event `\(String(describing: context.event))`.")
|
fatalError("Unexpected transition from `\(context.fromState)` to `\(context.toState)` with event `\(String(describing: context.event))`.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateMachine.tryEvent(.start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - App unlock
|
// MARK: - App unlock
|
||||||
|
|||||||
Reference in New Issue
Block a user