From 7373eabffb22cd17e7b3e31acc55cdb9cc46bfe8 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 6 Mar 2025 12:16:28 +0100 Subject: [PATCH] refactored the ClientProxy to: - throw an error during the init - to have its init directly instantiate the sync service and the providers Update ElementX/Sources/Services/UserSession/UserSessionStore.swift Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> renamed --- .../PinnedEventsTimelineFlowCoordinator.swift | 4 +- .../RoomFlowCoordinator.swift | 6 +- .../UserSessionFlowCoordinator.swift | 8 +- ElementX/Sources/Mocks/ClientProxyMock.swift | 4 +- .../Mocks/Generated/GeneratedMocks.swift | 18 +- .../JoinRoomScreenViewModel.swift | 24 +-- .../Sources/Services/Client/ClientProxy.swift | 188 ++++++++---------- .../Services/Client/ClientProxyProtocol.swift | 6 +- .../UserSession/UserSessionStore.swift | 23 ++- .../UserSessionStoreProtocol.swift | 1 + 10 files changed, 134 insertions(+), 148 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift index bcd0e1300..ab63e8e74 100644 --- a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift @@ -127,9 +127,7 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { } private func presentMessageForwarding(with forwardingItem: MessageForwardingItem) { - guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else { - fatalError() - } + let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider let stackCoordinator = NavigationStackCoordinator() diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index db8c04b3b..7abb8f100 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -696,7 +696,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.timelineController = timelineController let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy, - roomListPublisher: userSession.clientProxy.staticRoomSummaryProvider?.roomListPublisher.eraseToAnyPublisher() ?? Empty().replaceEmpty(with: []).eraseToAnyPublisher()) + roomListPublisher: userSession.clientProxy.staticRoomSummaryProvider.roomListPublisher.eraseToAnyPublisher()) let composerDraftService = ComposerDraftService(roomProxy: roomProxy, timelineItemfactory: timelineItemFactory) let parameters = RoomScreenCoordinatorParameters(clientProxy: userSession.clientProxy, @@ -1295,9 +1295,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } private func presentMessageForwarding(with forwardingItem: MessageForwardingItem) { - guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else { - fatalError() - } + let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider let stackCoordinator = NavigationStackCoordinator() diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 43bee1137..97073e939 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -831,9 +831,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // MARK: Global search private func presentGlobalSearch() { - guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else { - fatalError("Global search room summary provider unavailable") - } + let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider let coordinator = GlobalSearchScreenCoordinator(parameters: .init(roomSummaryProvider: roomSummaryProvider, mediaProvider: userSession.mediaProvider)) @@ -935,9 +933,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // MARK: Sharing private func presentRoomSelectionScreen(sharePayload: ShareExtensionPayload, animated: Bool) { - guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else { - fatalError() - } + let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider let stackCoordinator = NavigationStackCoordinator() diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 615ee090c..907029451 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -13,7 +13,7 @@ struct ClientProxyMockConfiguration { var userIDServerName: String? var userID: String = RoomMemberProxyMock.mockMe.userID var deviceID: String? - var roomSummaryProvider: RoomSummaryProviderProtocol? = RoomSummaryProviderMock(.init()) + var roomSummaryProvider: RoomSummaryProviderProtocol = RoomSummaryProviderMock(.init()) var roomDirectorySearchProxy: RoomDirectorySearchProxyProtocol? var recoveryState: SecureBackupRecoveryState = .enabled @@ -86,7 +86,7 @@ extension ClientProxyMock { resetIdentityReturnValue = .success(IdentityResetHandleSDKMock(.init())) roomForIdentifierClosure = { [weak self] identifier in - guard let room = self?.roomSummaryProvider?.roomListPublisher.value.first(where: { $0.id == identifier }) else { + guard let room = self?.roomSummaryProvider.roomListPublisher.value.first(where: { $0.id == identifier }) else { return nil } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 5bd03c2eb..bb5db1e65 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2267,9 +2267,21 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable { } var underlyingIgnoredUsersPublisher: CurrentValuePublisher<[String]?, Never>! var pusherNotificationClientIdentifier: String? - var roomSummaryProvider: RoomSummaryProviderProtocol? - var alternateRoomSummaryProvider: RoomSummaryProviderProtocol? - var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol? + var roomSummaryProvider: RoomSummaryProviderProtocol { + get { return underlyingRoomSummaryProvider } + set(value) { underlyingRoomSummaryProvider = value } + } + var underlyingRoomSummaryProvider: RoomSummaryProviderProtocol! + var alternateRoomSummaryProvider: RoomSummaryProviderProtocol { + get { return underlyingAlternateRoomSummaryProvider } + set(value) { underlyingAlternateRoomSummaryProvider = value } + } + var underlyingAlternateRoomSummaryProvider: RoomSummaryProviderProtocol! + var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol { + get { return underlyingStaticRoomSummaryProvider } + set(value) { underlyingStaticRoomSummaryProvider = value } + } + var underlyingStaticRoomSummaryProvider: StaticRoomSummaryProviderProtocol! var roomsToAwait: Set { get { return underlyingRoomsToAwait } set(value) { underlyingRoomsToAwait = value } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index fb83838d5..b7c741315 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -140,19 +140,19 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo roomInfo = invitedRoomProxy.info case .knocked(let knockedRoomProxy): roomInfo = knockedRoomProxy.info - if let roomSummaryProvider = clientProxy.staticRoomSummaryProvider { - membershipStateChangeCancellable = roomSummaryProvider.roomListPublisher - .compactMap { summaries -> Void? in - guard let roomSummary = summaries.first(where: { $0.id == roomInfo?.id }), - roomSummary.roomListItem.membership() != .knocked else { - return nil - } - return () + membershipStateChangeCancellable = clientProxy + .staticRoomSummaryProvider + .roomListPublisher + .compactMap { summaries -> Void? in + guard let roomSummary = summaries.first(where: { $0.id == roomInfo?.id }), + roomSummary.roomListItem.membership() != .knocked else { + return nil } - .sink { [weak self] in - Task { await self?.loadRoomDetails() } - } - } + return () + } + .sink { [weak self] in + Task { await self?.loadRoomDetails() } + } case .banned(let bannedRoomProxy): roomInfo = bannedRoomProxy.info default: diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 4f716cbb1..85dea85d0 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -20,13 +20,13 @@ class ClientProxy: ClientProxyProtocol { private let mediaLoader: MediaLoaderProtocol private let clientQueue: DispatchQueue - private var roomListService: RoomListService? + private var roomListService: RoomListService // periphery: ignore - only for retain private var roomListStateUpdateTaskHandle: TaskHandle? // periphery: ignore - only for retain private var roomListStateLoadingStateUpdateTaskHandle: TaskHandle? - private var syncService: SyncService? + private var syncService: SyncService // periphery: ignore - only for retain private var syncServiceStateUpdateTaskHandle: TaskHandle? @@ -43,10 +43,10 @@ class ClientProxy: ClientProxyProtocol { // These following summary providers both operate on the same allRooms() list but // can apply their own filtering and pagination - private(set) var roomSummaryProvider: RoomSummaryProviderProtocol? - private(set) var alternateRoomSummaryProvider: RoomSummaryProviderProtocol? + private(set) var roomSummaryProvider: RoomSummaryProviderProtocol + private(set) var alternateRoomSummaryProvider: RoomSummaryProviderProtocol - private(set) var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol? + private(set) var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol let notificationSettings: NotificationSettingsProxyProtocol @@ -139,7 +139,7 @@ class ClientProxy: ClientProxyProtocol { init(client: ClientProtocol, needsSlidingSyncMigration: Bool, networkMonitor: NetworkMonitorProtocol, - appSettings: AppSettings) async { + appSettings: AppSettings) async throws { self.client = client self.networkMonitor = networkMonitor self.appSettings = appSettings @@ -153,7 +153,22 @@ class ClientProxy: ClientProxyProtocol { secureBackupController = SecureBackupController(encryption: client.encryption()) self.needsSlidingSyncMigration = needsSlidingSyncMigration - + + let configuredAppService = try await ClientProxyServices(client: client, + actionsSubject: actionsSubject, + notificationSettings: notificationSettings, + appSettings: appSettings) + + syncService = configuredAppService.syncService + roomListService = configuredAppService.roomListService + roomSummaryProvider = configuredAppService.roomSummaryProvider + alternateRoomSummaryProvider = configuredAppService.alternateRoomSummaryProvider + staticRoomSummaryProvider = configuredAppService.staticRoomSummaryProvider + + syncServiceStateUpdateTaskHandle = createSyncServiceStateObserver(syncService) + roomListStateUpdateTaskHandle = createRoomListServiceObserver(roomListService) + roomListStateLoadingStateUpdateTaskHandle = createRoomListLoadingStateUpdateObserver(roomListService) + delegateHandle = client.setDelegate(delegate: ClientDelegateWrapper { [weak self] isSoftLogout in self?.hasEncounteredAuthError = true self?.actionsSubject.send(.receivedAuthError(isSoftLogout: isSoftLogout)) @@ -168,8 +183,6 @@ class ClientProxy: ClientProxyProtocol { } } .store(in: &cancellables) - - await configureAppService() loadUserAvatarURLFromCache() @@ -281,7 +294,7 @@ class ClientProxy: ClientProxyProtocol { MXLog.info("Starting sync") Task { - await syncService?.start() + await syncService.start() // If we are using OIDC we want to cache the account management URL in volatile memory on the SDK side. // To avoid the cache being invalidated while the app is backgrounded, we cache at every sync start. @@ -322,12 +335,6 @@ class ClientProxy: ClientProxyProtocol { restartTask = nil } - guard let syncService else { - MXLog.warning("No sync service to stop.") - completion?() - return - } - // Capture the sync service strongly as this method is called on deinit and so the // existence of self when the Task executes is questionable and would sometimes crash. // Note: This isn't strictly necessary now given the unwrap above, but leaving the code as @@ -493,12 +500,6 @@ class ClientProxy: ClientProxyProtocol { return room } - // Else wait for the visible rooms list to go into fully loaded - guard let roomSummaryProvider else { - MXLog.error("Rooms summary provider not setup yet") - return nil - } - if !roomSummaryProvider.statePublisher.value.isLoaded { _ = await roomSummaryProvider.statePublisher.values.first { $0.isLoaded } } @@ -524,33 +525,11 @@ class ClientProxy: ClientProxyProtocol { } func roomSummaryForIdentifier(_ identifier: String) -> RoomSummary? { - // the alternate room summary provider is not impacted by filtering - guard let provider = staticRoomSummaryProvider else { - MXLog.verbose("Missing room summary provider") - return nil - } - - guard let roomSummary = provider.roomListPublisher.value.first(where: { $0.id == identifier }) else { - MXLog.verbose("Missing room summary, count: \(provider.roomListPublisher.value.count)") - return nil - } - - return roomSummary + staticRoomSummaryProvider.roomListPublisher.value.first(where: { $0.id == identifier }) } func roomSummaryForAlias(_ alias: String) -> RoomSummary? { - // the alternate room summary provider is not impacted by filtering - guard let provider = staticRoomSummaryProvider else { - MXLog.verbose("Missing room summary provider") - return nil - } - - guard let roomSummary = provider.roomListPublisher.value.first(where: { $0.canonicalAlias == alias || $0.alternativeAliases.contains(alias) }) else { - MXLog.verbose("Missing room summary, count: \(provider.roomListPublisher.value.count)") - return nil - } - - return roomSummary + staticRoomSummaryProvider.roomListPublisher.value.first(where: { $0.canonicalAlias == alias || $0.alternativeAliases.contains(alias) }) } func loadUserDisplayName() async -> Result { @@ -839,63 +818,7 @@ class ClientProxy: ClientProxyProtocol { self.userAvatarURLSubject.value = urlString.flatMap(URL.init) } } - - private func configureAppService() async { - guard syncService == nil else { - fatalError("This shouldn't be called more than once") - } - - do { - let syncService = try await client - .syncService() - .withCrossProcessLock() - .withUtdHook(delegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject)) - .finish() - - let roomListService = syncService.roomListService() - - let roomMessageEventStringBuilder = RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(cacheKey: "roomList", - mentionBuilder: PlainMentionBuilder()), destination: .roomList) - let eventStringBuilder = RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID, shouldDisambiguateDisplayNames: false), - messageEventStringBuilder: roomMessageEventStringBuilder, - shouldDisambiguateDisplayNames: false, - shouldPrefixSenderName: true) - - roomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, - eventStringBuilder: eventStringBuilder, - name: "AllRooms", - shouldUpdateVisibleRange: true, - notificationSettings: notificationSettings, - appSettings: appSettings) - try await roomSummaryProvider?.setRoomList(roomListService.allRooms()) - - alternateRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, - eventStringBuilder: eventStringBuilder, - name: "AlternateAllRooms", - notificationSettings: notificationSettings, - appSettings: appSettings) - try await alternateRoomSummaryProvider?.setRoomList(roomListService.allRooms()) - - staticRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, - eventStringBuilder: eventStringBuilder, - name: "StaticAllRooms", - roomListPageSize: .max, - notificationSettings: notificationSettings, - appSettings: appSettings) - try await staticRoomSummaryProvider?.setRoomList(roomListService.allRooms()) - - self.syncService = syncService - self.roomListService = roomListService - - syncServiceStateUpdateTaskHandle = createSyncServiceStateObserver(syncService) - roomListStateUpdateTaskHandle = createRoomListServiceObserver(roomListService) - roomListStateLoadingStateUpdateTaskHandle = createRoomListLoadingStateUpdateObserver(roomListService) - - } catch { - MXLog.error("Failed building room list service with error: \(error)") - } - } - + private func createSyncServiceStateObserver(_ syncService: SyncService) -> TaskHandle { syncService.state(listener: SyncServiceStateObserverProxy { [weak self] state in guard let self else { return } @@ -967,11 +890,6 @@ class ClientProxy: ClientProxyProtocol { }() private func buildRoomForIdentifier(_ roomID: String) async -> RoomProxyType? { - guard let roomListService else { - MXLog.error("Failed retrieving room: \(roomID), room list service not set up") - return nil - } - do { let roomListItem = try roomListService.room(roomId: roomID) @@ -1205,3 +1123,57 @@ private class SendQueueRoomErrorListenerProxy: SendQueueRoomErrorListener { onErrorClosure(roomId, error) } } + +private struct ClientProxyServices { + let syncService: SyncService + let roomListService: RoomListService + let roomSummaryProvider: RoomSummaryProviderProtocol + let alternateRoomSummaryProvider: RoomSummaryProviderProtocol + let staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol + + init(client: ClientProtocol, + actionsSubject: PassthroughSubject, + notificationSettings: NotificationSettingsProxyProtocol, + appSettings: AppSettings) async throws { + let syncService = try await client + .syncService() + .withCrossProcessLock() + .withUtdHook(delegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject)) + .finish() + + let roomListService = syncService.roomListService() + + let roomMessageEventStringBuilder = RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(cacheKey: "roomList", + mentionBuilder: PlainMentionBuilder()), destination: .roomList) + let eventStringBuilder = try RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: client.userId(), shouldDisambiguateDisplayNames: false), + messageEventStringBuilder: roomMessageEventStringBuilder, + shouldDisambiguateDisplayNames: false, + shouldPrefixSenderName: true) + + roomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, + eventStringBuilder: eventStringBuilder, + name: "AllRooms", + shouldUpdateVisibleRange: true, + notificationSettings: notificationSettings, + appSettings: appSettings) + try await roomSummaryProvider.setRoomList(roomListService.allRooms()) + + alternateRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, + eventStringBuilder: eventStringBuilder, + name: "AlternateAllRooms", + notificationSettings: notificationSettings, + appSettings: appSettings) + try await alternateRoomSummaryProvider.setRoomList(roomListService.allRooms()) + + staticRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, + eventStringBuilder: eventStringBuilder, + name: "StaticAllRooms", + roomListPageSize: .max, + notificationSettings: notificationSettings, + appSettings: appSettings) + try await staticRoomSummaryProvider.setRoomList(roomListService.allRooms()) + + self.syncService = syncService + self.roomListService = roomListService + } +} diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 26bfbe787..eb659caf3 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -94,15 +94,15 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { var pusherNotificationClientIdentifier: String? { get } - var roomSummaryProvider: RoomSummaryProviderProtocol? { get } + var roomSummaryProvider: RoomSummaryProviderProtocol { get } /// Used for listing rooms that shouldn't be affected by the main `roomSummaryProvider` filtering /// But can still be filtered by queries, since this may be shared across multiple views, remember to reset /// The filtering state when you are done with it - var alternateRoomSummaryProvider: RoomSummaryProviderProtocol? { get } + var alternateRoomSummaryProvider: RoomSummaryProviderProtocol { get } /// Used for listing rooms, can't be filtered nor its state observed - var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol? { get } + var staticRoomSummaryProvider: StaticRoomSummaryProviderProtocol { get } var roomsToAwait: Set { get set } diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index c8967df1b..2e39c7da7 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -64,7 +64,7 @@ class UserSessionStore: UserSessionStoreProtocol { do { let session = try client.session() let userID = try client.userId() - let clientProxy = await setupProxyForClient(client, needsSlidingSyncMigration: false) + let clientProxy = try await setupProxyForClient(client, needsSlidingSyncMigration: false) keychainController.setRestorationToken(RestorationToken(session: session, sessionDirectories: sessionDirectories, @@ -138,17 +138,26 @@ class UserSessionStore: UserSessionStoreProtocol { MXLog.info("Set up session for user \(credentials.userID) at: \(credentials.restorationToken.sessionDirectories)") - return await .success(setupProxyForClient(client, needsSlidingSyncMigration: credentials.restorationToken.needsSlidingSyncMigration)) + return try await .success(setupProxyForClient(client, needsSlidingSyncMigration: credentials.restorationToken.needsSlidingSyncMigration)) + } catch UserSessionStoreError.failedSettingUpClientProxy(let error) { + // If this has failed, there is likely something wrong with the creation of the sync service + // There is nothing we can do, but at the same time we don't want the user to the get logged out + // So it's better to crash here and let the app restart + fatalError("Failed setting up the client proxy with error: \(error)") } catch { MXLog.error("Failed restoring login with error: \(error)") return .failure(.failedRestoringLogin) } } - private func setupProxyForClient(_ client: ClientProtocol, needsSlidingSyncMigration: Bool) async -> ClientProxyProtocol { - await ClientProxy(client: client, - needsSlidingSyncMigration: needsSlidingSyncMigration, - networkMonitor: networkMonitor, - appSettings: appSettings) + private func setupProxyForClient(_ client: ClientProtocol, needsSlidingSyncMigration: Bool) async throws -> ClientProxyProtocol { + do { + return try await ClientProxy(client: client, + needsSlidingSyncMigration: needsSlidingSyncMigration, + networkMonitor: networkMonitor, + appSettings: appSettings) + } catch { + throw UserSessionStoreError.failedSettingUpClientProxy(error) + } } } diff --git a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift index c099d597e..d834226d1 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift @@ -12,6 +12,7 @@ enum UserSessionStoreError: Error { case missingCredentials case failedRestoringLogin case failedSettingUpSession + case failedSettingUpClientProxy(Error) } // sourcery: AutoMockable