diff --git a/Examples/xx-messenger/Package.swift b/Examples/xx-messenger/Package.swift index 4ed6cfd8bbfbd9a1da66a1743410f1e623ed2648..f7e0d0399b2b67ef32c8bece58cfb102e76864a2 100644 --- a/Examples/xx-messenger/Package.swift +++ b/Examples/xx-messenger/Package.swift @@ -21,7 +21,6 @@ let package = Package( .library(name: "AppCore", targets: ["AppCore"]), .library(name: "AppFeature", targets: ["AppFeature"]), .library(name: "HomeFeature", targets: ["HomeFeature"]), - .library(name: "LaunchFeature", targets: ["LaunchFeature"]), .library(name: "RegisterFeature", targets: ["RegisterFeature"]), .library(name: "RestoreFeature", targets: ["RestoreFeature"]), .library(name: "WelcomeFeature", targets: ["WelcomeFeature"]), @@ -104,26 +103,6 @@ let package = Package( ], swiftSettings: swiftSettings ), - .target( - name: "LaunchFeature", - dependencies: [ - .target(name: "AppCore"), - .target(name: "RegisterFeature"), - .target(name: "RestoreFeature"), - .target(name: "WelcomeFeature"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "ComposablePresentation", package: "swift-composable-presentation"), - .product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"), - ], - swiftSettings: swiftSettings - ), - .testTarget( - name: "LaunchFeatureTests", - dependencies: [ - .target(name: "LaunchFeature"), - ], - swiftSettings: swiftSettings - ), .target( name: "RegisterFeature", dependencies: [ diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift deleted file mode 100644 index 893492c0b9670af7389e341713527346eb6e4d26..0000000000000000000000000000000000000000 --- a/Examples/xx-messenger/Sources/LaunchFeature/LaunchErrorView.swift +++ /dev/null @@ -1,44 +0,0 @@ -import ComposableArchitecture -import SwiftUI - -struct LaunchErrorView: View { - var failure: String - var onRetry: () -> Void - - var body: some View { - NavigationView { - VStack(spacing: 0) { - ScrollView { - Text(failure) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - } - - Divider() - - Button { - onRetry() - } label: { - Text("Retry") - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderedProminent) - .controlSize(.large) - .padding() - } - .navigationTitle("Error") - } - .navigationViewStyle(.stack) - } -} - -#if DEBUG -struct LaunchErrorView_Previews: PreviewProvider { - static var previews: some View { - LaunchErrorView( - failure: "Something went wrong...", - onRetry: {} - ) - } -} -#endif diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift deleted file mode 100644 index a7bf89ce216e6dbeea571dc7f4ea019e8940792d..0000000000000000000000000000000000000000 --- a/Examples/xx-messenger/Sources/LaunchFeature/LaunchFeature.swift +++ /dev/null @@ -1,173 +0,0 @@ -import AppCore -import Combine -import ComposableArchitecture -import ComposablePresentation -import Foundation -import RegisterFeature -import RestoreFeature -import WelcomeFeature -import XXMessengerClient -import XXModels - -public struct LaunchState: Equatable { - public enum Screen: Equatable { - case loading - case welcome(WelcomeState) - case restore(RestoreState) - case register(RegisterState) - case failure(String) - } - - public init( - screen: Screen = .loading - ) { - self.screen = screen - } - - @BindableState public var screen: Screen -} - -extension LaunchState.Screen { - var asWelcome: WelcomeState? { - get { (/LaunchState.Screen.welcome).extract(from: self) } - set { if let state = newValue { self = .welcome(state) } } - } - var asRestore: RestoreState? { - get { (/LaunchState.Screen.restore).extract(from: self) } - set { if let state = newValue { self = .restore(state) } } - } - var asRegister: RegisterState? { - get { (/LaunchState.Screen.register).extract(from: self) } - set { if let state = newValue { self = .register(state) } } - } -} - -public enum LaunchAction: Equatable, BindableAction { - case start - case finished - case binding(BindingAction<LaunchState>) - case welcome(WelcomeAction) - case restore(RestoreAction) - case register(RegisterAction) -} - -public struct LaunchEnvironment { - public init( - dbManager: DBManager, - messenger: Messenger, - mainQueue: AnySchedulerOf<DispatchQueue>, - bgQueue: AnySchedulerOf<DispatchQueue>, - welcome: @escaping () -> WelcomeEnvironment, - restore: @escaping () -> RestoreEnvironment, - register: @escaping () -> RegisterEnvironment - ) { - self.dbManager = dbManager - self.messenger = messenger - self.mainQueue = mainQueue - self.bgQueue = bgQueue - self.welcome = welcome - self.restore = restore - self.register = register - } - - public var dbManager: DBManager - public var messenger: Messenger - public var mainQueue: AnySchedulerOf<DispatchQueue> - public var bgQueue: AnySchedulerOf<DispatchQueue> - public var welcome: () -> WelcomeEnvironment - public var restore: () -> RestoreEnvironment - public var register: () -> RegisterEnvironment -} - -extension LaunchEnvironment { - public static let unimplemented = LaunchEnvironment( - dbManager: .unimplemented, - messenger: .unimplemented, - mainQueue: .unimplemented, - bgQueue: .unimplemented, - welcome: { .unimplemented }, - restore: { .unimplemented }, - register: { .unimplemented } - ) -} - -public let launchReducer = Reducer<LaunchState, LaunchAction, LaunchEnvironment> -{ state, action, env in - switch action { - case .start, .welcome(.finished), .restore(.finished), .register(.finished): - state.screen = .loading - return .future { fulfill in - do { - if env.dbManager.hasDB() == false { - try env.dbManager.makeDB() - } - - if env.messenger.isLoaded() == false { - if env.messenger.isCreated() == false { - fulfill(.success(.set(\.$screen, .welcome(WelcomeState())))) - return - } - try env.messenger.load() - } - - try env.messenger.start() - - if env.messenger.isConnected() == false { - try env.messenger.connect() - } - - if env.messenger.isLoggedIn() == false { - if try env.messenger.isRegistered() == false { - fulfill(.success(.set(\.$screen, .register(RegisterState())))) - return - } - try env.messenger.logIn() - } - - fulfill(.success(.finished)) - } - catch { - fulfill(.success(.set(\.$screen, .failure(error.localizedDescription)))) - } - } - .subscribe(on: env.bgQueue) - .receive(on: env.mainQueue) - .eraseToEffect() - - case .finished: - return .none - - case .welcome(.restoreTapped): - state.screen = .restore(RestoreState()) - return .none - - case .welcome(.failed(let failure)): - state.screen = .failure(failure) - return .none - - case .binding(_), .welcome(_), .restore(_), .register(_): - return .none - } -} -.binding() -.presenting( - welcomeReducer, - state: .keyPath(\.screen.asWelcome), - id: .notNil(), - action: /LaunchAction.welcome, - environment: { $0.welcome() } -) -.presenting( - restoreReducer, - state: .keyPath(\.screen.asRestore), - id: .notNil(), - action: /LaunchAction.restore, - environment: { $0.restore() } -) -.presenting( - registerReducer, - state: .keyPath(\.screen.asRegister), - id: .notNil(), - action: /LaunchAction.register, - environment: { $0.register() } -) diff --git a/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift b/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift deleted file mode 100644 index ed110302f45ff390664f9af2e939571c9bbb2646..0000000000000000000000000000000000000000 --- a/Examples/xx-messenger/Sources/LaunchFeature/LaunchView.swift +++ /dev/null @@ -1,118 +0,0 @@ -import ComposableArchitecture -import RegisterFeature -import RestoreFeature -import SwiftUI -import WelcomeFeature - -public struct LaunchView: View { - public init(store: Store<LaunchState, LaunchAction>) { - self.store = store - } - - struct ViewState: Equatable { - enum Screen: Equatable { - case loading - case welcome - case restore - case register - case failure(String) - } - - init(_ state: LaunchState) { - switch state.screen { - case .loading: screen = .loading - case .welcome(_): screen = .welcome - case .restore(_): screen = .restore - case .register(_): screen = .register - case .failure(let failure): screen = .failure(failure) - } - } - - var screen: Screen - } - - let store: Store<LaunchState, LaunchAction> - - public var body: some View { - WithViewStore(store.scope(state: ViewState.init)) { viewStore in - ZStack { - switch viewStore.screen { - case .loading: - ProgressView { - Text("Loading") - } - .controlSize(.large) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .transition(.opacity) - - case .welcome: - IfLetStore( - store.scope( - state: { (/LaunchState.Screen.welcome).extract(from: $0.screen) }, - action: LaunchAction.welcome - ), - then: WelcomeView.init(store:) - ) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .transition(.asymmetric( - insertion: .move(edge: .trailing), - removal: .opacity - )) - - case .restore: - IfLetStore( - store.scope( - state: { (/LaunchState.Screen.restore).extract(from: $0.screen) }, - action: LaunchAction.restore - ), - then: RestoreView.init(store:) - ) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .transition(.asymmetric( - insertion: .move(edge: .trailing), - removal: .opacity - )) - - case .register: - IfLetStore( - store.scope( - state: { (/LaunchState.Screen.register).extract(from: $0.screen) }, - action: LaunchAction.register - ), - then: RegisterView.init(store:) - ) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .transition(.asymmetric( - insertion: .move(edge: .trailing), - removal: .opacity - )) - - case .failure(let failure): - LaunchErrorView( - failure: failure, - onRetry: { viewStore.send(.start) } - ) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .transition(.asymmetric( - insertion: .move(edge: .trailing), - removal: .opacity - )) - } - } - .animation(.default, value: viewStore.screen) - .task { viewStore.send(.start) } - } - } -} - -#if DEBUG -public struct LaunchView_Previews: PreviewProvider { - public static var previews: some View { - LaunchView(store: Store( - initialState: LaunchState(), - reducer: .empty, - environment: () - )) - } -} -#endif diff --git a/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift b/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift deleted file mode 100644 index b2b3f856f79260ef652664f9c9892382296ede95..0000000000000000000000000000000000000000 --- a/Examples/xx-messenger/Tests/LaunchFeatureTests/LaunchFeatureTests.swift +++ /dev/null @@ -1,327 +0,0 @@ -import AppCore -import ComposableArchitecture -import RegisterFeature -import RestoreFeature -import WelcomeFeature -import XCTest -import XXModels -@testable import LaunchFeature - -@MainActor -final class LaunchFeatureTests: XCTestCase { - func testStart() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - var didMakeDB = 0 - var messengerDidLoad = 0 - var messengerDidStart = 0 - var messengerDidConnect = 0 - var messengerDidLogIn = 0 - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { false } - store.environment.dbManager.makeDB.run = { didMakeDB += 1 } - store.environment.messenger.isLoaded.run = { false } - store.environment.messenger.isCreated.run = { true } - store.environment.messenger.load.run = { messengerDidLoad += 1 } - store.environment.messenger.start.run = { _ in messengerDidStart += 1 } - store.environment.messenger.isConnected.run = { false } - store.environment.messenger.connect.run = { messengerDidConnect += 1 } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { true } - store.environment.messenger.logIn.run = { messengerDidLogIn += 1 } - - store.send(.start) - - bgQueue.advance() - - XCTAssertNoDifference(didMakeDB, 1) - XCTAssertNoDifference(messengerDidLoad, 1) - XCTAssertNoDifference(messengerDidStart, 1) - XCTAssertNoDifference(messengerDidConnect, 1) - XCTAssertNoDifference(messengerDidLogIn, 1) - - mainQueue.advance() - - store.receive(.finished) - } - - func testStartWithoutMessengerCreated() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { false } - store.environment.messenger.isCreated.run = { false } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .welcome(WelcomeState()))) { - $0.screen = .welcome(WelcomeState()) - } - } - - func testStartUnregistered() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { true } - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { false } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .register(RegisterState()))) { - $0.screen = .register(RegisterState()) - } - } - - func testStartMakeDBFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { false } - store.environment.dbManager.makeDB.run = { throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testStartMessengerLoadFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { false } - store.environment.messenger.isCreated.run = { true } - store.environment.messenger.load.run = { throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testStartMessengerStartFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { true } - store.environment.messenger.start.run = { _ in throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testStartMessengerConnectFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { true } - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { false } - store.environment.messenger.connect.run = { throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testStartMessengerIsRegisteredFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { true } - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testStartMessengerLogInFailure() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - let mainQueue = DispatchQueue.test - let bgQueue = DispatchQueue.test - struct Error: Swift.Error {} - let error = Error() - - store.environment.mainQueue = mainQueue.eraseToAnyScheduler() - store.environment.bgQueue = bgQueue.eraseToAnyScheduler() - store.environment.dbManager.hasDB.run = { true } - store.environment.messenger.isLoaded.run = { true } - store.environment.messenger.start.run = { _ in } - store.environment.messenger.isConnected.run = { true } - store.environment.messenger.isLoggedIn.run = { false } - store.environment.messenger.isRegistered.run = { true } - store.environment.messenger.logIn.run = { throw error } - - store.send(.start) - - bgQueue.advance() - mainQueue.advance() - - store.receive(.set(\.$screen, .failure(error.localizedDescription))) { - $0.screen = .failure(error.localizedDescription) - } - } - - func testWelcomeRestoreTapped() { - let store = TestStore( - initialState: LaunchState( - screen: .welcome(WelcomeState()) - ), - reducer: launchReducer, - environment: .unimplemented - ) - - store.send(.welcome(.restoreTapped)) { - $0.screen = .restore(RestoreState()) - } - } - - func testWelcomeFailed() { - let store = TestStore( - initialState: LaunchState( - screen: .welcome(WelcomeState()) - ), - reducer: launchReducer, - environment: .unimplemented - ) - - let failure = "Something went wrong" - - store.send(.welcome(.failed(failure))) { - $0.screen = .failure(failure) - } - } - - func testFinished() { - let store = TestStore( - initialState: LaunchState(), - reducer: launchReducer, - environment: .unimplemented - ) - - store.send(.finished) - } -}