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
import SwiftUI
public struct WelcomeView: View {
public init(store: Store<WelcomeState, WelcomeAction>) {
public init(store: StoreOf<WelcomeComponent>) {
self.store = store
}
let store: Store<WelcomeState, WelcomeAction>
let store: StoreOf<WelcomeComponent>
struct ViewState: Equatable {
init(_ state: WelcomeState) {
init(_ state: WelcomeComponent.State) {
isCreatingAccount = state.isCreatingAccount
failure = state.failure
}
......@@ -69,9 +69,8 @@ public struct WelcomeView: View {
public struct WelcomeView_Previews: PreviewProvider {
public static var previews: some View {
WelcomeView(store: Store(
initialState: WelcomeState(),
reducer: .empty,
environment: ()
initialState: WelcomeComponent.State(),
reducer: EmptyReducer()
))
}
}
......
......@@ -2,8 +2,7 @@ import ComposableArchitecture
import XCTest
@testable import WelcomeFeature
@MainActor
final class WelcomeFeatureTests: XCTestCase {
final class WelcomeComponentTests: XCTestCase {
func testNewAccountTapped() {
let mainQueue = DispatchQueue.test
let bgQueue = DispatchQueue.test
......@@ -11,14 +10,13 @@ final class WelcomeFeatureTests: XCTestCase {
var didCreateMessenger = 0
let store = TestStore(
initialState: WelcomeState(),
reducer: welcomeReducer,
environment: .unimplemented
initialState: WelcomeComponent.State(),
reducer: WelcomeComponent()
)
store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
store.environment.messenger.create.run = { didCreateMessenger += 1 }
store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
store.dependencies.app.messenger.create.run = { didCreateMessenger += 1 }
store.send(.newAccountTapped) {
$0.isCreatingAccount = true
......@@ -44,14 +42,13 @@ final class WelcomeFeatureTests: XCTestCase {
let bgQueue = DispatchQueue.test
let store = TestStore(
initialState: WelcomeState(),
reducer: welcomeReducer,
environment: .unimplemented
initialState: WelcomeComponent.State(),
reducer: WelcomeComponent()
)
store.environment.mainQueue = mainQueue.eraseToAnyScheduler()
store.environment.bgQueue = bgQueue.eraseToAnyScheduler()
store.environment.messenger.create.run = { throw failure }
store.dependencies.app.mainQueue = mainQueue.eraseToAnyScheduler()
store.dependencies.app.bgQueue = bgQueue.eraseToAnyScheduler()
store.dependencies.app.messenger.create.run = { throw failure }
store.send(.newAccountTapped) {
$0.isCreatingAccount = true
......@@ -69,9 +66,8 @@ final class WelcomeFeatureTests: XCTestCase {
func testRestore() {
let store = TestStore(
initialState: WelcomeState(),
reducer: welcomeReducer,
environment: .unimplemented
initialState: WelcomeComponent.State(),
reducer: WelcomeComponent()
)
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