diff --git a/Example/example-app/Sources/AppFeature/App.swift b/Example/example-app/Sources/AppFeature/App.swift index 1af9cbbe587b02f3da91be1a324729d1f1f155e6..ed136fb10a3f3537a911f4972ad144e53913eea4 100644 --- a/Example/example-app/Sources/AppFeature/App.swift +++ b/Example/example-app/Sources/AppFeature/App.swift @@ -29,6 +29,7 @@ extension AppEnvironment { ).eraseToAnyScheduler() return AppEnvironment( + makeId: UUID.init, hasClient: clientSubject.map { $0 != nil }.eraseToAnyPublisher(), mainScheduler: mainScheduler, landing: LandingEnvironment( diff --git a/Example/example-app/Sources/AppFeature/AppFeature.swift b/Example/example-app/Sources/AppFeature/AppFeature.swift index b99904b08b9a7f800fb7d244e5e274f3f3715c57..347c13b151a70c09e0188a4e3cd5f4ac690d4c08 100644 --- a/Example/example-app/Sources/AppFeature/AppFeature.swift +++ b/Example/example-app/Sources/AppFeature/AppFeature.swift @@ -10,7 +10,8 @@ struct AppState: Equatable { case session(SessionState) } - var scene: Scene = .landing(LandingState()) + var id: UUID = UUID() + var scene: Scene = .landing(LandingState(id: UUID())) } extension AppState.Scene { @@ -45,6 +46,7 @@ enum AppAction: Equatable { } struct AppEnvironment { + var makeId: () -> UUID var hasClient: AnyPublisher<Bool, Never> var mainScheduler: AnySchedulerOf<DispatchQueue> var landing: LandingEnvironment @@ -55,20 +57,22 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, env in switch action { case .viewDidLoad: - struct HasClientEffectId: Hashable {} + struct HasClientEffectId: Hashable { + var id: UUID + } return env.hasClient .removeDuplicates() .map(AppAction.clientDidChange(hasClient:)) .receive(on: env.mainScheduler) .eraseToEffect() - .cancellable(id: HasClientEffectId(), cancelInFlight: true) + .cancellable(id: HasClientEffectId(id: state.id), cancelInFlight: true) case .clientDidChange(let hasClient): if hasClient { - let sessionState = state.scene.asSession ?? SessionState() + let sessionState = state.scene.asSession ?? SessionState(id: env.makeId()) state.scene = .session(sessionState) } else { - let landingState = state.scene.asLanding ?? LandingState() + let landingState = state.scene.asLanding ?? LandingState(id: env.makeId()) state.scene = .landing(landingState) } return .none @@ -95,6 +99,7 @@ let appReducer = Reducer<AppState, AppAction, AppEnvironment> #if DEBUG extension AppEnvironment { static let failing = AppEnvironment( + makeId: { fatalError() }, hasClient: Empty().eraseToAnyPublisher(), mainScheduler: .failing, landing: .failing, diff --git a/Example/example-app/Sources/LandingFeature/LandingFeature.swift b/Example/example-app/Sources/LandingFeature/LandingFeature.swift index dfca1d37c532b6cdbf682c3ea5465f4829e513e0..511b1107b0afed85ddcbf2a9a03c3b99841a055b 100644 --- a/Example/example-app/Sources/LandingFeature/LandingFeature.swift +++ b/Example/example-app/Sources/LandingFeature/LandingFeature.swift @@ -5,17 +5,20 @@ import ErrorFeature public struct LandingState: Equatable { public init( + id: UUID, hasStoredClient: Bool = false, isMakingClient: Bool = false, isRemovingClient: Bool = false, error: ErrorState? = nil ) { + self.id = id self.hasStoredClient = hasStoredClient self.isMakingClient = isMakingClient self.isRemovingClient = isRemovingClient self.error = error } + var id: UUID var hasStoredClient: Bool var isMakingClient: Bool var isRemovingClient: Bool diff --git a/Example/example-app/Sources/LandingFeature/LandingView.swift b/Example/example-app/Sources/LandingFeature/LandingView.swift index a4dd2b73face637d99e3615922a512d84d9994e5..4a45669a3538af966d54876187964d4697a59ee5 100644 --- a/Example/example-app/Sources/LandingFeature/LandingView.swift +++ b/Example/example-app/Sources/LandingFeature/LandingView.swift @@ -80,7 +80,7 @@ public struct LandingView_Previews: PreviewProvider { public static var previews: some View { NavigationView { LandingView(store: .init( - initialState: .init(), + initialState: .init(id: UUID()), reducer: .empty, environment: () )) diff --git a/Example/example-app/Sources/SessionFeature/SessionFeature.swift b/Example/example-app/Sources/SessionFeature/SessionFeature.swift index c98f2d4d8581bc1c56ef64ce234f1b35d7a30e55..2e0d9c96b5430cc95ff6f37b0050a2f3314a47f2 100644 --- a/Example/example-app/Sources/SessionFeature/SessionFeature.swift +++ b/Example/example-app/Sources/SessionFeature/SessionFeature.swift @@ -2,7 +2,13 @@ import ComposableArchitecture import ElixxirDAppsSDK public struct SessionState: Equatable { - public init() {} + public init( + id: UUID + ) { + self.id = id + } + + public var id: UUID } public enum SessionAction: Equatable { diff --git a/Example/example-app/Sources/SessionFeature/SessionView.swift b/Example/example-app/Sources/SessionFeature/SessionView.swift index eb1fcf0071a4e534f867c443b2913cbab1ec0779..cbf8934e341bdc3ef6bfbb36a45531a3c97a0319 100644 --- a/Example/example-app/Sources/SessionFeature/SessionView.swift +++ b/Example/example-app/Sources/SessionFeature/SessionView.swift @@ -27,7 +27,7 @@ public struct SessionView: View { public struct SessionView_Previews: PreviewProvider { public static var previews: some View { SessionView(store: .init( - initialState: .init(), + initialState: .init(id: UUID()), reducer: .empty, environment: () )) diff --git a/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift b/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift index 96a0b07759825f391e32584051116f09acf55934..ce5891ed1d1e147abdca7390420e2888e82321a5 100644 --- a/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift +++ b/Example/example-app/Tests/AppFeatureTests/AppFeatureTests.swift @@ -7,10 +7,12 @@ import XCTest final class AppFeatureTests: XCTestCase { func testViewDidLoad() throws { + let newId = UUID() let hasClient = PassthroughSubject<Bool, Never>() let mainScheduler = DispatchQueue.test var env = AppEnvironment.failing + env.makeId = { newId } env.hasClient = hasClient.eraseToAnyPublisher() env.mainScheduler = mainScheduler.eraseToAnyScheduler() @@ -31,7 +33,7 @@ final class AppFeatureTests: XCTestCase { mainScheduler.advance() store.receive(.clientDidChange(hasClient: true)) { - $0.scene = .session(SessionState()) + $0.scene = .session(SessionState(id: newId)) } hasClient.send(true) @@ -41,7 +43,7 @@ final class AppFeatureTests: XCTestCase { mainScheduler.advance() store.receive(.clientDidChange(hasClient: false)) { - $0.scene = .landing(LandingState()) + $0.scene = .landing(LandingState(id: newId)) } hasClient.send(completion: .finished) diff --git a/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift b/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift index df9791e7de8c81a4a43f21a11e16f188afa044f7..c5b56f988cd96cf0f8a8c9e03ab2afad9ef885cc 100644 --- a/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift +++ b/Example/example-app/Tests/LandingFeatureTests/LandingFeatureTests.swift @@ -9,7 +9,7 @@ final class LandingFeatureTests: XCTestCase { env.clientStorage.hasStoredClient = { true } let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) @@ -33,7 +33,7 @@ final class LandingFeatureTests: XCTestCase { env.mainScheduler = mainScheduler.eraseToAnyScheduler() let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) @@ -68,7 +68,7 @@ final class LandingFeatureTests: XCTestCase { env.mainScheduler = mainScheduler.eraseToAnyScheduler() let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) @@ -101,7 +101,7 @@ final class LandingFeatureTests: XCTestCase { env.mainScheduler = mainScheduler.eraseToAnyScheduler() let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) @@ -133,7 +133,7 @@ final class LandingFeatureTests: XCTestCase { env.mainScheduler = mainScheduler.eraseToAnyScheduler() let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) @@ -167,7 +167,7 @@ final class LandingFeatureTests: XCTestCase { env.mainScheduler = mainScheduler.eraseToAnyScheduler() let store = TestStore( - initialState: LandingState(), + initialState: LandingState(id: UUID()), reducer: landingReducer, environment: env ) diff --git a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift index 5ed11e32b14f6ee73da18a53cee600418122f87c..1e13e0d1a2f2d9297d10b5fada28a81a7a61098a 100644 --- a/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift +++ b/Example/example-app/Tests/SessionFeatureTests/SessionFeatureTests.swift @@ -5,7 +5,7 @@ import XCTest final class SessionFeatureTests: XCTestCase { func testViewDidLoad() throws { let store = TestStore( - initialState: SessionState(), + initialState: SessionState(id: UUID()), reducer: sessionReducer, environment: .failing )