diff --git a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift index 3b9b9a3d00ee09ec4dd23ddc24fec105f94946fe..36b604362e0f07c67b0a1e626a519241f692271c 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift @@ -1,8 +1,8 @@ import ComposableArchitecture extension AlertState { - public static func confirmAccountDeletion() -> AlertState<HomeAction> { - AlertState<HomeAction>( + public static func confirmAccountDeletion() -> AlertState<HomeComponent.Action> { + AlertState<HomeComponent.Action>( title: TextState("Delete Account"), message: TextState("This will permanently delete your account and can't be undone."), buttons: [ @@ -12,8 +12,8 @@ extension AlertState { ) } - public static func accountDeletionFailed(_ error: Error) -> AlertState<HomeAction> { - AlertState<HomeAction>( + public static func accountDeletionFailed(_ error: Error) -> AlertState<HomeComponent.Action> { + AlertState<HomeComponent.Action>( title: TextState("Error"), message: TextState(error.localizedDescription), buttons: [] diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift new file mode 100644 index 0000000000000000000000000000000000000000..18212bf0ca916cffff86f5a4ee6cb09f4d220359 --- /dev/null +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeComponent.swift @@ -0,0 +1,292 @@ +import AppCore +import BackupFeature +import Combine +import ComposableArchitecture +import ComposablePresentation +import ContactsFeature +import Foundation +import RegisterFeature +import UserSearchFeature +import XCTestDynamicOverlay +import XXClient +import XXMessengerClient +import XXModels + +public struct HomeComponent: ReducerProtocol { + public struct State: Equatable { + public init( + failure: String? = nil, + isNetworkHealthy: Bool? = nil, + networkNodesReport: NodeRegistrationReport? = nil, + isDeletingAccount: Bool = false, + alert: AlertState<Action>? = nil, + register: RegisterComponent.State? = nil, + contacts: ContactsComponent.State? = nil, + userSearch: UserSearchComponent.State? = nil, + backup: BackupComponent.State? = nil + ) { + self.failure = failure + self.isNetworkHealthy = isNetworkHealthy + self.isDeletingAccount = isDeletingAccount + self.alert = alert + self.register = register + self.contacts = contacts + self.userSearch = userSearch + self.backup = backup + } + + public var failure: String? + public var isNetworkHealthy: Bool? + public var networkNodesReport: NodeRegistrationReport? + public var isDeletingAccount: Bool + public var alert: AlertState<Action>? + public var register: RegisterComponent.State? + public var contacts: ContactsComponent.State? + public var userSearch: UserSearchComponent.State? + public var backup: BackupComponent.State? + } + + public enum Action: Equatable { + public enum Messenger: Equatable { + case start + case didStartRegistered + case didStartUnregistered + case failure(NSError) + } + + public enum NetworkMonitor: Equatable { + case start + case stop + case health(Bool) + case nodes(NodeRegistrationReport) + } + + public enum DeleteAccount: Equatable { + case buttonTapped + case confirmed + case success + case failure(NSError) + } + + case messenger(Messenger) + case networkMonitor(NetworkMonitor) + case deleteAccount(DeleteAccount) + case didDismissAlert + case didDismissRegister + case userSearchButtonTapped + case didDismissUserSearch + case contactsButtonTapped + case didDismissContacts + case backupButtonTapped + case didDismissBackup + case register(RegisterComponent.Action) + case contacts(ContactsComponent.Action) + case userSearch(UserSearchComponent.Action) + case backup(BackupComponent.Action) + } + + public init() {} + + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.dbManager) var dbManager: DBManager + @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue> + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> + + public var body: some ReducerProtocol<State, Action> { + Reduce { state, action in + enum NetworkHealthEffectId {} + enum NetworkNodesEffectId {} + + let messenger = self.messenger + + switch action { + case .messenger(.start): + return .merge( + Effect(value: .networkMonitor(.stop)), + Effect.result { + do { + try messenger.start() + + if messenger.isConnected() == false { + try messenger.connect() + } + + if messenger.isListeningForMessages() == false { + try messenger.listenForMessages() + } + + if messenger.isFileTransferRunning() == false { + try messenger.startFileTransfer() + } + + if messenger.isLoggedIn() == false { + if try messenger.isRegistered() == false { + return .success(.messenger(.didStartUnregistered)) + } + try messenger.logIn() + } + + if !messenger.isBackupRunning() { + try? messenger.resumeBackup() + } + + return .success(.messenger(.didStartRegistered)) + } catch { + return .success(.messenger(.failure(error as NSError))) + } + } + ) + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .messenger(.didStartUnregistered): + state.register = RegisterComponent.State() + return .none + + case .messenger(.didStartRegistered): + return Effect(value: .networkMonitor(.start)) + + case .messenger(.failure(let error)): + state.failure = error.localizedDescription + return .none + + case .networkMonitor(.start): + return .merge( + Effect.run { subscriber in + let callback = HealthCallback { isHealthy in + subscriber.send(.networkMonitor(.health(isHealthy))) + } + let cancellable = messenger.cMix()?.addHealthCallback(callback) + return AnyCancellable { cancellable?.cancel() } + } + .cancellable(id: NetworkHealthEffectId.self, cancelInFlight: true), + Effect.timer( + id: NetworkNodesEffectId.self, + every: .seconds(2), + on: bgQueue + ) + .compactMap { _ in try? messenger.cMix()?.getNodeRegistrationStatus() } + .map { Action.networkMonitor(.nodes($0)) } + .eraseToEffect() + ) + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .networkMonitor(.stop): + state.isNetworkHealthy = nil + state.networkNodesReport = nil + return .merge( + .cancel(id: NetworkHealthEffectId.self), + .cancel(id: NetworkNodesEffectId.self) + ) + + case .networkMonitor(.health(let isHealthy)): + state.isNetworkHealthy = isHealthy + return .none + + case .networkMonitor(.nodes(let report)): + state.networkNodesReport = report + return .none + + case .deleteAccount(.buttonTapped): + state.alert = .confirmAccountDeletion() + return .none + + case .deleteAccount(.confirmed): + state.isDeletingAccount = true + return .result { + do { + let contactId = try messenger.e2e.tryGet().getContact().getId() + let contact = try dbManager.getDB().fetchContacts(.init(id: [contactId])).first + if let username = contact?.username { + let ud = try messenger.ud.tryGet() + try ud.permanentDeleteAccount(username: Fact(type: .username, value: username)) + } + try messenger.destroy() + try dbManager.removeDB() + return .success(.deleteAccount(.success)) + } catch { + return .success(.deleteAccount(.failure(error as NSError))) + } + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .deleteAccount(.success): + state.isDeletingAccount = false + return .none + + case .deleteAccount(.failure(let error)): + state.isDeletingAccount = false + state.alert = .accountDeletionFailed(error) + return .none + + case .didDismissAlert: + state.alert = nil + return .none + + case .didDismissRegister: + state.register = nil + return .none + + case .userSearchButtonTapped: + state.userSearch = UserSearchComponent.State() + return .none + + case .didDismissUserSearch: + state.userSearch = nil + return .none + + case .contactsButtonTapped: + state.contacts = ContactsComponent.State() + return .none + + case .didDismissContacts: + state.contacts = nil + return .none + + case .register(.finished): + state.register = nil + return Effect(value: .messenger(.start)) + + case .backupButtonTapped: + state.backup = BackupComponent.State() + return .none + + case .didDismissBackup: + state.backup = nil + return .none + + case .register(_), .contacts(_), .userSearch(_), .backup(_): + return .none + } + } + .presenting( + state: .keyPath(\.register), + id: .notNil(), + action: /Action.register, + presented: { RegisterComponent() } + ) + .presenting( + state: .keyPath(\.contacts), + id: .notNil(), + action: /Action.contacts, + presented: { ContactsComponent() } + ) + .presenting( + state: .keyPath(\.userSearch), + id: .notNil(), + action: /Action.userSearch, + presented: { UserSearchComponent() } + ) + .presenting( + state: .keyPath(\.backup), + id: .notNil(), + action: /Action.backup, + presented: { BackupComponent() } + ) + } +} diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift deleted file mode 100644 index 6433dd330faac74045cad472798cd8c06a79cb40..0000000000000000000000000000000000000000 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift +++ /dev/null @@ -1,330 +0,0 @@ -import AppCore -import BackupFeature -import Combine -import ComposableArchitecture -import ComposablePresentation -import ContactsFeature -import Foundation -import RegisterFeature -import UserSearchFeature -import XCTestDynamicOverlay -import XXClient -import XXMessengerClient -import XXModels - -public struct HomeState: Equatable { - public init( - failure: String? = nil, - isNetworkHealthy: Bool? = nil, - networkNodesReport: NodeRegistrationReport? = nil, - isDeletingAccount: Bool = false, - alert: AlertState<HomeAction>? = nil, - register: RegisterState? = nil, - contacts: ContactsState? = nil, - userSearch: UserSearchState? = nil, - backup: BackupState? = nil - ) { - self.failure = failure - self.isNetworkHealthy = isNetworkHealthy - self.isDeletingAccount = isDeletingAccount - self.alert = alert - self.register = register - self.contacts = contacts - self.userSearch = userSearch - self.backup = backup - } - - public var failure: String? - public var isNetworkHealthy: Bool? - public var networkNodesReport: NodeRegistrationReport? - public var isDeletingAccount: Bool - public var alert: AlertState<HomeAction>? - public var register: RegisterState? - public var contacts: ContactsState? - public var userSearch: UserSearchState? - public var backup: BackupState? -} - -public enum HomeAction: Equatable { - public enum Messenger: Equatable { - case start - case didStartRegistered - case didStartUnregistered - case failure(NSError) - } - - public enum NetworkMonitor: Equatable { - case start - case stop - case health(Bool) - case nodes(NodeRegistrationReport) - } - - public enum DeleteAccount: Equatable { - case buttonTapped - case confirmed - case success - case failure(NSError) - } - - case messenger(Messenger) - case networkMonitor(NetworkMonitor) - case deleteAccount(DeleteAccount) - case didDismissAlert - case didDismissRegister - case userSearchButtonTapped - case didDismissUserSearch - case contactsButtonTapped - case didDismissContacts - case backupButtonTapped - case didDismissBackup - case register(RegisterAction) - case contacts(ContactsAction) - case userSearch(UserSearchAction) - case backup(BackupAction) -} - -public struct HomeEnvironment { - public init( - messenger: Messenger, - dbManager: DBManager, - mainQueue: AnySchedulerOf<DispatchQueue>, - bgQueue: AnySchedulerOf<DispatchQueue>, - register: @escaping () -> RegisterEnvironment, - contacts: @escaping () -> ContactsEnvironment, - userSearch: @escaping () -> UserSearchEnvironment, - backup: @escaping () -> BackupEnvironment - ) { - self.messenger = messenger - self.dbManager = dbManager - self.mainQueue = mainQueue - self.bgQueue = bgQueue - self.register = register - self.contacts = contacts - self.userSearch = userSearch - self.backup = backup - } - - public var messenger: Messenger - public var dbManager: DBManager - public var mainQueue: AnySchedulerOf<DispatchQueue> - public var bgQueue: AnySchedulerOf<DispatchQueue> - public var register: () -> RegisterEnvironment - public var contacts: () -> ContactsEnvironment - public var userSearch: () -> UserSearchEnvironment - public var backup: () -> BackupEnvironment -} - -#if DEBUG -extension HomeEnvironment { - public static let unimplemented = HomeEnvironment( - messenger: .unimplemented, - dbManager: .unimplemented, - mainQueue: .unimplemented, - bgQueue: .unimplemented, - register: { .unimplemented }, - contacts: { .unimplemented }, - userSearch: { .unimplemented }, - backup: { .unimplemented } - ) -} -#endif - -public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> -{ state, action, env in - enum NetworkHealthEffectId {} - enum NetworkNodesEffectId {} - - switch action { - case .messenger(.start): - return .merge( - Effect(value: .networkMonitor(.stop)), - Effect.result { - do { - try env.messenger.start() - - if env.messenger.isConnected() == false { - try env.messenger.connect() - } - - if env.messenger.isListeningForMessages() == false { - try env.messenger.listenForMessages() - } - - if env.messenger.isFileTransferRunning() == false { - try env.messenger.startFileTransfer() - } - - if env.messenger.isLoggedIn() == false { - if try env.messenger.isRegistered() == false { - return .success(.messenger(.didStartUnregistered)) - } - try env.messenger.logIn() - } - - if !env.messenger.isBackupRunning() { - try? env.messenger.resumeBackup() - } - - return .success(.messenger(.didStartRegistered)) - } catch { - return .success(.messenger(.failure(error as NSError))) - } - } - ) - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .messenger(.didStartUnregistered): - state.register = RegisterState() - return .none - - case .messenger(.didStartRegistered): - return Effect(value: .networkMonitor(.start)) - - case .messenger(.failure(let error)): - state.failure = error.localizedDescription - return .none - - case .networkMonitor(.start): - return .merge( - Effect.run { subscriber in - let callback = HealthCallback { isHealthy in - subscriber.send(.networkMonitor(.health(isHealthy))) - } - let cancellable = env.messenger.cMix()?.addHealthCallback(callback) - return AnyCancellable { cancellable?.cancel() } - } - .cancellable(id: NetworkHealthEffectId.self, cancelInFlight: true), - Effect.timer( - id: NetworkNodesEffectId.self, - every: .seconds(2), - on: env.bgQueue - ) - .compactMap { _ in try? env.messenger.cMix()?.getNodeRegistrationStatus() } - .map { HomeAction.networkMonitor(.nodes($0)) } - .eraseToEffect() - ) - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .networkMonitor(.stop): - state.isNetworkHealthy = nil - state.networkNodesReport = nil - return .merge( - .cancel(id: NetworkHealthEffectId.self), - .cancel(id: NetworkNodesEffectId.self) - ) - - case .networkMonitor(.health(let isHealthy)): - state.isNetworkHealthy = isHealthy - return .none - - case .networkMonitor(.nodes(let report)): - state.networkNodesReport = report - return .none - - case .deleteAccount(.buttonTapped): - state.alert = .confirmAccountDeletion() - return .none - - case .deleteAccount(.confirmed): - state.isDeletingAccount = true - return .result { - do { - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - let contact = try env.dbManager.getDB().fetchContacts(.init(id: [contactId])).first - if let username = contact?.username { - let ud = try env.messenger.ud.tryGet() - try ud.permanentDeleteAccount(username: Fact(type: .username, value: username)) - } - try env.messenger.destroy() - try env.dbManager.removeDB() - return .success(.deleteAccount(.success)) - } catch { - return .success(.deleteAccount(.failure(error as NSError))) - } - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .deleteAccount(.success): - state.isDeletingAccount = false - return .none - - case .deleteAccount(.failure(let error)): - state.isDeletingAccount = false - state.alert = .accountDeletionFailed(error) - return .none - - case .didDismissAlert: - state.alert = nil - return .none - - case .didDismissRegister: - state.register = nil - return .none - - case .userSearchButtonTapped: - state.userSearch = UserSearchState() - return .none - - case .didDismissUserSearch: - state.userSearch = nil - return .none - - case .contactsButtonTapped: - state.contacts = ContactsState() - return .none - - case .didDismissContacts: - state.contacts = nil - return .none - - case .register(.finished): - state.register = nil - return Effect(value: .messenger(.start)) - - case .backupButtonTapped: - state.backup = BackupState() - return .none - - case .didDismissBackup: - state.backup = nil - return .none - - case .register(_), .contacts(_), .userSearch(_), .backup(_): - return .none - } -} -.presenting( - registerReducer, - state: .keyPath(\.register), - id: .notNil(), - action: /HomeAction.register, - environment: { $0.register() } -) -.presenting( - contactsReducer, - state: .keyPath(\.contacts), - id: .notNil(), - action: /HomeAction.contacts, - environment: { $0.contacts() } -) -.presenting( - userSearchReducer, - state: .keyPath(\.userSearch), - id: .notNil(), - action: /HomeAction.userSearch, - environment: { $0.userSearch() } -) -.presenting( - backupReducer, - state: .keyPath(\.backup), - id: .notNil(), - action: /HomeAction.backup, - environment: { $0.backup() } -) diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift index 8a1775d6a84ffdfc6b5ac1b2b816a8ee686b039c..d505100474cdd330d28afc81e126bbb0daf853b0 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift @@ -9,11 +9,11 @@ import UserSearchFeature import XXClient public struct HomeView: View { - public init(store: Store<HomeState, HomeAction>) { + public init(store: StoreOf<HomeComponent>) { self.store = store } - let store: Store<HomeState, HomeAction> + let store: StoreOf<HomeComponent> struct ViewState: Equatable { var failure: String? @@ -21,7 +21,7 @@ public struct HomeView: View { var networkNodesReport: NodeRegistrationReport? var isDeletingAccount: Bool - init(state: HomeState) { + init(state: HomeComponent.State) { failure = state.failure isNetworkHealthy = state.isNetworkHealthy isDeletingAccount = state.isDeletingAccount @@ -148,12 +148,12 @@ public struct HomeView: View { .navigationTitle("Home") .alert( store.scope(state: \.alert), - dismiss: HomeAction.didDismissAlert + dismiss: HomeComponent.Action.didDismissAlert ) .background(NavigationLinkWithStore( store.scope( state: \.contacts, - action: HomeAction.contacts + action: HomeComponent.Action.contacts ), onDeactivate: { viewStore.send(.didDismissContacts) @@ -163,7 +163,7 @@ public struct HomeView: View { .background(NavigationLinkWithStore( store.scope( state: \.userSearch, - action: HomeAction.userSearch + action: HomeComponent.Action.userSearch ), onDeactivate: { viewStore.send(.didDismissUserSearch) @@ -173,7 +173,7 @@ public struct HomeView: View { .background(NavigationLinkWithStore( store.scope( state: \.backup, - action: HomeAction.backup + action: HomeComponent.Action.backup ), onDeactivate: { viewStore.send(.didDismissBackup) @@ -186,7 +186,7 @@ public struct HomeView: View { .fullScreenCover( store.scope( state: \.register, - action: HomeAction.register + action: HomeComponent.Action.register ), onDismiss: { viewStore.send(.didDismissRegister) @@ -201,9 +201,8 @@ public struct HomeView: View { public struct HomeView_Previews: PreviewProvider { public static var previews: some View { HomeView(store: Store( - initialState: HomeState(), - reducer: .empty, - environment: () + initialState: HomeComponent.State(), + reducer: EmptyReducer() )) } } diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift similarity index 59% rename from Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift rename to Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift index 6c23ff8b718191771ce8dbf30ef4777cfd7303f9..3bd49a4283b9b4e535c40fb8c9279a5482aa3d16 100644 --- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift +++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeComponentTests.swift @@ -11,12 +11,11 @@ import XXMessengerClient import XXModels @testable import HomeFeature -final class HomeFeatureTests: XCTestCase { +final class HomeComponentTests: XCTestCase { func testMessengerStartUnregistered() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) var messengerDidStartWithTimeout: [Int] = [] @@ -24,17 +23,17 @@ final class HomeFeatureTests: XCTestCase { var messengerDidListenForMessages = 0 var messengerDidStartFileTransfer = 0 - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } - store.environment.messenger.isConnected.run = { false } - store.environment.messenger.connect.run = { messengerDidConnect += 1 } - store.environment.messenger.isListeningForMessages.run = { false } - store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 } - store.environment.messenger.isFileTransferRunning.run = { false } - store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { false } + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) } + store.dependencies.app.messenger.isConnected.run = { false } + store.dependencies.app.messenger.connect.run = { messengerDidConnect += 1 } + store.dependencies.app.messenger.isListeningForMessages.run = { false } + store.dependencies.app.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 } + store.dependencies.app.messenger.isFileTransferRunning.run = { false } + store.dependencies.app.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 } + store.dependencies.app.messenger.isLoggedIn.run = { false } + store.dependencies.app.messenger.isRegistered.run = { false } store.send(.messenger(.start)) @@ -45,15 +44,14 @@ final class HomeFeatureTests: XCTestCase { store.receive(.networkMonitor(.stop)) store.receive(.messenger(.didStartUnregistered)) { - $0.register = RegisterState() + $0.register = RegisterComponent.State() } } func testMessengerStartRegistered() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) var messengerDidStartWithTimeout: [Int] = [] @@ -63,21 +61,21 @@ final class HomeFeatureTests: XCTestCase { var messengerDidLogIn = 0 var messengerDidResumeBackup = 0 - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } - store.environment.messenger.isConnected.run = { false } - store.environment.messenger.connect.run = { messengerDidConnect += 1 } - store.environment.messenger.isListeningForMessages.run = { false } - store.environment.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 } - store.environment.messenger.isFileTransferRunning.run = { false } - store.environment.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { true } - store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - store.environment.messenger.isBackupRunning.run = { false } - store.environment.messenger.resumeBackup.run = { messengerDidResumeBackup += 1 } - store.environment.messenger.cMix.get = { + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) } + store.dependencies.app.messenger.isConnected.run = { false } + store.dependencies.app.messenger.connect.run = { messengerDidConnect += 1 } + store.dependencies.app.messenger.isListeningForMessages.run = { false } + store.dependencies.app.messenger.listenForMessages.run = { messengerDidListenForMessages += 1 } + store.dependencies.app.messenger.isFileTransferRunning.run = { false } + store.dependencies.app.messenger.startFileTransfer.run = { messengerDidStartFileTransfer += 1 } + store.dependencies.app.messenger.isLoggedIn.run = { false } + store.dependencies.app.messenger.isRegistered.run = { true } + store.dependencies.app.messenger.logIn.run = { messengerDidLogIn += 1 } + store.dependencies.app.messenger.isBackupRunning.run = { false } + store.dependencies.app.messenger.resumeBackup.run = { messengerDidResumeBackup += 1 } + store.dependencies.app.messenger.cMix.get = { var cMix: CMix = .unimplemented cMix.addHealthCallback.run = { _ in Cancellable {} } cMix.getNodeRegistrationStatus.run = { @@ -105,27 +103,26 @@ final class HomeFeatureTests: XCTestCase { func testRegisterFinished() { let store = TestStore( - initialState: HomeState( - register: RegisterState() + initialState: HomeComponent.State( + register: RegisterComponent.State() ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) var messengerDidStartWithTimeout: [Int] = [] var messengerDidLogIn = 0 - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { messengerDidStartWithTimeout.append($0) } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isListeningForMessages.run = { true } - store.environment.messenger.isFileTransferRunning.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { true } - store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - store.environment.messenger.isBackupRunning.run = { true } - store.environment.messenger.cMix.get = { + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { messengerDidStartWithTimeout.append($0) } + store.dependencies.app.messenger.isConnected.run = { true } + store.dependencies.app.messenger.isListeningForMessages.run = { true } + store.dependencies.app.messenger.isFileTransferRunning.run = { true } + store.dependencies.app.messenger.isLoggedIn.run = { false } + store.dependencies.app.messenger.isRegistered.run = { true } + store.dependencies.app.messenger.logIn.run = { messengerDidLogIn += 1 } + store.dependencies.app.messenger.isBackupRunning.run = { true } + store.dependencies.app.messenger.cMix.get = { var cMix: CMix = .unimplemented cMix.addHealthCallback.run = { _ in Cancellable {} } cMix.getNodeRegistrationStatus.run = { @@ -153,17 +150,16 @@ final class HomeFeatureTests: XCTestCase { func testMessengerStartFailure() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) struct Failure: Error {} let error = Failure() - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { _ in throw error } + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { _ in throw error } store.send(.messenger(.start)) @@ -175,19 +171,18 @@ final class HomeFeatureTests: XCTestCase { func testMessengerStartConnectFailure() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) struct Failure: Error {} let error = Failure() - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { false } - store.environment.messenger.connect.run = { throw error } + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { _ in } + store.dependencies.app.messenger.isConnected.run = { false } + store.dependencies.app.messenger.connect.run = { throw error } store.send(.messenger(.start)) @@ -199,22 +194,21 @@ final class HomeFeatureTests: XCTestCase { func testMessengerStartIsRegisteredFailure() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) struct Failure: Error {} let error = Failure() - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isListeningForMessages.run = { true } - store.environment.messenger.isFileTransferRunning.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { throw error } + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { _ in } + store.dependencies.app.messenger.isConnected.run = { true } + store.dependencies.app.messenger.isListeningForMessages.run = { true } + store.dependencies.app.messenger.isFileTransferRunning.run = { true } + store.dependencies.app.messenger.isLoggedIn.run = { false } + store.dependencies.app.messenger.isRegistered.run = { throw error } store.send(.messenger(.start)) @@ -226,23 +220,22 @@ final class HomeFeatureTests: XCTestCase { func testMessengerStartLogInFailure() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) struct Failure: Error {} let error = Failure() - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isListeningForMessages.run = { true } - store.environment.messenger.isFileTransferRunning.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { true } - store.environment.messenger.logIn.run = { throw error } + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.start.run = { _ in } + store.dependencies.app.messenger.isConnected.run = { true } + store.dependencies.app.messenger.isListeningForMessages.run = { true } + store.dependencies.app.messenger.isFileTransferRunning.run = { true } + store.dependencies.app.messenger.isLoggedIn.run = { false } + store.dependencies.app.messenger.isRegistered.run = { true } + store.dependencies.app.messenger.logIn.run = { throw error } store.send(.messenger(.start)) @@ -254,9 +247,8 @@ final class HomeFeatureTests: XCTestCase { func testNetworkMonitorStart() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) let bgQueue = DispatchQueue.test @@ -271,9 +263,9 @@ final class HomeFeatureTests: XCTestCase { .init(registered: 2, total: 12), ] - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.messenger.cMix.get = { + store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler() + store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler() + store.dependencies.app.messenger.cMix.get = { var cMix: CMix = .unimplemented cMix.addHealthCallback.run = { callback in cMixDidAddHealthCallback.append(callback) @@ -339,9 +331,8 @@ final class HomeFeatureTests: XCTestCase { func testAccountDeletion() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) var dbDidFetchContacts: [XXModels.Contact.Query] = [] @@ -349,9 +340,9 @@ final class HomeFeatureTests: XCTestCase { var messengerDidDestroy = 0 var didRemoveDB = 0 - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.e2e.get = { + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) @@ -360,7 +351,7 @@ final class HomeFeatureTests: XCTestCase { } return e2e } - store.environment.dbManager.getDB.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in dbDidFetchContacts.append(query) @@ -374,17 +365,17 @@ final class HomeFeatureTests: XCTestCase { } return db } - store.environment.dbManager.removeDB.run = { + store.dependencies.app.dbManager.removeDB.run = { didRemoveDB += 1 } - store.environment.messenger.ud.get = { + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.permanentDeleteAccount.run = { usernameFact in udDidPermanentDeleteAccount.append(usernameFact) } return ud } - store.environment.messenger.destroy.run = { + store.dependencies.app.messenger.destroy.run = { messengerDidDestroy += 1 } @@ -412,17 +403,16 @@ final class HomeFeatureTests: XCTestCase { func testAccountDeletionFailure() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) struct Failure: Error {} let error = Failure() - store.environment.bgQueue = .immediate - store.environment.mainQueue = .immediate - store.environment.messenger.e2e.get = { + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact = Contact.unimplemented("contact-data".data(using: .utf8)!) @@ -444,11 +434,10 @@ final class HomeFeatureTests: XCTestCase { func testDidDismissAlert() { let store = TestStore( - initialState: HomeState( + initialState: HomeComponent.State( alert: AlertState(title: TextState("")) ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) store.send(.didDismissAlert) { @@ -458,11 +447,10 @@ final class HomeFeatureTests: XCTestCase { func testDidDismissRegister() { let store = TestStore( - initialState: HomeState( - register: RegisterState() + initialState: HomeComponent.State( + register: RegisterComponent.State() ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) store.send(.didDismissRegister) { @@ -472,23 +460,21 @@ final class HomeFeatureTests: XCTestCase { func testUserSearchButtonTapped() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) store.send(.userSearchButtonTapped) { - $0.userSearch = UserSearchState() + $0.userSearch = UserSearchComponent.State() } } func testDidDismissUserSearch() { let store = TestStore( - initialState: HomeState( - userSearch: UserSearchState() + initialState: HomeComponent.State( + userSearch: UserSearchComponent.State() ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) store.send(.didDismissUserSearch) { @@ -498,23 +484,21 @@ final class HomeFeatureTests: XCTestCase { func testContactsButtonTapped() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) store.send(.contactsButtonTapped) { - $0.contacts = ContactsState() + $0.contacts = ContactsComponent.State() } } func testDidDismissContacts() { let store = TestStore( - initialState: HomeState( - contacts: ContactsState() + initialState: HomeComponent.State( + contacts: ContactsComponent.State() ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) store.send(.didDismissContacts) { @@ -524,23 +508,21 @@ final class HomeFeatureTests: XCTestCase { func testBackupButtonTapped() { let store = TestStore( - initialState: HomeState(), - reducer: homeReducer, - environment: .unimplemented + initialState: HomeComponent.State(), + reducer: HomeComponent() ) store.send(.backupButtonTapped) { - $0.backup = BackupState() + $0.backup = BackupComponent.State() } } func testDidDismissBackup() { let store = TestStore( - initialState: HomeState( - backup: BackupState() + initialState: HomeComponent.State( + backup: BackupComponent.State() ), - reducer: homeReducer, - environment: .unimplemented + reducer: HomeComponent() ) store.send(.didDismissBackup) {