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

Migrate WelcomeFeature to ReducerProtocol

parent dc24e4a4
No related branches found
No related tags found
2 merge requests!126Migrate example app to ComposableArchitecture's ReducerProtocol,!102Release 1.0.0
import ComposableArchitecture
import SwiftUI
import XXMessengerClient
public struct WelcomeComponent: ReducerProtocol {
public struct State: Equatable {
public init(
isCreatingCMix: Bool = false,
failure: String? = nil
) {
self.isCreatingAccount = isCreatingCMix
self.failure = failure
}
public var isCreatingAccount: Bool
public var failure: String?
}
public enum Action: Equatable {
case newAccountTapped
case restoreTapped
case finished
case failed(String)
}
public init() {}
@Dependency(\.app.messenger) var messenger: Messenger
@Dependency(\.app.mainQueue) var mainQueue: AnySchedulerOf<DispatchQueue>
@Dependency(\.app.bgQueue) var bgQueue: AnySchedulerOf<DispatchQueue>
public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .newAccountTapped:
state.isCreatingAccount = true
state.failure = nil
return .future { fulfill in
do {
try messenger.create()
fulfill(.success(.finished))
}
catch {
fulfill(.success(.failed(error.localizedDescription)))
}
}
.subscribe(on: bgQueue)
.receive(on: mainQueue)
.eraseToEffect()
case .restoreTapped:
return .none
case .finished:
state.isCreatingAccount = false
state.failure = nil
return .none
case .failed(let failure):
state.isCreatingAccount = false
state.failure = failure
return .none
}
}
}
import ComposableArchitecture
import SwiftUI
import XXMessengerClient
public struct WelcomeState: Equatable {
public init(
isCreatingCMix: Bool = false,
failure: String? = nil
) {
self.isCreatingAccount = isCreatingCMix
self.failure = failure
}
public var isCreatingAccount: Bool
public var failure: String?
}
public enum WelcomeAction: Equatable {
case newAccountTapped
case restoreTapped
case finished
case failed(String)
}
public struct WelcomeEnvironment {
public init(
messenger: Messenger,
mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>
) {
self.messenger = messenger
self.mainQueue = mainQueue
self.bgQueue = bgQueue
}
public var messenger: Messenger
public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue>
}
#if DEBUG
extension WelcomeEnvironment {
public static let unimplemented = WelcomeEnvironment(
messenger: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented
)
}
#endif
public let welcomeReducer = Reducer<WelcomeState, WelcomeAction, WelcomeEnvironment>
{ state, action, env in
switch action {
case .newAccountTapped:
state.isCreatingAccount = true
state.failure = nil
return .future { fulfill in
do {
try env.messenger.create()
fulfill(.success(.finished))
}
catch {
fulfill(.success(.failed(error.localizedDescription)))
}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .restoreTapped:
return .none
case .finished:
state.isCreatingAccount = false
state.failure = nil
return .none
case .failed(let failure):
state.isCreatingAccount = false
state.failure = failure
return .none
}
}
...@@ -3,14 +3,14 @@ import ComposableArchitecture ...@@ -3,14 +3,14 @@ import ComposableArchitecture
import SwiftUI import SwiftUI
public struct WelcomeView: View { public struct WelcomeView: View {
public init(store: Store<WelcomeState, WelcomeAction>) { public init(store: StoreOf<WelcomeComponent>) {
self.store = store self.store = store
} }
let store: Store<WelcomeState, WelcomeAction> let store: StoreOf<WelcomeComponent>
struct ViewState: Equatable { struct ViewState: Equatable {
init(_ state: WelcomeState) { init(_ state: WelcomeComponent.State) {
isCreatingAccount = state.isCreatingAccount isCreatingAccount = state.isCreatingAccount
failure = state.failure failure = state.failure
} }
...@@ -69,9 +69,8 @@ public struct WelcomeView: View { ...@@ -69,9 +69,8 @@ public struct WelcomeView: View {
public struct WelcomeView_Previews: PreviewProvider { public struct WelcomeView_Previews: PreviewProvider {
public static var previews: some View { public static var previews: some View {
WelcomeView(store: Store( WelcomeView(store: Store(
initialState: WelcomeState(), initialState: WelcomeComponent.State(),
reducer: .empty, reducer: EmptyReducer()
environment: ()
)) ))
} }
} }
......
...@@ -2,8 +2,7 @@ import ComposableArchitecture ...@@ -2,8 +2,7 @@ import ComposableArchitecture
import XCTest import XCTest
@testable import WelcomeFeature @testable import WelcomeFeature
@MainActor final class WelcomeComponentTests: XCTestCase {
final class WelcomeFeatureTests: XCTestCase {
func testNewAccountTapped() { func testNewAccountTapped() {
let mainQueue = DispatchQueue.test let mainQueue = DispatchQueue.test
let bgQueue = DispatchQueue.test let bgQueue = DispatchQueue.test
...@@ -11,14 +10,13 @@ final class WelcomeFeatureTests: XCTestCase { ...@@ -11,14 +10,13 @@ final class WelcomeFeatureTests: XCTestCase {
var didCreateMessenger = 0 var didCreateMessenger = 0
let store = TestStore( let store = TestStore(
initialState: WelcomeState(), initialState: WelcomeComponent.State(),
reducer: welcomeReducer, reducer: WelcomeComponent()
environment: .unimplemented
) )
store.environment.mainQueue = mainQueue.eraseToAnyScheduler() store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
store.environment.bgQueue = bgQueue.eraseToAnyScheduler() store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
store.environment.messenger.create.run = { didCreateMessenger += 1 } store.dependencies.app.messenger.create.run = { didCreateMessenger += 1 }
store.send(.newAccountTapped) { store.send(.newAccountTapped) {
$0.isCreatingAccount = true $0.isCreatingAccount = true
...@@ -44,14 +42,13 @@ final class WelcomeFeatureTests: XCTestCase { ...@@ -44,14 +42,13 @@ final class WelcomeFeatureTests: XCTestCase {
let bgQueue = DispatchQueue.test let bgQueue = DispatchQueue.test
let store = TestStore( let store = TestStore(
initialState: WelcomeState(), initialState: WelcomeComponent.State(),
reducer: welcomeReducer, reducer: WelcomeComponent()
environment: .unimplemented
) )
store.environment.mainQueue = mainQueue.eraseToAnyScheduler() store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
store.environment.bgQueue = bgQueue.eraseToAnyScheduler() store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
store.environment.messenger.create.run = { throw failure } store.dependencies.app.messenger.create.run = { throw failure }
store.send(.newAccountTapped) { store.send(.newAccountTapped) {
$0.isCreatingAccount = true $0.isCreatingAccount = true
...@@ -69,9 +66,8 @@ final class WelcomeFeatureTests: XCTestCase { ...@@ -69,9 +66,8 @@ final class WelcomeFeatureTests: XCTestCase {
func testRestore() { func testRestore() {
let store = TestStore( let store = TestStore(
initialState: WelcomeState(), initialState: WelcomeComponent.State(),
reducer: welcomeReducer, reducer: WelcomeComponent()
environment: .unimplemented
) )
store.send(.restoreTapped) store.send(.restoreTapped)
......
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