Soft logout removal (#672)

* Remove isSoftLogout from everywhere except the client delegate
- bump the RustSDK to v1.0.43
- cleanup the app coordinator state machine and login/logout flows

* Merge `succeededSigningIn` and `succeededRestoringSession` into `createdUserSession` in order to simplify the logic
This commit is contained in:
Stefan Ceriu 2023-03-06 14:58:50 +02:00 committed by GitHub
parent 1fe2e7e882
commit e4117e3205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 34 additions and 122 deletions

View File

@ -95,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "852bb356fbd2b5053f17b3c6c27c8c098cf5f055",
"version" : "1.0.42-alpha"
"revision" : "32645162ea39badc586d610d2ada6bbc52f398ba",
"version" : "1.0.43-alpha"
}
},
{

View File

@ -154,7 +154,6 @@ class AppCoordinator: AppCoordinatorProtocol {
userSessionStore.reset()
}
// swiftlint:disable:next cyclomatic_complexity
private func setupStateMachine() {
stateMachine.addTransitionHandler { [weak self] context in
guard let self else { return }
@ -162,25 +161,19 @@ class AppCoordinator: AppCoordinatorProtocol {
switch (context.fromState, context.event, context.toState) {
case (.initial, .startWithAuthentication, .signedOut):
self.startAuthentication()
case (.signedOut, .succeededSigningIn, .signedIn):
case (.signedOut, .createdUserSession, .signedIn):
self.setupUserSession()
case (.initial, .startWithExistingSession, .restoringSession):
self.restoreUserSession()
case (.restoringSession, .failedRestoringSession, .signedOut):
self.showLoginErrorToast()
self.presentSplashScreen()
case (.restoringSession, .succeededRestoringSession, .signedIn):
case (.restoringSession, .createdUserSession, .signedIn):
self.setupUserSession()
case (_, .signOut, .signingOut):
self.logout(isSoftLogout: false)
case (.signingOut, .completedSigningOut, .signedOut):
self.tearDownUserSession()
self.presentSplashScreen()
case (_, .remoteSignOut(let isSoftLogout), .remoteSigningOut):
self.logout(isSoftLogout: isSoftLogout)
case (.remoteSigningOut(let isSoftLogout), .completedSigningOut, .signedOut):
self.tearDownUserSession()
self.presentSplashScreen(isSoftLogout: isSoftLogout)
case (_, .signOut(let isSoft), .signingOut):
self.logout(isSoft: isSoft)
case (.signingOut, .completedSigningOut(let isSoft), .signedOut):
self.presentSplashScreen(isSoftLogout: isSoft)
default:
fatalError("Unknown transition: \(context)")
}
@ -196,11 +189,7 @@ class AppCoordinator: AppCoordinatorProtocol {
switch await userSessionStore.restoreUserSession() {
case .success(let userSession):
self.userSession = userSession
if userSession.isSoftLogout {
stateMachine.processEvent(.remoteSignOut(isSoft: true))
} else {
stateMachine.processEvent(.succeededRestoringSession)
}
stateMachine.processEvent(.createdUserSession)
case .failure:
MXLog.error("Failed to restore an existing session.")
stateMachine.processEvent(.failedRestoringSession)
@ -243,12 +232,9 @@ class AppCoordinator: AppCoordinatorProtocol {
switch result {
case .signedIn(let session):
self.userSession = session
self.stateMachine.processEvent(.succeededSigningIn)
self.stateMachine.processEvent(.createdUserSession)
case .clearAllData:
// clear user data
self.userSessionStore.logout(userSession: self.userSession)
self.userSession = nil
self.startAuthentication()
self.stateMachine.processEvent(.signOut(isSoft: false))
}
}
@ -266,7 +252,7 @@ class AppCoordinator: AppCoordinatorProtocol {
userSessionFlowCoordinator.callback = { [weak self] action in
switch action {
case .signOut:
self?.stateMachine.processEvent(.signOut)
self?.stateMachine.processEvent(.signOut(isSoft: false))
}
}
@ -277,7 +263,7 @@ class AppCoordinator: AppCoordinatorProtocol {
navigationRootCoordinator.setRootCoordinator(navigationSplitCoordinator)
}
private func logout(isSoftLogout: Bool) {
private func logout(isSoft: Bool) {
showLoadingIndicator()
defer {
@ -287,8 +273,8 @@ class AppCoordinator: AppCoordinatorProtocol {
userSession.clientProxy.stopSync()
userSessionFlowCoordinator?.stop()
guard !isSoftLogout else {
stateMachine.processEvent(.completedSigningOut)
guard !isSoft else {
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
return
}
@ -298,8 +284,9 @@ class AppCoordinator: AppCoordinatorProtocol {
// regardless of the result, clear user data
userSessionStore.logout(userSession: userSession)
tearDownUserSession()
stateMachine.processEvent(.completedSigningOut)
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
}
}
@ -361,11 +348,7 @@ class AppCoordinator: AppCoordinatorProtocol {
guard let self else { return }
switch callback {
case .didReceiveAuthError(let isSoftLogout):
self.stateMachine.processEvent(.remoteSignOut(isSoft: isSoftLogout))
case .updateRestoreTokenNeeded:
if let userSession = self.userSession {
_ = self.userSessionStore.refreshRestorationToken(for: userSession)
}
self.stateMachine.processEvent(.signOut(isSoft: isSoftLogout))
default:
break
}
@ -463,7 +446,7 @@ class AppCoordinator: AppCoordinatorProtocol {
extension AppCoordinator: AuthenticationCoordinatorDelegate {
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator, didLoginWithSession userSession: UserSessionProtocol) {
self.userSession = userSession
stateMachine.processEvent(.succeededSigningIn)
stateMachine.processEvent(.createdUserSession)
}
}

View File

@ -31,32 +31,27 @@ class AppCoordinatorStateMachine {
case signedIn
/// Processing a sign out request
case signingOut
/// Processing a remote sign out
case remoteSigningOut(isSoft: Bool)
case signingOut(isSoft: Bool)
}
/// Events that can be triggered on the AppCoordinator state machine
enum Event: EventType {
/// Start the `AppCoordinator` by showing authentication.
case startWithAuthentication
/// Signing in succeeded
case succeededSigningIn
/// Start the `AppCoordinator` by restoring an existing account.
case startWithExistingSession
/// Restoring session succeeded.
case succeededRestoringSession
/// Restoring session failed.
case failedRestoringSession
/// A session has been created
case createdUserSession
/// Request sign out
case signOut
/// Remote sign out.
case remoteSignOut(isSoft: Bool)
case signOut(isSoft: Bool)
/// Signing out completed
case completedSigningOut
case completedSigningOut(isSoft: Bool)
}
private let stateMachine: StateMachine<State, Event>
@ -72,21 +67,18 @@ class AppCoordinatorStateMachine {
private func configure() {
stateMachine.addRoutes(event: .startWithAuthentication, transitions: [.initial => .signedOut])
stateMachine.addRoutes(event: .succeededSigningIn, transitions: [.signedOut => .signedIn])
stateMachine.addRoutes(event: .createdUserSession, transitions: [.signedOut => .signedIn])
stateMachine.addRoutes(event: .startWithExistingSession, transitions: [.initial => .restoringSession])
stateMachine.addRoutes(event: .succeededRestoringSession, transitions: [.restoringSession => .signedIn])
stateMachine.addRoutes(event: .createdUserSession, transitions: [.restoringSession => .signedIn])
stateMachine.addRoutes(event: .failedRestoringSession, transitions: [.restoringSession => .signedOut])
stateMachine.addRoutes(event: .signOut, transitions: [.any => .signingOut])
stateMachine.addRoutes(event: .completedSigningOut, transitions: [.signingOut => .signedOut])
// Transitions with associated values need to be handled through `addRouteMapping`
stateMachine.addRouteMapping { event, fromState, _ in
switch (event, fromState) {
case (.remoteSignOut(let isSoft), _):
return .remoteSigningOut(isSoft: isSoft)
case (.completedSigningOut, .remoteSigningOut):
case (.signOut(let isSoft), _):
return .signingOut(isSoft: isSoft)
case (.completedSigningOut, .signingOut):
return .signedOut
default:
return nil

View File

@ -35,11 +35,6 @@ private class WeakClientProxyWrapper: ClientDelegate, SlidingSyncObserver {
clientProxy?.didReceiveAuthError(isSoftLogout: isSoftLogout)
}
func didUpdateRestoreToken() {
MXLog.info("Did update restoration token")
clientProxy?.didUpdateRestoreToken()
}
// MARK: - SlidingSyncDelegate
func didReceiveSyncUpdate(summary: UpdateSummary) {
@ -98,10 +93,6 @@ class ClientProxy: ClientProxyProtocol {
}
}
var isSoftLogout: Bool {
client.isSoftLogout()
}
var deviceId: String? {
do {
return try client.deviceId()
@ -126,7 +117,7 @@ class ClientProxy: ClientProxyProtocol {
func startSync() {
MXLog.info("Starting sync")
guard !client.isSoftLogout(), slidingSyncObserverToken == nil else {
guard slidingSyncObserverToken == nil else {
return
}
@ -382,10 +373,6 @@ class ClientProxy: ClientProxyProtocol {
fileprivate func didReceiveAuthError(isSoftLogout: Bool) {
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
}
fileprivate func didUpdateRestoreToken() {
callbacks.send(.updatedRestoreToken)
}
fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
callbacks.send(.receivedSyncUpdate)

View File

@ -21,7 +21,6 @@ import MatrixRustSDK
enum ClientProxyCallback {
case receivedSyncUpdate
case receivedAuthError(isSoftLogout: Bool)
case updatedRestoreToken
}
enum ClientProxyError: Error {
@ -69,8 +68,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
var userID: String { get }
var isSoftLogout: Bool { get }
var deviceId: String? { get }
var homeserver: String { get }

View File

@ -22,7 +22,6 @@ class MockClientProxy: ClientProxyProtocol {
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
let userID: String
let isSoftLogout = false
let deviceId: String? = nil
let homeserver = ""
let restorationToken: RestorationToken? = nil

View File

@ -20,7 +20,6 @@ struct MockUserSession: UserSessionProtocol {
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil
var userID: String { clientProxy.userID }
var isSoftLogout: Bool { clientProxy.isSoftLogout }
var deviceID: String? { clientProxy.deviceId }
var homeserver: String { clientProxy.homeserver }
let clientProxy: ClientProxyProtocol

View File

@ -21,10 +21,8 @@ class UserSession: UserSessionProtocol {
private var cancellables = Set<AnyCancellable>()
private var checkForSessionVerificationControllerCancellable: AnyCancellable?
private var authErrorCancellable: AnyCancellable?
private var restoreTokenUpdateCancellable: AnyCancellable?
var userID: String { clientProxy.userID }
var isSoftLogout: Bool { clientProxy.isSoftLogout }
var deviceID: String? { clientProxy.deviceId }
var homeserver: String { clientProxy.homeserver }
@ -39,7 +37,6 @@ class UserSession: UserSessionProtocol {
setupSessionVerificationWatchdog()
setupAuthErrorWatchdog()
setupRestoreTokenUpdateWatchdog()
}
// MARK: - Private
@ -102,21 +99,4 @@ class UserSession: UserSessionProtocol {
private func tearDownAuthErrorWatchdog() {
authErrorCancellable = nil
}
// MARK: Restore Token Update Watchdog
private func setupRestoreTokenUpdateWatchdog() {
restoreTokenUpdateCancellable = clientProxy.callbacks
.receive(on: DispatchQueue.main)
.sink { [weak self] callback in
if case .updatedRestoreToken = callback {
self?.callbacks.send(.updateRestoreTokenNeeded)
self?.tearDownRestoreTokenUpdateWatchdog()
}
}
}
private func tearDownRestoreTokenUpdateWatchdog() {
restoreTokenUpdateCancellable = nil
}
}

View File

@ -21,7 +21,6 @@ enum UserSessionCallback {
case sessionVerificationNeeded
case didVerifySession
case didReceiveAuthError(isSoftLogout: Bool)
case updateRestoreTokenNeeded
}
protocol UserSessionProtocol {
@ -29,8 +28,6 @@ protocol UserSessionProtocol {
var userID: String { get }
var deviceID: String? { get }
var isSoftLogout: Bool { get }
var clientProxy: ClientProxyProtocol { get }
var mediaProvider: MediaProviderProtocol { get }

View File

@ -29,7 +29,6 @@ extension MatrixRustSDK.Session: Codable {
userId: try container.decode(String.self, forKey: .userId),
deviceId: try container.decode(String.self, forKey: .deviceId),
homeserverUrl: try container.decode(String.self, forKey: .homeserverUrl),
isSoftLogout: try container.decode(Bool.self, forKey: .isSoftLogout),
slidingSyncProxy: try container.decode(String.self, forKey: .slidingSyncProxy))
}
@ -40,11 +39,10 @@ extension MatrixRustSDK.Session: Codable {
try container.encode(userId, forKey: .userId)
try container.encode(deviceId, forKey: .deviceId)
try container.encode(homeserverUrl, forKey: .homeserverUrl)
try container.encode(isSoftLogout, forKey: .isSoftLogout)
try container.encode(slidingSyncProxy, forKey: .slidingSyncProxy)
}
enum CodingKeys: String, CodingKey {
case accessToken, refreshToken, userId, deviceId, homeserverUrl, isSoftLogout, slidingSyncProxy
case accessToken, refreshToken, userId, deviceId, homeserverUrl, slidingSyncProxy
}
}

View File

@ -37,7 +37,6 @@ class KeychainControllerTests: XCTestCase {
userId: "userId",
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
isSoftLogout: false,
slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: username)
@ -53,7 +52,6 @@ class KeychainControllerTests: XCTestCase {
userId: "userId",
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
isSoftLogout: false,
slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: username)
XCTAssertEqual(keychain.restorationTokens().count, 1, "The keychain should have 1 restoration token.")
@ -75,7 +73,6 @@ class KeychainControllerTests: XCTestCase {
userId: "userId",
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
isSoftLogout: false,
slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
}
@ -96,7 +93,6 @@ class KeychainControllerTests: XCTestCase {
userId: "userId",
deviceId: "deviceId",
homeserverUrl: "homeserverUrl",
isSoftLogout: false,
slidingSyncProxy: "https://my.sync.proxy"))
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
}

View File

@ -69,20 +69,4 @@ final class UserSessionTests: XCTestCase {
controller.callbacks.send(.finished)
waitForExpectations(timeout: 1.0)
}
func test_whenUserSessionReceivesUpdatedRestoreToken_updateRestoreTokenNeededEventReceived() throws {
let expectation = expectation(description: "UpdatedRestoreToken expectation")
userSession.callbacks.sink { callback in
switch callback {
case .updateRestoreTokenNeeded:
expectation.fulfill()
default:
break
}
}
.store(in: &cancellables)
clientProxy.callbacks.send(.updatedRestoreToken)
waitForExpectations(timeout: 1.0)
}
}

View File

@ -40,7 +40,7 @@ include:
packages:
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.0.42-alpha
exactVersion: 1.0.43-alpha
# path: ../matrix-rust-sdk
DesignKit:
path: DesignKit