From ce8cfc61c1229b6794f986f31b471e3222532cbc Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Fri, 21 Oct 2022 11:55:15 +0200 Subject: [PATCH] Migrate MyContactFeature to ReducerProtocol --- .../Sources/MyContactFeature/Alerts.swift | 4 +- .../MyContactFeature/MyContactComponent.swift | 297 ++++++++++++++++ .../MyContactFeature/MyContactFeature.swift | 316 ------------------ .../MyContactFeature/MyContactView.swift | 23 +- ...ts.swift => MyContactComponentTests.swift} | 194 +++++------ 5 files changed, 399 insertions(+), 435 deletions(-) create mode 100644 Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift delete mode 100644 Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift rename Examples/xx-messenger/Tests/MyContactFeatureTests/{MyContactFeatureTests.swift => MyContactComponentTests.swift} (80%) diff --git a/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift b/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift index 321139ae..f8693f17 100644 --- a/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift +++ b/Examples/xx-messenger/Sources/MyContactFeature/Alerts.swift @@ -1,8 +1,8 @@ import ComposableArchitecture extension AlertState { - public static func error(_ message: String) -> AlertState<MyContactAction> { - AlertState<MyContactAction>( + public static func error(_ message: String) -> AlertState<MyContactComponent.Action> { + AlertState<MyContactComponent.Action>( title: TextState("Error"), message: TextState(message), buttons: [] diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift new file mode 100644 index 00000000..377e824b --- /dev/null +++ b/Examples/xx-messenger/Sources/MyContactFeature/MyContactComponent.swift @@ -0,0 +1,297 @@ +import AppCore +import Combine +import ComposableArchitecture +import Foundation +import XCTestDynamicOverlay +import XXClient +import XXMessengerClient +import XXModels + +public struct MyContactComponent: ReducerProtocol { + public struct State: Equatable { + public enum Field: String, Hashable { + case email + case emailCode + case phone + case phoneCode + } + + public init( + contact: XXModels.Contact? = nil, + focusedField: Field? = nil, + email: String = "", + emailConfirmationID: String? = nil, + emailConfirmationCode: String = "", + isRegisteringEmail: Bool = false, + isConfirmingEmail: Bool = false, + isUnregisteringEmail: Bool = false, + phone: String = "", + phoneConfirmationID: String? = nil, + phoneConfirmationCode: String = "", + isRegisteringPhone: Bool = false, + isConfirmingPhone: Bool = false, + isUnregisteringPhone: Bool = false, + isLoadingFacts: Bool = false, + alert: AlertState<Action>? = nil + ) { + self.contact = contact + self.focusedField = focusedField + self.email = email + self.emailConfirmationID = emailConfirmationID + self.emailConfirmationCode = emailConfirmationCode + self.isRegisteringEmail = isRegisteringEmail + self.isConfirmingEmail = isConfirmingEmail + self.isUnregisteringEmail = isUnregisteringEmail + self.phone = phone + self.phoneConfirmationID = phoneConfirmationID + self.phoneConfirmationCode = phoneConfirmationCode + self.isRegisteringPhone = isRegisteringPhone + self.isConfirmingPhone = isConfirmingPhone + self.isUnregisteringPhone = isUnregisteringPhone + self.isLoadingFacts = isLoadingFacts + self.alert = alert + } + + public var contact: XXModels.Contact? + @BindableState public var focusedField: Field? + @BindableState public var email: String + @BindableState public var emailConfirmationID: String? + @BindableState public var emailConfirmationCode: String + @BindableState public var isRegisteringEmail: Bool + @BindableState public var isConfirmingEmail: Bool + @BindableState public var isUnregisteringEmail: Bool + @BindableState public var phone: String + @BindableState public var phoneConfirmationID: String? + @BindableState public var phoneConfirmationCode: String + @BindableState public var isRegisteringPhone: Bool + @BindableState public var isConfirmingPhone: Bool + @BindableState public var isUnregisteringPhone: Bool + @BindableState public var isLoadingFacts: Bool + public var alert: AlertState<Action>? + } + + public enum Action: Equatable, BindableAction { + case start + case contactFetched(XXModels.Contact?) + case registerEmailTapped + case confirmEmailTapped + case unregisterEmailTapped + case registerPhoneTapped + case confirmPhoneTapped + case unregisterPhoneTapped + case loadFactsTapped + case didFail(String) + case alertDismissed + case binding(BindingAction<State>) + } + + public init() {} + + @Dependency(\.app.messenger) var messenger: Messenger + @Dependency(\.app.dbManager.getDB) var db: DBManagerGetDB + @Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue> + @Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue> + + public var body: some ReducerProtocol<State, Action> { + BindingReducer() + Reduce { state, action in + enum DBFetchEffectID {} + let db = self.db + + switch action { + case .start: + return Effect + .catching { try messenger.e2e.tryGet().getContact().getId() } + .tryMap { try db().fetchContactsPublisher(.init(id: [$0])) } + .flatMap { $0 } + .assertNoFailure() + .map(\.first) + .map(Action.contactFetched) + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + .cancellable(id: DBFetchEffectID.self, cancelInFlight: true) + + case .contactFetched(let contact): + state.contact = contact + return .none + + case .registerEmailTapped: + state.focusedField = nil + state.isRegisteringEmail = true + return Effect.run { [state] subscriber in + do { + let ud = try messenger.ud.tryGet() + let fact = Fact(type: .email, value: state.email) + let confirmationID = try ud.sendRegisterFact(fact) + subscriber.send(.set(\.$emailConfirmationID, confirmationID)) + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isRegisteringEmail, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .confirmEmailTapped: + guard let confirmationID = state.emailConfirmationID else { return .none } + state.focusedField = nil + state.isConfirmingEmail = true + return Effect.run { [state] subscriber in + do { + let ud = try messenger.ud.tryGet() + try ud.confirmFact(confirmationId: confirmationID, code: state.emailConfirmationCode) + let contactId = try messenger.e2e.tryGet().getContact().getId() + if var dbContact = try db().fetchContacts(.init(id: [contactId])).first { + dbContact.email = state.email + try db().saveContact(dbContact) + } + subscriber.send(.set(\.$email, "")) + subscriber.send(.set(\.$emailConfirmationID, nil)) + subscriber.send(.set(\.$emailConfirmationCode, "")) + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isConfirmingEmail, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .unregisterEmailTapped: + guard let email = state.contact?.email else { return .none } + state.isUnregisteringEmail = true + return Effect.run { subscriber in + do { + let ud: UserDiscovery = try messenger.ud.tryGet() + let fact = Fact(type: .email, value: email) + try ud.removeFact(fact) + let contactId = try messenger.e2e.tryGet().getContact().getId() + if var dbContact = try db().fetchContacts(.init(id: [contactId])).first { + dbContact.email = nil + try db().saveContact(dbContact) + } + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isUnregisteringEmail, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .registerPhoneTapped: + state.focusedField = nil + state.isRegisteringPhone = true + return Effect.run { [state] subscriber in + do { + let ud = try messenger.ud.tryGet() + let fact = Fact(type: .phone, value: state.phone) + let confirmationID = try ud.sendRegisterFact(fact) + subscriber.send(.set(\.$phoneConfirmationID, confirmationID)) + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isRegisteringPhone, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .confirmPhoneTapped: + guard let confirmationID = state.phoneConfirmationID else { return .none } + state.focusedField = nil + state.isConfirmingPhone = true + return Effect.run { [state] subscriber in + do { + let ud = try messenger.ud.tryGet() + try ud.confirmFact(confirmationId: confirmationID, code: state.phoneConfirmationCode) + let contactId = try messenger.e2e.tryGet().getContact().getId() + if var dbContact = try db().fetchContacts(.init(id: [contactId])).first { + dbContact.phone = state.phone + try db().saveContact(dbContact) + } + subscriber.send(.set(\.$phone, "")) + subscriber.send(.set(\.$phoneConfirmationID, nil)) + subscriber.send(.set(\.$phoneConfirmationCode, "")) + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isConfirmingPhone, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .unregisterPhoneTapped: + guard let phone = state.contact?.phone else { return .none } + state.isUnregisteringPhone = true + return Effect.run { subscriber in + do { + let ud: UserDiscovery = try messenger.ud.tryGet() + let fact = Fact(type: .phone, value: phone) + try ud.removeFact(fact) + let contactId = try messenger.e2e.tryGet().getContact().getId() + if var dbContact = try db().fetchContacts(.init(id: [contactId])).first { + dbContact.phone = nil + try db().saveContact(dbContact) + } + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isUnregisteringPhone, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .loadFactsTapped: + state.isLoadingFacts = true + return Effect.run { subscriber in + do { + let contactId = try messenger.e2e.tryGet().getContact().getId() + if var dbContact = try db().fetchContacts(.init(id: [contactId])).first { + let facts = try messenger.ud.tryGet().getFacts() + dbContact.username = facts.get(.username)?.value + dbContact.email = facts.get(.email)?.value + dbContact.phone = facts.get(.phone)?.value + try db().saveContact(dbContact) + } + } catch { + subscriber.send(.didFail(error.localizedDescription)) + } + subscriber.send(.set(\.$isLoadingFacts, false)) + subscriber.send(completion: .finished) + return AnyCancellable {} + } + .subscribe(on: bgQueue) + .receive(on: mainQueue) + .eraseToEffect() + + case .didFail(let failure): + state.alert = .error(failure) + return .none + + case .alertDismissed: + state.alert = nil + return .none + + case .binding(_): + return .none + } + } + } +} diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift deleted file mode 100644 index 434a1aca..00000000 --- a/Examples/xx-messenger/Sources/MyContactFeature/MyContactFeature.swift +++ /dev/null @@ -1,316 +0,0 @@ -import AppCore -import Combine -import ComposableArchitecture -import Foundation -import XCTestDynamicOverlay -import XXClient -import XXMessengerClient -import XXModels - -public struct MyContactState: Equatable { - public enum Field: String, Hashable { - case email - case emailCode - case phone - case phoneCode - } - - public init( - contact: XXModels.Contact? = nil, - focusedField: Field? = nil, - email: String = "", - emailConfirmationID: String? = nil, - emailConfirmationCode: String = "", - isRegisteringEmail: Bool = false, - isConfirmingEmail: Bool = false, - isUnregisteringEmail: Bool = false, - phone: String = "", - phoneConfirmationID: String? = nil, - phoneConfirmationCode: String = "", - isRegisteringPhone: Bool = false, - isConfirmingPhone: Bool = false, - isUnregisteringPhone: Bool = false, - isLoadingFacts: Bool = false, - alert: AlertState<MyContactAction>? = nil - ) { - self.contact = contact - self.focusedField = focusedField - self.email = email - self.emailConfirmationID = emailConfirmationID - self.emailConfirmationCode = emailConfirmationCode - self.isRegisteringEmail = isRegisteringEmail - self.isConfirmingEmail = isConfirmingEmail - self.isUnregisteringEmail = isUnregisteringEmail - self.phone = phone - self.phoneConfirmationID = phoneConfirmationID - self.phoneConfirmationCode = phoneConfirmationCode - self.isRegisteringPhone = isRegisteringPhone - self.isConfirmingPhone = isConfirmingPhone - self.isUnregisteringPhone = isUnregisteringPhone - self.isLoadingFacts = isLoadingFacts - self.alert = alert - } - - public var contact: XXModels.Contact? - @BindableState public var focusedField: Field? - @BindableState public var email: String - @BindableState public var emailConfirmationID: String? - @BindableState public var emailConfirmationCode: String - @BindableState public var isRegisteringEmail: Bool - @BindableState public var isConfirmingEmail: Bool - @BindableState public var isUnregisteringEmail: Bool - @BindableState public var phone: String - @BindableState public var phoneConfirmationID: String? - @BindableState public var phoneConfirmationCode: String - @BindableState public var isRegisteringPhone: Bool - @BindableState public var isConfirmingPhone: Bool - @BindableState public var isUnregisteringPhone: Bool - @BindableState public var isLoadingFacts: Bool - public var alert: AlertState<MyContactAction>? -} - -public enum MyContactAction: Equatable, BindableAction { - case start - case contactFetched(XXModels.Contact?) - case registerEmailTapped - case confirmEmailTapped - case unregisterEmailTapped - case registerPhoneTapped - case confirmPhoneTapped - case unregisterPhoneTapped - case loadFactsTapped - case didFail(String) - case alertDismissed - case binding(BindingAction<MyContactState>) -} - -public struct MyContactEnvironment { - public init( - messenger: Messenger, - db: DBManagerGetDB, - mainQueue: AnySchedulerOf<DispatchQueue>, - bgQueue: AnySchedulerOf<DispatchQueue> - ) { - self.messenger = messenger - self.db = db - self.mainQueue = mainQueue - self.bgQueue = bgQueue - } - - public var messenger: Messenger - public var db: DBManagerGetDB - public var mainQueue: AnySchedulerOf<DispatchQueue> - public var bgQueue: AnySchedulerOf<DispatchQueue> -} - -#if DEBUG -extension MyContactEnvironment { - public static let unimplemented = MyContactEnvironment( - messenger: .unimplemented, - db: .unimplemented, - mainQueue: .unimplemented, - bgQueue: .unimplemented - ) -} -#endif - -public let myContactReducer = Reducer<MyContactState, MyContactAction, MyContactEnvironment> -{ state, action, env in - enum DBFetchEffectID {} - - switch action { - case .start: - return Effect - .catching { try env.messenger.e2e.tryGet().getContact().getId() } - .tryMap { try env.db().fetchContactsPublisher(.init(id: [$0])) } - .flatMap { $0 } - .assertNoFailure() - .map(\.first) - .map(MyContactAction.contactFetched) - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - .cancellable(id: DBFetchEffectID.self, cancelInFlight: true) - - case .contactFetched(let contact): - state.contact = contact - return .none - - case .registerEmailTapped: - state.focusedField = nil - state.isRegisteringEmail = true - return Effect.run { [state] subscriber in - do { - let ud = try env.messenger.ud.tryGet() - let fact = Fact(type: .email, value: state.email) - let confirmationID = try ud.sendRegisterFact(fact) - subscriber.send(.set(\.$emailConfirmationID, confirmationID)) - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isRegisteringEmail, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .confirmEmailTapped: - guard let confirmationID = state.emailConfirmationID else { return .none } - state.focusedField = nil - state.isConfirmingEmail = true - return Effect.run { [state] subscriber in - do { - let ud = try env.messenger.ud.tryGet() - try ud.confirmFact(confirmationId: confirmationID, code: state.emailConfirmationCode) - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first { - dbContact.email = state.email - try env.db().saveContact(dbContact) - } - subscriber.send(.set(\.$email, "")) - subscriber.send(.set(\.$emailConfirmationID, nil)) - subscriber.send(.set(\.$emailConfirmationCode, "")) - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isConfirmingEmail, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .unregisterEmailTapped: - guard let email = state.contact?.email else { return .none } - state.isUnregisteringEmail = true - return Effect.run { [state] subscriber in - do { - let ud: UserDiscovery = try env.messenger.ud.tryGet() - let fact = Fact(type: .email, value: email) - try ud.removeFact(fact) - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first { - dbContact.email = nil - try env.db().saveContact(dbContact) - } - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isUnregisteringEmail, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .registerPhoneTapped: - state.focusedField = nil - state.isRegisteringPhone = true - return Effect.run { [state] subscriber in - do { - let ud = try env.messenger.ud.tryGet() - let fact = Fact(type: .phone, value: state.phone) - let confirmationID = try ud.sendRegisterFact(fact) - subscriber.send(.set(\.$phoneConfirmationID, confirmationID)) - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isRegisteringPhone, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .confirmPhoneTapped: - guard let confirmationID = state.phoneConfirmationID else { return .none } - state.focusedField = nil - state.isConfirmingPhone = true - return Effect.run { [state] subscriber in - do { - let ud = try env.messenger.ud.tryGet() - try ud.confirmFact(confirmationId: confirmationID, code: state.phoneConfirmationCode) - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first { - dbContact.phone = state.phone - try env.db().saveContact(dbContact) - } - subscriber.send(.set(\.$phone, "")) - subscriber.send(.set(\.$phoneConfirmationID, nil)) - subscriber.send(.set(\.$phoneConfirmationCode, "")) - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isConfirmingPhone, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .unregisterPhoneTapped: - guard let phone = state.contact?.phone else { return .none } - state.isUnregisteringPhone = true - return Effect.run { [state] subscriber in - do { - let ud: UserDiscovery = try env.messenger.ud.tryGet() - let fact = Fact(type: .phone, value: phone) - try ud.removeFact(fact) - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first { - dbContact.phone = nil - try env.db().saveContact(dbContact) - } - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isUnregisteringPhone, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .loadFactsTapped: - state.isLoadingFacts = true - return Effect.run { subscriber in - do { - let contactId = try env.messenger.e2e.tryGet().getContact().getId() - if var dbContact = try env.db().fetchContacts(.init(id: [contactId])).first { - let facts = try env.messenger.ud.tryGet().getFacts() - dbContact.username = facts.get(.username)?.value - dbContact.email = facts.get(.email)?.value - dbContact.phone = facts.get(.phone)?.value - try env.db().saveContact(dbContact) - } - } catch { - subscriber.send(.didFail(error.localizedDescription)) - } - subscriber.send(.set(\.$isLoadingFacts, false)) - subscriber.send(completion: .finished) - return AnyCancellable {} - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .didFail(let failure): - state.alert = .error(failure) - return .none - - case .alertDismissed: - state.alert = nil - return .none - - case .binding(_): - return .none - } -} -.binding() diff --git a/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift b/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift index d32a6f68..b5cd127e 100644 --- a/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift +++ b/Examples/xx-messenger/Sources/MyContactFeature/MyContactView.swift @@ -4,15 +4,15 @@ import SwiftUI import XXModels public struct MyContactView: View { - public init(store: Store<MyContactState, MyContactAction>) { + public init(store: StoreOf<MyContactComponent>) { self.store = store } - let store: Store<MyContactState, MyContactAction> - @FocusState var focusedField: MyContactState.Field? + let store: StoreOf<MyContactComponent> + @FocusState var focusedField: MyContactComponent.State.Field? struct ViewState: Equatable { - init(state: MyContactState) { + init(state: MyContactComponent.State) { contact = state.contact focusedField = state.focusedField email = state.email @@ -31,7 +31,7 @@ public struct MyContactView: View { } var contact: XXModels.Contact? - var focusedField: MyContactState.Field? + var focusedField: MyContactComponent.State.Field? var email: String var emailConfirmation: Bool var emailCode: String @@ -86,7 +86,7 @@ public struct MyContactView: View { TextField( text: viewStore.binding( get: \.email, - send: { MyContactAction.set(\.$email, $0) } + send: { MyContactComponent.Action.set(\.$email, $0) } ), prompt: Text("Enter email"), label: { Text("Email") } @@ -99,7 +99,7 @@ public struct MyContactView: View { TextField( text: viewStore.binding( get: \.emailCode, - send: { MyContactAction.set(\.$emailConfirmationCode, $0) } + send: { MyContactComponent.Action.set(\.$emailConfirmationCode, $0) } ), prompt: Text("Enter confirmation code"), label: { Text("Confirmation code") } @@ -163,7 +163,7 @@ public struct MyContactView: View { TextField( text: viewStore.binding( get: \.phone, - send: { MyContactAction.set(\.$phone, $0) } + send: { MyContactComponent.Action.set(\.$phone, $0) } ), prompt: Text("Enter phone"), label: { Text("Phone") } @@ -176,7 +176,7 @@ public struct MyContactView: View { TextField( text: viewStore.binding( get: \.phoneCode, - send: { MyContactAction.set(\.$phoneConfirmationCode, $0) } + send: { MyContactComponent.Action.set(\.$phoneConfirmationCode, $0) } ), prompt: Text("Enter confirmation code"), label: { Text("Confirmation code") } @@ -250,9 +250,8 @@ public struct MyContactView_Previews: PreviewProvider { public static var previews: some View { NavigationView { MyContactView(store: Store( - initialState: MyContactState(), - reducer: .empty, - environment: () + initialState: MyContactComponent.State(), + reducer: EmptyReducer() )) } } diff --git a/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift b/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift similarity index 80% rename from Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift rename to Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift index 830e9715..358db1c2 100644 --- a/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactFeatureTests.swift +++ b/Examples/xx-messenger/Tests/MyContactFeatureTests/MyContactComponentTests.swift @@ -7,22 +7,21 @@ import XXMessengerClient import XXModels @testable import MyContactFeature -final class MyContactFeatureTests: XCTestCase { +final class MyContactComponentTests: XCTestCase { func testStart() { let contactId = "contact-id".data(using: .utf8)! let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) var dbDidFetchContacts: [XXModels.Contact.Query] = [] let dbContactsPublisher = PassthroughSubject<[XXModels.Contact], Error>() - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.e2e.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -31,7 +30,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContactsPublisher.run = { query in dbDidFetchContacts.append(query) @@ -65,14 +64,13 @@ final class MyContactFeatureTests: XCTestCase { var didSendRegisterFact: [Fact] = [] let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.sendRegisterFact.run = { fact in didSendRegisterFact.append(fact) @@ -110,14 +108,13 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.sendRegisterFact.run = { _ in throw failure } return ud @@ -149,17 +146,16 @@ final class MyContactFeatureTests: XCTestCase { var didSaveContact: [XXModels.Contact] = [] let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( email: email, emailConfirmationID: confirmationID ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.confirmFact.run = { id, code in didConfirmWithID.append(id) @@ -167,7 +163,7 @@ final class MyContactFeatureTests: XCTestCase { } return ud } - store.environment.messenger.e2e.get = { + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -176,7 +172,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in didFetchContacts.append(query) @@ -228,16 +224,15 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( emailConfirmationID: "123" ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.confirmFact.run = { _, _ in throw failure } return ud @@ -266,21 +261,20 @@ final class MyContactFeatureTests: XCTestCase { var didSaveContact: [XXModels.Contact] = [] let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( contact: dbContact ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.removeFact.run = { didRemoveFact.append($0) } return ud } - store.environment.messenger.e2e.get = { + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -289,7 +283,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in didFetchContacts.append(query) @@ -322,16 +316,15 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( contact: .init(id: Data(), email: "test@email.com") ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.removeFact.run = { _ in throw failure } return ud @@ -357,14 +350,13 @@ final class MyContactFeatureTests: XCTestCase { var didSendRegisterFact: [Fact] = [] let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.sendRegisterFact.run = { fact in didSendRegisterFact.append(fact) @@ -402,14 +394,13 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.sendRegisterFact.run = { _ in throw failure } return ud @@ -441,17 +432,16 @@ final class MyContactFeatureTests: XCTestCase { var didSaveContact: [XXModels.Contact] = [] let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( phone: phone, phoneConfirmationID: confirmationID ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.confirmFact.run = { id, code in didConfirmWithID.append(id) @@ -459,7 +449,7 @@ final class MyContactFeatureTests: XCTestCase { } return ud } - store.environment.messenger.e2e.get = { + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -468,7 +458,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in didFetchContacts.append(query) @@ -520,16 +510,15 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( phoneConfirmationID: "123" ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.confirmFact.run = { _, _ in throw failure } return ud @@ -558,21 +547,20 @@ final class MyContactFeatureTests: XCTestCase { var didSaveContact: [XXModels.Contact] = [] let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( contact: dbContact ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.removeFact.run = { didRemoveFact.append($0) } return ud } - store.environment.messenger.e2e.get = { + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -581,7 +569,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in didFetchContacts.append(query) @@ -614,16 +602,15 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState( + initialState: MyContactComponent.State( contact: .init(id: Data(), phone: "+123456789") ), - reducer: myContactReducer, - environment: .unimplemented + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.ud.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.removeFact.run = { _ in throw failure } return ud @@ -653,14 +640,13 @@ final class MyContactFeatureTests: XCTestCase { var didSaveContact: [XXModels.Contact] = [] let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.e2e.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -669,7 +655,7 @@ final class MyContactFeatureTests: XCTestCase { } return e2e } - store.environment.messenger.ud.get = { + store.dependencies.app.messenger.ud.get = { var ud: UserDiscovery = .unimplemented ud.getFacts.run = { [ @@ -680,7 +666,7 @@ final class MyContactFeatureTests: XCTestCase { } return ud } - store.environment.db.run = { + store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContacts.run = { query in didFetchContacts.append(query) @@ -714,14 +700,13 @@ final class MyContactFeatureTests: XCTestCase { let failure = Failure() let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) - store.environment.mainQueue = .immediate - store.environment.bgQueue = .immediate - store.environment.messenger.e2e.get = { + store.dependencies.app.mainQueue = .immediate + store.dependencies.app.bgQueue = .immediate + store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact: XXClient.Contact = .unimplemented(Data()) @@ -746,9 +731,8 @@ final class MyContactFeatureTests: XCTestCase { func testErrorAlert() { let store = TestStore( - initialState: MyContactState(), - reducer: myContactReducer, - environment: .unimplemented + initialState: MyContactComponent.State(), + reducer: MyContactComponent() ) let failure = "Something went wrong" -- GitLab