From 44bd113210cb0ee246b96cf1cf73d58d7269a7f2 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Mon, 6 Jun 2022 12:01:43 +0200
Subject: [PATCH] Add id to feature states

---
 Example/example-app/Sources/AppFeature/App.swift  |  1 +
 .../Sources/AppFeature/AppFeature.swift           | 15 ++++++++++-----
 .../Sources/LandingFeature/LandingFeature.swift   |  3 +++
 .../Sources/LandingFeature/LandingView.swift      |  2 +-
 .../Sources/SessionFeature/SessionFeature.swift   |  8 +++++++-
 .../Sources/SessionFeature/SessionView.swift      |  2 +-
 .../Tests/AppFeatureTests/AppFeatureTests.swift   |  6 ++++--
 .../LandingFeatureTests/LandingFeatureTests.swift | 12 ++++++------
 .../SessionFeatureTests/SessionFeatureTests.swift |  2 +-
 9 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/Example/example-app/Sources/AppFeature/App.swift b/Example/example-app/Sources/AppFeature/App.swift
index 1af9cbbe..ed136fb1 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 b99904b0..347c13b1 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 dfca1d37..511b1107 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 a4dd2b73..4a45669a 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 c98f2d4d..2e0d9c96 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 eb1fcf00..cbf8934e 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 96a0b077..ce5891ed 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 df9791e7..c5b56f98 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 5ed11e32..1e13e0d1 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
     )
-- 
GitLab