[#462] Migrate Profile to ReducerProtocol (#484)

- Profile migrated to ReducerProtocol
- unit and snapshot tests fixed
- sandbox feature still works with all the TCA pullback/scope navigation
This commit is contained in:
Lukas Korba 2022-11-08 09:36:23 +01:00 committed by GitHub
parent 046681efff
commit 7f6c104d28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 155 deletions

View File

@ -6,6 +6,7 @@
// //
import Foundation import Foundation
import ComposableArchitecture
struct AppVersionHandler { struct AppVersionHandler {
let appVersion: () -> String let appVersion: () -> String
@ -23,3 +24,15 @@ extension AppVersionHandler {
appBuild: { "31" } appBuild: { "31" }
) )
} }
private enum AppVersionHandlerKey: DependencyKey {
static let liveValue = AppVersionHandler.live
static let testValue = AppVersionHandler.test
}
extension DependencyValues {
var appVersionHandler: AppVersionHandler {
get { self[AppVersionHandlerKey.self] }
set { self[AppVersionHandlerKey.self] = newValue }
}
}

View File

@ -12,6 +12,7 @@ typealias HomeViewStore = ViewStore<HomeState, HomeAction>
typealias AnyBalanceBreakdownReducer = AnyReducer<BalanceBreakdownReducer.State, BalanceBreakdownReducer.Action, HomeEnvironment> typealias AnyBalanceBreakdownReducer = AnyReducer<BalanceBreakdownReducer.State, BalanceBreakdownReducer.Action, HomeEnvironment>
typealias AnyScanReducer = AnyReducer<ScanReducer.State, ScanReducer.Action, HomeEnvironment> typealias AnyScanReducer = AnyReducer<ScanReducer.State, ScanReducer.Action, HomeEnvironment>
typealias AnyWalletEventsFlowReducer = AnyReducer<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action, HomeEnvironment> typealias AnyWalletEventsFlowReducer = AnyReducer<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action, HomeEnvironment>
typealias AnyProfileReducer = AnyReducer<ProfileReducer.State, ProfileReducer.Action, HomeEnvironment>
// MARK: State // MARK: State
@ -29,7 +30,7 @@ struct HomeState: Equatable {
var balanceBreakdown: BalanceBreakdownReducer.State var balanceBreakdown: BalanceBreakdownReducer.State
var drawerOverlay: DrawerOverlay var drawerOverlay: DrawerOverlay
var profileState: ProfileState var profileState: ProfileReducer.State
var requestState: RequestReducer.State var requestState: RequestReducer.State
var requiredTransactionConfirmations = 0 var requiredTransactionConfirmations = 0
var scanState: ScanReducer.State var scanState: ScanReducer.State
@ -66,7 +67,7 @@ enum HomeAction: Equatable {
case debugMenuStartup case debugMenuStartup
case onAppear case onAppear
case onDisappear case onDisappear
case profile(ProfileAction) case profile(ProfileReducer.Action)
case request(RequestReducer.Action) case request(RequestReducer.Action)
case send(SendFlowAction) case send(SendFlowAction)
case scan(ScanReducer.Action) case scan(ScanReducer.Action)
@ -269,18 +270,13 @@ extension HomeReducer {
environment: { $0 } environment: { $0 }
) )
private static let profileReducer: HomeReducer = ProfileReducer.default.pullback( private static let profileReducer: HomeReducer = AnyProfileReducer { _ in
ProfileReducer()
}
.pullback(
state: \HomeState.profileState, state: \HomeState.profileState,
action: /HomeAction.profile, action: /HomeAction.profile,
environment: { environment in environment: { $0 }
ProfileEnvironment(
appVersionHandler: .live,
mnemonic: environment.mnemonic,
SDKSynchronizer: environment.SDKSynchronizer,
walletStorage: environment.walletStorage,
zcashSDKEnvironment: environment.zcashSDKEnvironment
)
}
) )
private static let balanceBreakdownReducer: HomeReducer = AnyBalanceBreakdownReducer { _ in private static let balanceBreakdownReducer: HomeReducer = AnyBalanceBreakdownReducer { _ in

View File

@ -1,16 +1,11 @@
import ComposableArchitecture import ComposableArchitecture
import SwiftUI import SwiftUI
typealias ProfileReducer = Reducer<ProfileState, ProfileAction, ProfileEnvironment> typealias ProfileStore = Store<ProfileReducer.State, ProfileReducer.Action>
typealias ProfileStore = Store<ProfileState, ProfileAction> typealias ProfileViewStore = ViewStore<ProfileReducer.State, ProfileReducer.Action>
typealias ProfileViewStore = ViewStore<ProfileState, ProfileAction>
typealias AnySettingsReducer = AnyReducer<SettingsReducer.State, SettingsReducer.Action, ProfileEnvironment> struct ProfileReducer: ReducerProtocol {
typealias AnyAddressDetailsReducer = AnyReducer<AddressDetailsReducer.State, AddressDetailsReducer.Action, ProfileEnvironment> struct State: Equatable {
// MARK: - State
struct ProfileState: Equatable {
enum Route { enum Route {
case addressDetails case addressDetails
case settings case settings
@ -23,64 +18,36 @@ struct ProfileState: Equatable {
var route: Route? var route: Route?
var sdkVersion = "" var sdkVersion = ""
var settingsState: SettingsReducer.State var settingsState: SettingsReducer.State
} }
// MARK: - Action enum Action: Equatable {
enum ProfileAction: Equatable {
case addressDetails(AddressDetailsReducer.Action) case addressDetails(AddressDetailsReducer.Action)
case back case back
case onAppear case onAppear
case settings(SettingsReducer.Action) case settings(SettingsReducer.Action)
case updateRoute(ProfileState.Route?) case updateRoute(ProfileReducer.State.Route?)
} }
// MARK: - Environment @Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
@Dependency(\.appVersionHandler) var appVersionHandler
struct ProfileEnvironment { var body: some ReducerProtocol<State, Action> {
let appVersionHandler: AppVersionHandler Scope(state: \.addressDetailsState, action: /Action.addressDetails) {
let mnemonic: WrappedMnemonic AddressDetailsReducer()
let SDKSynchronizer: WrappedSDKSynchronizer }
let walletStorage: WrappedWalletStorage
let zcashSDKEnvironment: ZCashSDKEnvironment
}
extension ProfileEnvironment { Scope(state: \.settingsState, action: /Action.settings) {
static let live = ProfileEnvironment( SettingsReducer()
appVersionHandler: .live, }
mnemonic: .live,
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
walletStorage: .live(),
zcashSDKEnvironment: .mainnet
)
static let mock = ProfileEnvironment( Reduce { state, action in
appVersionHandler: .test,
mnemonic: .mock,
SDKSynchronizer: MockWrappedSDKSynchronizer(),
walletStorage: .live(),
zcashSDKEnvironment: .testnet
)
}
// MARK: - Reducer
extension ProfileReducer {
static let `default` = ProfileReducer.combine(
[
profileReducer,
addressDetailsReducer,
settingsReducer
]
)
private static let profileReducer = ProfileReducer { state, action, environment in
switch action { switch action {
case .onAppear: case .onAppear:
state.address = environment.SDKSynchronizer.getShieldedAddress() ?? "" state.address = sdkSynchronizer.getShieldedAddress() ?? ""
state.appBuild = environment.appVersionHandler.appBuild() state.appBuild = appVersionHandler.appBuild()
state.appVersion = environment.appVersionHandler.appVersion() state.appVersion = appVersionHandler.appVersion()
state.sdkVersion = environment.zcashSDKEnvironment.sdkVersion state.sdkVersion = zcashSDKEnvironment.sdkVersion
return .none return .none
case .back: case .back:
@ -97,24 +64,7 @@ extension ProfileReducer {
return .none return .none
} }
} }
private static let addressDetailsReducer: ProfileReducer = AnyAddressDetailsReducer { _ in
AddressDetailsReducer()
} }
.pullback(
state: \ProfileState.addressDetailsState,
action: /ProfileAction.addressDetails,
environment: { $0 }
)
private static let settingsReducer: ProfileReducer = AnySettingsReducer { _ in
SettingsReducer()
}
.pullback(
state: \ProfileState.settingsState,
action: /ProfileAction.settings,
environment: { $0 }
)
} }
// MARK: - Store // MARK: - Store
@ -123,7 +73,7 @@ extension ProfileStore {
func settingsStore() -> SettingsStore { func settingsStore() -> SettingsStore {
self.scope( self.scope(
state: \.settingsState, state: \.settingsState,
action: ProfileAction.settings action: ProfileReducer.Action.settings
) )
} }
} }
@ -131,10 +81,10 @@ extension ProfileStore {
// MARK: - ViewStore // MARK: - ViewStore
extension ProfileViewStore { extension ProfileViewStore {
var routeBinding: Binding<ProfileState.Route?> { var routeBinding: Binding<ProfileReducer.State.Route?> {
self.binding( self.binding(
get: \.route, get: \.route,
send: ProfileAction.updateRoute send: ProfileReducer.Action.updateRoute
) )
} }
@ -153,9 +103,9 @@ extension ProfileViewStore {
} }
} }
// MARK: Placeholders // MARK: - Placeholders
extension ProfileState { extension ProfileReducer.State {
static var placeholder: Self { static var placeholder: Self {
.init( .init(
addressDetailsState: .placeholder, addressDetailsState: .placeholder,

View File

@ -116,8 +116,7 @@ struct ProfileView_Previews: PreviewProvider {
addressDetailsState: .placeholder, addressDetailsState: .placeholder,
settingsState: .placeholder settingsState: .placeholder
), ),
reducer: .default, reducer: ProfileReducer()
environment: .live
) )
) )
} }

View File

@ -15,14 +15,14 @@ struct SandboxReducer: ReducerProtocol {
case request case request
} }
var walletEventsState: WalletEventsFlowReducer.State var walletEventsState: WalletEventsFlowReducer.State
var profileState: ProfileState var profileState: ProfileReducer.State
var route: Route? var route: Route?
} }
enum Action: Equatable { enum Action: Equatable {
case updateRoute(SandboxReducer.State.Route?) case updateRoute(SandboxReducer.State.Route?)
case walletEvents(WalletEventsFlowReducer.Action) case walletEvents(WalletEventsFlowReducer.Action)
case profile(ProfileAction) case profile(ProfileReducer.Action)
case reset case reset
} }
@ -38,14 +38,10 @@ struct SandboxReducer: ReducerProtocol {
.map(SandboxReducer.Action.walletEvents) .map(SandboxReducer.Action.walletEvents)
case .profile: case .profile:
return ProfileReducer return Scope(state: \SandboxReducer.State.profileState, action: /SandboxReducer.Action.profile) {
.default ProfileReducer()
.pullback( }
state: \.profileState, .reduce(into: &state, action: action)
action: /SandboxReducer.Action.profile,
environment: { _ in ProfileEnvironment.live }
)
.run(&state, action, ())
case .reset: case .reset:
return .none return .none

View File

@ -11,18 +11,10 @@ import ComposableArchitecture
class ProfileTests: XCTestCase { class ProfileTests: XCTestCase {
func testSynchronizerStateChanged_AnyButSynced() throws { func testSynchronizerStateChanged_AnyButSynced() throws {
let testEnvironment = ProfileEnvironment(
appVersionHandler: .test,
mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
walletStorage: .throwing,
zcashSDKEnvironment: .testnet
)
let store = TestStore( let store = TestStore(
initialState: .placeholder, initialState: .placeholder,
reducer: ProfileReducer.default, reducer: ProfileReducer()
environment: testEnvironment .dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
) )
store.send(.onAppear) { state in store.send(.onAppear) { state in

View File

@ -12,18 +12,10 @@ import SwiftUI
class ProfileSnapshotTests: XCTestCase { class ProfileSnapshotTests: XCTestCase {
func testProfileSnapshot_sent() throws { func testProfileSnapshot_sent() throws {
let testEnvironment = ProfileEnvironment(
appVersionHandler: .test,
mnemonic: .mock,
SDKSynchronizer: TestWrappedSDKSynchronizer(),
walletStorage: .throwing,
zcashSDKEnvironment: .testnet
)
let store = Store( let store = Store(
initialState: .placeholder, initialState: .placeholder,
reducer: ProfileReducer.default, reducer: ProfileReducer()
environment: testEnvironment .dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
) )
ViewStore(store).send(.onAppear) ViewStore(store).send(.onAppear)