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