From 5e4eec5a05858f03c93288e44eff2ace27ee9a90 Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Mon, 5 Sep 2022 15:04:49 +0200 Subject: [PATCH] Refactor --- .../Sources/AppFeature/AppFeature.swift | 2 +- .../Sources/HomeFeature/Alerts.swift | 2 +- .../Sources/HomeFeature/HomeFeature.swift | 91 +++++++++++++------ .../Sources/HomeFeature/HomeView.swift | 10 +- .../AppFeatureTests/AppFeatureTests.swift | 2 +- .../HomeFeatureTests/HomeFeatureTests.swift | 81 +++++++++++------ 6 files changed, 124 insertions(+), 64 deletions(-) diff --git a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift index 582478d4..43cede69 100644 --- a/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift +++ b/Examples/xx-messenger/Sources/AppFeature/AppFeature.swift @@ -68,7 +68,7 @@ extension AppEnvironment { let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in switch action { - case .start, .welcome(.finished), .restore(.finished), .home(.didDeleteAccount): + case .start, .welcome(.finished), .restore(.finished), .home(.deleteAccount(.success)): state.screen = .loading return .run { subscriber in do { diff --git a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift index a3347be4..3b9b9a3d 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/Alerts.swift @@ -6,7 +6,7 @@ extension AlertState { title: TextState("Delete Account"), message: TextState("This will permanently delete your account and can't be undone."), buttons: [ - .destructive(TextState("Delete"), action: .send(.deleteAccountConfirmed)), + .destructive(TextState("Delete"), action: .send(.deleteAccount(.confirmed))), .cancel(TextState("Cancel")) ] ) diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift index 279a5628..8116fde0 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeFeature.swift @@ -20,18 +20,31 @@ public struct HomeState: Equatable { self.isDeletingAccount = isDeletingAccount } - @BindableState public var failure: String? - @BindableState public var register: RegisterState? - @BindableState public var alert: AlertState<HomeAction>? - @BindableState public var isDeletingAccount: Bool + public var failure: String? + public var register: RegisterState? + public var alert: AlertState<HomeAction>? + public var isDeletingAccount: Bool } -public enum HomeAction: Equatable, BindableAction { - case start - case deleteAccountButtonTapped - case deleteAccountConfirmed - case didDeleteAccount - case binding(BindingAction<HomeState>) +public enum HomeAction: Equatable { + public enum Messenger: Equatable { + case start + case didStartRegistered + case didStartUnregistered + case failure(NSError) + } + + public enum DeleteAccount: Equatable { + case buttonTapped + case confirmed + case success + case failure(NSError) + } + + case messenger(Messenger) + case deleteAccount(DeleteAccount) + case didDismissAlert + case didDismissRegister case register(RegisterAction) } @@ -70,8 +83,8 @@ extension HomeEnvironment { public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> { state, action, env in switch action { - case .start: - return .run { subscriber in + case .messenger(.start): + return .result { do { try env.messenger.start() @@ -81,29 +94,38 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> if env.messenger.isLoggedIn() == false { if try env.messenger.isRegistered() == false { - subscriber.send(.set(\.$register, RegisterState())) - subscriber.send(completion: .finished) - return AnyCancellable {} + return .success(.messenger(.didStartUnregistered)) } try env.messenger.logIn() } + + return .success(.messenger(.didStartRegistered)) } catch { - subscriber.send(.set(\.$failure, error.localizedDescription)) + return .success(.messenger(.failure(error as NSError))) } - subscriber.send(completion: .finished) - return AnyCancellable {} } .subscribe(on: env.bgQueue) .receive(on: env.mainQueue) .eraseToEffect() - case .deleteAccountButtonTapped: + case .messenger(.didStartUnregistered): + state.register = RegisterState() + return .none + + case .messenger(.didStartRegistered): + return .none + + case .messenger(.failure(let error)): + state.failure = error.localizedDescription + return .none + + case .deleteAccount(.buttonTapped): state.alert = .confirmAccountDeletion() return .none - case .deleteAccountConfirmed: + case .deleteAccount(.confirmed): state.isDeletingAccount = true - return .run { subscriber in + return .result { do { let contactId = try env.messenger.e2e.tryGet().getContact().getId() let contact = try env.db().fetchContacts(.init(id: [contactId])).first @@ -113,31 +135,40 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> } try env.messenger.destroy() try env.db().drop() - subscriber.send(.didDeleteAccount) + return .success(.deleteAccount(.success)) } catch { - subscriber.send(.set(\.$isDeletingAccount, false)) - subscriber.send(.set(\.$alert, .accountDeletionFailed(error))) + return .success(.deleteAccount(.failure(error as NSError))) } - subscriber.send(completion: .finished) - return AnyCancellable {} } .subscribe(on: env.bgQueue) .receive(on: env.mainQueue) .eraseToEffect() - case .didDeleteAccount: + 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 .register(.finished): state.register = nil - return Effect(value: .start) + return Effect(value: .messenger(.start)) - case .binding(_), .register(_): + case .register(_): return .none } } -.binding() .presenting( registerReducer, state: .keyPath(\.register), diff --git a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift index f73f6b8a..b2c9e660 100644 --- a/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift +++ b/Examples/xx-messenger/Sources/HomeFeature/HomeView.swift @@ -28,7 +28,7 @@ public struct HomeView: View { Section { Text(failure) Button { - viewStore.send(.start) + viewStore.send(.messenger(.start)) } label: { Text("Retry") } @@ -39,7 +39,7 @@ public struct HomeView: View { Section { Button(role: .destructive) { - viewStore.send(.deleteAccountButtonTapped) + viewStore.send(.deleteAccount(.buttonTapped)) } label: { HStack { Text("Delete Account") @@ -57,18 +57,18 @@ public struct HomeView: View { .navigationTitle("Home") .alert( store.scope(state: \.alert), - dismiss: HomeAction.set(\.$alert, nil) + dismiss: HomeAction.didDismissAlert ) } .navigationViewStyle(.stack) - .task { viewStore.send(.start) } + .task { viewStore.send(.messenger(.start)) } .fullScreenCover( store.scope( state: \.register, action: HomeAction.register ), onDismiss: { - viewStore.send(.set(\.$register, nil)) + viewStore.send(.didDismissRegister) }, content: RegisterView.init(store:) ) diff --git a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift index 5ea96813..5a013b28 100644 --- a/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift +++ b/Examples/xx-messenger/Tests/AppFeatureTests/AppFeatureTests.swift @@ -159,7 +159,7 @@ final class AppFeatureTests: XCTestCase { store.environment.messenger.isLoaded.run = { false } store.environment.messenger.isCreated.run = { false } - store.send(.home(.didDeleteAccount)) { + store.send(.home(.deleteAccount(.success))) { $0.screen = .loading } diff --git a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift index 4fbad5b8..60c7e305 100644 --- a/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift +++ b/Examples/xx-messenger/Tests/HomeFeatureTests/HomeFeatureTests.swift @@ -7,7 +7,7 @@ import XXModels @testable import HomeFeature final class HomeFeatureTests: XCTestCase { - func testStartUnregistered() { + func testMessengerStartUnregistered() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -27,7 +27,7 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isRegistered.run = { false } - store.send(.start) + store.send(.messenger(.start)) bgQueue.advance() @@ -36,12 +36,12 @@ final class HomeFeatureTests: XCTestCase { mainQueue.advance() - store.receive(.set(\.$register, RegisterState())) { + store.receive(.messenger(.didStartUnregistered)) { $0.register = RegisterState() } } - func testStartRegistered() { + func testMessengerStartRegistered() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -63,7 +63,7 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isRegistered.run = { true } store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - store.send(.start) + store.send(.messenger(.start)) bgQueue.advance() @@ -72,6 +72,8 @@ final class HomeFeatureTests: XCTestCase { XCTAssertNoDifference(messengerDidLogIn, 1) mainQueue.advance() + + store.receive(.messenger(.didStartRegistered)) } func testRegisterFinished() { @@ -100,7 +102,7 @@ final class HomeFeatureTests: XCTestCase { $0.register = nil } - store.receive(.start) + store.receive(.messenger(.start)) bgQueue.advance() @@ -108,9 +110,11 @@ final class HomeFeatureTests: XCTestCase { XCTAssertNoDifference(messengerDidLogIn, 1) mainQueue.advance() + + store.receive(.messenger(.didStartRegistered)) } - func testStartMessengerStartFailure() { + func testMessengerStartFailure() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -124,14 +128,14 @@ final class HomeFeatureTests: XCTestCase { store.environment.mainQueue = .immediate store.environment.messenger.start.run = { _ in throw error } - store.send(.start) + store.send(.messenger(.start)) - store.receive(.set(\.$failure, error.localizedDescription)) { + store.receive(.messenger(.failure(error as NSError))) { $0.failure = error.localizedDescription } } - func testStartMessengerConnectFailure() { + func testMessengerStartConnectFailure() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -147,14 +151,14 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isConnected.run = { false } store.environment.messenger.connect.run = { throw error } - store.send(.start) + store.send(.messenger(.start)) - store.receive(.set(\.$failure, error.localizedDescription)) { + store.receive(.messenger(.failure(error as NSError))) { $0.failure = error.localizedDescription } } - func testStartMessengerIsRegisteredFailure() { + func testMessengerStartIsRegisteredFailure() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -171,14 +175,14 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isLoggedIn.run = { false } store.environment.messenger.isRegistered.run = { throw error } - store.send(.start) + store.send(.messenger(.start)) - store.receive(.set(\.$failure, error.localizedDescription)) { + store.receive(.messenger(.failure(error as NSError))) { $0.failure = error.localizedDescription } } - func testStartMessengerLogInFailure() { + func testMessengerStartLogInFailure() { let store = TestStore( initialState: HomeState(), reducer: homeReducer, @@ -196,9 +200,9 @@ final class HomeFeatureTests: XCTestCase { store.environment.messenger.isRegistered.run = { true } store.environment.messenger.logIn.run = { throw error } - store.send(.start) + store.send(.messenger(.start)) - store.receive(.set(\.$failure, error.localizedDescription)) { + store.receive(.messenger(.failure(error as NSError))) { $0.failure = error.localizedDescription } } @@ -254,15 +258,15 @@ final class HomeFeatureTests: XCTestCase { messengerDidDestroy += 1 } - store.send(.deleteAccountButtonTapped) { + store.send(.deleteAccount(.buttonTapped)) { $0.alert = .confirmAccountDeletion() } - store.send(.set(\.$alert, nil)) { + store.send(.didDismissAlert) { $0.alert = nil } - store.send(.deleteAccountConfirmed) { + store.send(.deleteAccount(.confirmed)) { $0.isDeletingAccount = true } @@ -271,7 +275,7 @@ final class HomeFeatureTests: XCTestCase { XCTAssertNoDifference(messengerDidDestroy, 1) XCTAssertNoDifference(dbDidDrop, 1) - store.receive(.didDeleteAccount) { + store.receive(.deleteAccount(.success)) { $0.isDeletingAccount = false } } @@ -298,16 +302,41 @@ final class HomeFeatureTests: XCTestCase { return e2e } - store.send(.deleteAccountConfirmed) { + store.send(.deleteAccount(.confirmed)) { $0.isDeletingAccount = true } - store.receive(.set(\.$isDeletingAccount, false)) { + store.receive(.deleteAccount(.failure(error as NSError))) { $0.isDeletingAccount = false + $0.alert = .accountDeletionFailed(error) } + } - store.receive(.set(\.$alert, .accountDeletionFailed(error))) { - $0.alert = .accountDeletionFailed(error) + func testDidDismissAlert() { + let store = TestStore( + initialState: HomeState( + alert: AlertState(title: TextState("")) + ), + reducer: homeReducer, + environment: .unimplemented + ) + + store.send(.didDismissAlert) { + $0.alert = nil + } + } + + func testDidDismissRegister() { + let store = TestStore( + initialState: HomeState( + register: RegisterState() + ), + reducer: homeReducer, + environment: .unimplemented + ) + + store.send(.didDismissRegister) { + $0.register = nil } } } -- GitLab