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

Migrate ContactLookupFeature to ReducerProtocol

parent 461c4734
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 Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
public struct ContactLookupComponent: ReducerProtocol {
public struct State: Equatable {
public init(
id: Data,
isLookingUp: Bool = false,
failure: String? = nil
) {
self.id = id
self.isLookingUp = isLookingUp
self.failure = failure
}
public var id: Data
public var isLookingUp: Bool
public var failure: String?
}
public enum Action: Equatable {
case lookupTapped
case didLookup(XXClient.Contact)
case didFail(NSError)
}
@Dependency(\.appDependencies.messenger) var messenger
@Dependency(\.appDependencies.mainQueue) var mainQueue
@Dependency(\.appDependencies.bgQueue) var bgQueue
public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .lookupTapped:
state.isLookingUp = true
state.failure = nil
return Effect.result { [state] in
do {
let contact = try messenger.lookupContact(id: state.id)
return .success(.didLookup(contact))
} catch {
return .success(.didFail(error as NSError))
}
}
.subscribe(on: bgQueue)
.receive(on: mainQueue)
.eraseToEffect()
case .didLookup(_):
state.isLookingUp = false
state.failure = nil
return .none
case .didFail(let error):
state.isLookingUp = false
state.failure = error.localizedDescription
return .none
}
}
}
import ComposableArchitecture
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
public struct ContactLookupState: Equatable {
public init(
id: Data,
isLookingUp: Bool = false,
failure: String? = nil
) {
self.id = id
self.isLookingUp = isLookingUp
self.failure = failure
}
public var id: Data
public var isLookingUp: Bool
public var failure: String?
}
public enum ContactLookupAction: Equatable {
case lookupTapped
case didLookup(XXClient.Contact)
case didFail(NSError)
}
public struct ContactLookupEnvironment {
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 ContactLookupEnvironment {
public static let unimplemented = ContactLookupEnvironment(
messenger: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented
)
}
#endif
public let contactLookupReducer = Reducer<ContactLookupState, ContactLookupAction, ContactLookupEnvironment>
{ state, action, env in
switch action {
case .lookupTapped:
state.isLookingUp = true
state.failure = nil
return Effect.result { [state] in
do {
let contact = try env.messenger.lookupContact(id: state.id)
return .success(.didLookup(contact))
} catch {
return .success(.didFail(error as NSError))
}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .didLookup(_):
state.isLookingUp = false
state.failure = nil
return .none
case .didFail(let error):
state.isLookingUp = false
state.failure = error.localizedDescription
return .none
}
}
...@@ -3,14 +3,14 @@ import ComposableArchitecture ...@@ -3,14 +3,14 @@ import ComposableArchitecture
import SwiftUI import SwiftUI
public struct ContactLookupView: View { public struct ContactLookupView: View {
public init(store: Store<ContactLookupState, ContactLookupAction>) { public init(store: StoreOf<ContactLookupComponent>) {
self.store = store self.store = store
} }
let store: Store<ContactLookupState, ContactLookupAction> let store: StoreOf<ContactLookupComponent>
struct ViewState: Equatable { struct ViewState: Equatable {
init(state: ContactLookupState) { init(state: ContactLookupComponent.State) {
id = state.id id = state.id
isLookingUp = state.isLookingUp isLookingUp = state.isLookingUp
failure = state.failure failure = state.failure
...@@ -64,11 +64,10 @@ public struct ContactLookupView_Previews: PreviewProvider { ...@@ -64,11 +64,10 @@ public struct ContactLookupView_Previews: PreviewProvider {
public static var previews: some View { public static var previews: some View {
NavigationView { NavigationView {
ContactLookupView(store: Store( ContactLookupView(store: Store(
initialState: ContactLookupState( initialState: ContactLookupComponent.State(
id: "1234".data(using: .utf8)! id: "1234".data(using: .utf8)!
), ),
reducer: .empty, reducer: EmptyReducer()
environment: ()
)) ))
} }
} }
......
...@@ -3,20 +3,19 @@ import XCTest ...@@ -3,20 +3,19 @@ import XCTest
import XXClient import XXClient
@testable import ContactLookupFeature @testable import ContactLookupFeature
final class ContactLookupFeatureTests: XCTestCase { final class ContactLookupComponentTests: XCTestCase {
func testLookup() { func testLookup() {
let id: Data = "1234".data(using: .utf8)! let id: Data = "1234".data(using: .utf8)!
var didLookupId: [Data] = [] var didLookupId: [Data] = []
let lookedUpContact = Contact.unimplemented("123data".data(using: .utf8)!) let lookedUpContact = Contact.unimplemented("123data".data(using: .utf8)!)
let store = TestStore( let store = TestStore(
initialState: ContactLookupState(id: id), initialState: ContactLookupComponent.State(id: id),
reducer: contactLookupReducer, reducer: ContactLookupComponent()
environment: .unimplemented
) )
store.environment.mainQueue = .immediate store.dependencies.appDependencies.mainQueue = .immediate
store.environment.bgQueue = .immediate store.dependencies.appDependencies.bgQueue = .immediate
store.environment.messenger.lookupContact.run = { id in store.dependencies.appDependencies.messenger.lookupContact.run = { id in
didLookupId.append(id) didLookupId.append(id)
return lookedUpContact return lookedUpContact
} }
...@@ -39,13 +38,12 @@ final class ContactLookupFeatureTests: XCTestCase { ...@@ -39,13 +38,12 @@ final class ContactLookupFeatureTests: XCTestCase {
let failure = NSError(domain: "test", code: 0) let failure = NSError(domain: "test", code: 0)
let store = TestStore( let store = TestStore(
initialState: ContactLookupState(id: id), initialState: ContactLookupComponent.State(id: id),
reducer: contactLookupReducer, reducer: ContactLookupComponent()
environment: .unimplemented
) )
store.environment.mainQueue = .immediate store.dependencies.appDependencies.mainQueue = .immediate
store.environment.bgQueue = .immediate store.dependencies.appDependencies.bgQueue = .immediate
store.environment.messenger.lookupContact.run = { _ in throw failure } store.dependencies.appDependencies.messenger.lookupContact.run = { _ in throw failure }
store.send(.lookupTapped) { store.send(.lookupTapped) {
$0.isLookingUp = true $0.isLookingUp = true
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment