User session state machine crashes (#281)

* Fix #277 - UserSession state machine crashes on foregrounding and add the bug report screen to it
* Rename home screen actions
This commit is contained in:
Stefan Ceriu
2022-11-02 12:44:51 +02:00
committed by GitHub
parent d398b96abd
commit b270b8a30e
4 changed files with 76 additions and 37 deletions
@@ -23,10 +23,10 @@ struct HomeScreenCoordinatorParameters {
}
enum HomeScreenCoordinatorAction {
case presentRoom(roomIdentifier: String)
case presentSettings
case presentBugReport
case verifySession
case presentRoomScreen(roomIdentifier: String)
case presentSettingsScreen
case presentFeedbackScreen
case presentSessionVerificationScreen
case signOut
}
@@ -63,11 +63,11 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
switch action {
case .selectRoom(let roomIdentifier):
self.callback?(.presentRoom(roomIdentifier: roomIdentifier))
self.callback?(.presentRoomScreen(roomIdentifier: roomIdentifier))
case .userMenu(let action):
self.processUserMenuAction(action)
case .verifySession:
self.callback?(.verifySession)
self.callback?(.presentSessionVerificationScreen)
}
}
}
@@ -87,11 +87,11 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
private func processUserMenuAction(_ action: HomeScreenViewUserMenuAction) {
switch action {
case .settings:
callback?(.presentSettings)
callback?(.presentSettingsScreen)
case .inviteFriends:
presentInviteFriends()
case .feedback:
callback?(.presentBugReport)
callback?(.presentFeedbackScreen)
case .signOut:
callback?(.signOut)
}
@@ -72,6 +72,12 @@ class UserSessionFlowCoordinator: Coordinator {
self.presentSettingsScreen()
case (.settingsScreen, .dismissedSettingsScreen, .homeScreen):
self.dismissSettingsScreen()
case (.homeScreen, .feedbackScreen, .feedbackScreen):
self.presentFeedbackScreen()
case (.feedbackScreen, .dismissedFeedbackScreen, .homeScreen):
self.dismissFeedbackScreen()
case (_, .resignActive, .suspended):
self.pause()
case (_, .becomeActive, _):
@@ -109,13 +115,13 @@ class UserSessionFlowCoordinator: Coordinator {
guard let self else { return }
switch action {
case .presentRoom(let roomIdentifier):
case .presentRoomScreen(let roomIdentifier):
self.stateMachine.processEvent(.showRoomScreen(roomId: roomIdentifier))
case .presentSettings:
case .presentSettingsScreen:
self.stateMachine.processEvent(.showSettingsScreen)
case .presentBugReport:
self.presentBugReportScreen()
case .verifySession:
case .presentFeedbackScreen:
self.stateMachine.processEvent(.feedbackScreen)
case .presentSessionVerificationScreen:
self.stateMachine.processEvent(.showSessionVerificationScreen)
case .signOut:
self.callback?(.signOut)
@@ -254,20 +260,18 @@ class UserSessionFlowCoordinator: Coordinator {
alert.addAction(UIAlertAction(title: ElementL10n.no, style: .cancel))
alert.addAction(UIAlertAction(title: ElementL10n.yes, style: .default) { [weak self] _ in
self?.presentBugReportScreen()
self?.stateMachine.processEvent(.feedbackScreen)
})
navigationRouter.present(alert, animated: true)
}
private func presentBugReportScreen(for image: UIImage? = nil) {
private func presentFeedbackScreen(for image: UIImage? = nil) {
let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService,
screenshot: image)
let coordinator = BugReportCoordinator(parameters: parameters)
coordinator.completion = { [weak self, weak coordinator] in
guard let self, let coordinator = coordinator else { return }
self.navigationRouter.dismissModule(animated: true)
self.remove(childCoordinator: coordinator)
coordinator.completion = { [weak self] in
self?.stateMachine.processEvent(.dismissedFeedbackScreen)
}
add(childCoordinator: coordinator)
@@ -275,21 +279,23 @@ class UserSessionFlowCoordinator: Coordinator {
let navController = ElementNavigationController(rootViewController: coordinator.toPresentable())
navController.navigationBar.topItem?.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
target: self,
action: #selector(dismissBugReportScreen))
action: #selector(handleFeedbackScreenCancellation))
navController.isModalInPresentation = true
navigationRouter.present(navController, animated: true)
}
@objc
private func dismissBugReportScreen() {
MXLog.debug("dismissBugReportScreen")
guard let bugReportCoordinator = childCoordinators.first(where: { $0 is BugReportCoordinator }) else {
private func handleFeedbackScreenCancellation() {
stateMachine.processEvent(.dismissedFeedbackScreen)
}
private func dismissFeedbackScreen() {
guard let coordinator = childCoordinators.first(where: { $0 is BugReportCoordinator }) else {
return
}
navigationRouter.dismissModule()
remove(childCoordinator: bugReportCoordinator)
remove(childCoordinator: coordinator)
}
// MARK: - Application State
@@ -33,6 +33,9 @@ class UserSessionFlowCoordinatorStateMachine {
/// Showing the session verification flows
case sessionVerificationScreen
/// Showing the session verification flows
case feedbackScreen
/// Showing the settings screen
case settingsScreen
@@ -51,15 +54,20 @@ class UserSessionFlowCoordinatorStateMachine {
/// The room screen has been dismissed
case dismissedRoomScreen
/// Request the start of the session verification flow
case showSessionVerificationScreen
/// Session verification has finished
case dismissedSessionVerificationScreen
/// Request presentation of the settings screen
case showSettingsScreen
/// The settings screen has been dismissed
case dismissedSettingsScreen
/// Request presentation of the feedback screen
case feedbackScreen
/// The feedback screen has been dismissed
case dismissedFeedbackScreen
/// Request the start of the session verification flow
case showSessionVerificationScreen
/// Session verification has finished
case dismissedSessionVerificationScreen
/// Application goes into inactive state
case resignActive
@@ -68,13 +76,14 @@ class UserSessionFlowCoordinatorStateMachine {
}
private let stateMachine: StateMachine<State, Event>
private var stateBeforeSuspension: State = .initial
private var stateBeforeSuspension: State?
init() {
stateMachine = StateMachine(state: .initial)
configure()
}
// swiftlint:disable:next cyclomatic_complexity
private func configure() {
stateMachine.addRoutes(event: .start, transitions: [.initial => .homeScreen])
@@ -84,23 +93,46 @@ class UserSessionFlowCoordinatorStateMachine {
return .roomScreen(roomId: roomId)
case (.dismissedRoomScreen, .roomScreen):
return .homeScreen
case (.resignActive, _):
self.stateBeforeSuspension = fromState
return .suspended
case (.becomeActive, .suspended):
return self.stateBeforeSuspension
case (.showSettingsScreen, .homeScreen):
return .settingsScreen
case (.dismissedSettingsScreen, .settingsScreen):
return .homeScreen
case (.feedbackScreen, .homeScreen):
return .feedbackScreen
case (.dismissedFeedbackScreen, .feedbackScreen):
return .homeScreen
case (.showSessionVerificationScreen, .homeScreen):
return .sessionVerificationScreen
case (.dismissedSessionVerificationScreen, .sessionVerificationScreen):
return .homeScreen
case (.resignActive, _):
self.stateBeforeSuspension = fromState
return .suspended
case (.becomeActive, _):
// Cannot become active if not previously suspended
// Happens when the app is backgrounded before the session is setup
guard let previousState = self.stateBeforeSuspension else {
return self.stateMachine.state
}
return previousState
default:
return nil
}
}
addTransitionHandler { context in
if let event = context.event {
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
} else {
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
}
}
}
/// Attempt to move the state machine to another state through an event
+1
View File
@@ -0,0 +1 @@
Fix state machine crashes when backgrounding the app before the user session is setup