Skip to content
Snippets Groups Projects
Commit a82b0222 authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Update SessionFeature

parent fccbe606
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!19Update example app
......@@ -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
),
......
......@@ -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))
......
......@@ -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
......@@ -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:)
)
)
}
}
}
......
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
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment