From a82b0222a51458aaf829edb429fd96f189e23d3d Mon Sep 17 00:00:00 2001 From: Dariusz Rybicki <dariusz@elixxir.io> Date: Wed, 3 Aug 2022 13:43:41 +0100 Subject: [PATCH] Update SessionFeature --- Example/example-app/Package.swift | 6 +- .../NetworkFollowerStatusView.swift | 4 - .../SessionFeature/SessionFeature.swift | 97 +++----------- .../Sources/SessionFeature/SessionView.swift | 48 ------- .../SessionFeatureTests.swift | 122 +++++------------- 5 files changed, 55 insertions(+), 222 deletions(-) diff --git a/Example/example-app/Package.swift b/Example/example-app/Package.swift index 1d3dc343..1eee8b4a 100644 --- a/Example/example-app/Package.swift +++ b/Example/example-app/Package.swift @@ -210,8 +210,6 @@ let package = Package( name: "SessionFeature", dependencies: [ .target(name: "ErrorFeature"), - .target(name: "MyContactFeature"), - .target(name: "MyIdentityFeature"), .product( name: "ComposableArchitecture", package: "swift-composable-architecture" @@ -224,6 +222,10 @@ let package = Package( name: "ElixxirDAppsSDK", package: "elixxir-dapps-sdk-swift" ), + .product( + name: "XCTestDynamicOverlay", + package: "xctest-dynamic-overlay" + ), ], swiftSettings: swiftSettings ), diff --git a/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift b/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift index 5d1ba1de..dbb06c4f 100644 --- a/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift +++ b/Example/example-app/Sources/SessionFeature/NetworkFollowerStatusView.swift @@ -9,9 +9,6 @@ struct NetworkFollowerStatusView: View { case .stopped: Label("Stopped", systemImage: "stop.fill") - case .starting: - Label("Starting...", systemImage: "play") - case .running: Label("Running", systemImage: "play.fill") @@ -32,7 +29,6 @@ struct NetworkFollowerStatusView_Previews: PreviewProvider { static var previews: some View { Group { NetworkFollowerStatusView(status: .stopped) - NetworkFollowerStatusView(status: .starting) NetworkFollowerStatusView(status: .running) NetworkFollowerStatusView(status: .stopping) NetworkFollowerStatusView(status: .unknown(code: -1)) diff --git a/Example/example-app/Sources/SessionFeature/SessionFeature.swift b/Example/example-app/Sources/SessionFeature/SessionFeature.swift index 5b1005a9..a5bd6f9c 100644 --- a/Example/example-app/Sources/SessionFeature/SessionFeature.swift +++ b/Example/example-app/Sources/SessionFeature/SessionFeature.swift @@ -2,32 +2,25 @@ import Combine import ComposableArchitecture import ElixxirDAppsSDK import ErrorFeature -import MyContactFeature -import MyIdentityFeature +import XCTestDynamicOverlay public struct SessionState: Equatable { public init( id: UUID, networkFollowerStatus: NetworkFollowerStatus? = nil, isNetworkHealthy: Bool? = nil, - error: ErrorState? = nil, - myIdentity: MyIdentityState? = nil, - myContact: MyContactState? = nil + error: ErrorState? = nil ) { self.id = id self.networkFollowerStatus = networkFollowerStatus self.isNetworkHealthy = isNetworkHealthy self.error = error - self.myIdentity = myIdentity - self.myContact = myContact } public var id: UUID public var networkFollowerStatus: NetworkFollowerStatus? public var isNetworkHealthy: Bool? public var error: ErrorState? - public var myIdentity: MyIdentityState? - public var myContact: MyContactState? } public enum SessionAction: Equatable { @@ -39,41 +32,26 @@ public enum SessionAction: Equatable { case monitorNetworkHealth(Bool) case didUpdateNetworkHealth(Bool?) case didDismissError - case presentMyIdentity - case didDismissMyIdentity - case presentMyContact - case didDismissMyContact case error(ErrorAction) - case myIdentity(MyIdentityAction) - case myContact(MyContactAction) } public struct SessionEnvironment { public init( - getClient: @escaping () -> Client?, + getCmix: @escaping () -> Cmix?, bgScheduler: AnySchedulerOf<DispatchQueue>, mainScheduler: AnySchedulerOf<DispatchQueue>, - makeId: @escaping () -> UUID, - error: ErrorEnvironment, - myIdentity: MyIdentityEnvironment, - myContact: MyContactEnvironment + error: ErrorEnvironment ) { - self.getClient = getClient + self.getCmix = getCmix self.bgScheduler = bgScheduler self.mainScheduler = mainScheduler - self.makeId = makeId self.error = error - self.myIdentity = myIdentity - self.myContact = myContact } - public var getClient: () -> Client? + public var getCmix: () -> Cmix? public var bgScheduler: AnySchedulerOf<DispatchQueue> public var mainScheduler: AnySchedulerOf<DispatchQueue> - public var makeId: () -> UUID public var error: ErrorEnvironment - public var myIdentity: MyIdentityEnvironment - public var myContact: MyContactEnvironment } public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironment> @@ -87,7 +65,7 @@ public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironm case .updateNetworkFollowerStatus: return Effect.future { fulfill in - let status = env.getClient()?.networkFollower.status() + let status = env.getCmix()?.networkFollowerStatus() fulfill(.success(.didUpdateNetworkFollowerStatus(status))) } .subscribe(on: env.bgScheduler) @@ -99,18 +77,17 @@ public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironm return .none case .runNetworkFollower(let start): - state.networkFollowerStatus = start ? .starting : .stopping return Effect.run { subscriber in do { if start { - try env.getClient()?.networkFollower.start(timeoutMS: 30_000) + try env.getCmix()?.startNetworkFollower(timeoutMS: 30_000) } else { - try env.getClient()?.networkFollower.stop() + try env.getCmix()?.stopNetworkFollower() } } catch { subscriber.send(.networkFollowerDidFail(error as NSError)) } - let status = env.getClient()?.networkFollower.status() + let status = env.getCmix()?.networkFollowerStatus() subscriber.send(.didUpdateNetworkFollowerStatus(status)) subscriber.send(completion: .finished) return AnyCancellable {} @@ -130,9 +107,10 @@ public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironm let effectId = MonitorEffectId(id: state.id) if start { return Effect.run { subscriber in - var cancellable = env.getClient()?.monitorNetworkHealth { isHealthy in + let callback = HealthCallback { isHealthy in subscriber.send(.didUpdateNetworkHealth(isHealthy)) } + let cancellable = env.getCmix()?.addHealthCallback(callback) return AnyCancellable { cancellable?.cancel() } @@ -155,27 +133,7 @@ public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironm state.error = nil return .none - case .presentMyIdentity: - if state.myIdentity == nil { - state.myIdentity = MyIdentityState(id: env.makeId()) - } - return .none - - case .didDismissMyIdentity: - state.myIdentity = nil - return .none - - case .presentMyContact: - if state.myContact == nil { - state.myContact = MyContactState(id: env.makeId()) - } - return .none - - case .didDismissMyContact: - state.myContact = nil - return .none - - case .error(_), .myIdentity(_), .myContact(_): + case .error(_): return .none } } @@ -186,31 +144,12 @@ public let sessionReducer = Reducer<SessionState, SessionAction, SessionEnvironm action: /SessionAction.error, environment: \.error ) -.presenting( - myIdentityReducer, - state: .keyPath(\.myIdentity), - id: .keyPath(\.?.id), - action: /SessionAction.myIdentity, - environment: \.myIdentity -) -.presenting( - myContactReducer, - state: .keyPath(\.myContact), - id: .keyPath(\.?.id), - action: /SessionAction.myContact, - environment: \.myContact -) -#if DEBUG extension SessionEnvironment { - public static let failing = SessionEnvironment( - getClient: { .failing }, - bgScheduler: .failing, - mainScheduler: .failing, - makeId: { fatalError() }, - error: .failing, - myIdentity: .failing, - myContact: .failing + public static let unimplemented = SessionEnvironment( + getCmix: XCTUnimplemented("\(Self.self).getCmix"), + bgScheduler: .unimplemented, + mainScheduler: .unimplemented, + error: .unimplemented ) } -#endif diff --git a/Example/example-app/Sources/SessionFeature/SessionView.swift b/Example/example-app/Sources/SessionFeature/SessionView.swift index ea14d91f..395cfcb1 100644 --- a/Example/example-app/Sources/SessionFeature/SessionView.swift +++ b/Example/example-app/Sources/SessionFeature/SessionView.swift @@ -2,8 +2,6 @@ import ComposableArchitecture import ComposablePresentation import ElixxirDAppsSDK import ErrorFeature -import MyContactFeature -import MyIdentityFeature import SwiftUI public struct SessionView: View { @@ -51,28 +49,6 @@ public struct SessionView: View { } header: { Text("Network health") } - - Section { - Button { - viewStore.send(.presentMyIdentity) - } label: { - HStack { - Text("My identity") - Spacer() - Image(systemName: "chevron.forward") - } - } - - Button { - viewStore.send(.presentMyContact) - } label: { - HStack { - Text("My contact") - Spacer() - Image(systemName: "chevron.forward") - } - } - } } .navigationTitle("Session") .task { @@ -88,30 +64,6 @@ public struct SessionView: View { }, content: ErrorView.init(store:) ) - .background( - NavigationLinkWithStore( - store.scope( - state: \.myIdentity, - action: SessionAction.myIdentity - ), - onDeactivate: { - viewStore.send(.didDismissMyIdentity) - }, - destination: MyIdentityView.init(store:) - ) - ) - .background( - NavigationLinkWithStore( - store.scope( - state: \.myContact, - action: SessionAction.myContact - ), - onDeactivate: { - viewStore.send(.didDismissMyContact) - }, - destination: MyContactView.init(store:) - ) - ) } } } diff --git a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift index 5c1aa3ba..2b3250c6 100644 --- a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift +++ b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift @@ -1,8 +1,6 @@ import ComposableArchitecture import ElixxirDAppsSDK import ErrorFeature -import MyContactFeature -import MyIdentityFeature import XCTest @testable import SessionFeature @@ -11,31 +9,30 @@ final class SessionFeatureTests: XCTestCase { var networkFollowerStatus: NetworkFollowerStatus! var didStartMonitoringNetworkHealth = 0 var didStopMonitoringNetworkHealth = 0 - var networkHealthCallback: ((Bool) -> Void)! + var networkHealthCallback: HealthCallback! let bgScheduler = DispatchQueue.test let mainScheduler = DispatchQueue.test - var env = SessionEnvironment.failing - env.getClient = { - var client = Client.failing - client.networkFollower.status.status = { networkFollowerStatus } - client.monitorNetworkHealth.listen = { callback in + let store = TestStore( + initialState: SessionState(id: UUID()), + reducer: sessionReducer, + environment: .unimplemented + ) + + store.environment.getCmix = { + var cmix = Cmix.unimplemented + cmix.networkFollowerStatus.run = { networkFollowerStatus } + cmix.addHealthCallback.run = { callback in networkHealthCallback = callback didStartMonitoringNetworkHealth += 1 return Cancellable { didStopMonitoringNetworkHealth += 1 } } - return client + return cmix } - env.bgScheduler = bgScheduler.eraseToAnyScheduler() - env.mainScheduler = mainScheduler.eraseToAnyScheduler() - - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: env - ) + store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() + store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() store.send(.viewDidLoad) @@ -53,7 +50,7 @@ final class SessionFeatureTests: XCTestCase { XCTAssertEqual(didStartMonitoringNetworkHealth, 1) XCTAssertEqual(didStopMonitoringNetworkHealth, 0) - networkHealthCallback(true) + networkHealthCallback.handle(true) bgScheduler.advance() mainScheduler.advance() @@ -77,35 +74,30 @@ final class SessionFeatureTests: XCTestCase { let bgScheduler = DispatchQueue.test let mainScheduler = DispatchQueue.test - var env = SessionEnvironment.failing - env.getClient = { - var client = Client.failing - client.networkFollower.status.status = { - networkFollowerStatus - } - client.networkFollower.start.start = { + let store = TestStore( + initialState: SessionState(id: UUID()), + reducer: sessionReducer, + environment: .unimplemented + ) + + store.environment.getCmix = { + var cmix = Cmix.unimplemented + cmix.networkFollowerStatus.run = { networkFollowerStatus } + cmix.startNetworkFollower.run = { didStartNetworkFollowerWithTimeout.append($0) if let error = networkFollowerStartError { throw error } } - client.networkFollower.stop.stop = { + cmix.stopNetworkFollower.run = { didStopNetworkFollower += 1 } - return client + return cmix } - env.bgScheduler = bgScheduler.eraseToAnyScheduler() - env.mainScheduler = mainScheduler.eraseToAnyScheduler() + store.environment.bgScheduler = bgScheduler.eraseToAnyScheduler() + store.environment.mainScheduler = mainScheduler.eraseToAnyScheduler() - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: env - ) - - store.send(.runNetworkFollower(true)) { - $0.networkFollowerStatus = .starting - } + store.send(.runNetworkFollower(true)) networkFollowerStatus = .running bgScheduler.advance() @@ -118,9 +110,7 @@ final class SessionFeatureTests: XCTestCase { $0.networkFollowerStatus = .running } - store.send(.runNetworkFollower(false)) { - $0.networkFollowerStatus = .stopping - } + store.send(.runNetworkFollower(false)) networkFollowerStatus = .stopped bgScheduler.advance() @@ -133,9 +123,7 @@ final class SessionFeatureTests: XCTestCase { $0.networkFollowerStatus = .stopped } - store.send(.runNetworkFollower(true)) { - $0.networkFollowerStatus = .starting - } + store.send(.runNetworkFollower(true)) networkFollowerStartError = NSError(domain: "test", code: 1234) networkFollowerStatus = .stopped @@ -149,54 +137,10 @@ final class SessionFeatureTests: XCTestCase { $0.error = ErrorState(error: networkFollowerStartError!) } - store.receive(.didUpdateNetworkFollowerStatus(.stopped)) { - $0.networkFollowerStatus = .stopped - } + store.receive(.didUpdateNetworkFollowerStatus(.stopped)) store.send(.didDismissError) { $0.error = nil } } - - func testPresentingMyIdentity() { - let newId = UUID() - - var env = SessionEnvironment.failing - env.makeId = { newId } - - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: env - ) - - store.send(.presentMyIdentity) { - $0.myIdentity = MyIdentityState(id: newId) - } - - store.send(.didDismissMyIdentity) { - $0.myIdentity = nil - } - } - - func testPresentingMyContact() { - let newId = UUID() - - var env = SessionEnvironment.failing - env.makeId = { newId } - - let store = TestStore( - initialState: SessionState(id: UUID()), - reducer: sessionReducer, - environment: env - ) - - store.send(.presentMyContact) { - $0.myContact = MyContactState(id: newId) - } - - store.send(.didDismissMyContact) { - $0.myContact = nil - } - } } -- GitLab