Created
February 16, 2021 15:37
-
-
Save JustinGanzer/3555927318953b9676ea215bb1df8bda to your computer and use it in GitHub Desktop.
A playground file showing odd behavior when creating a derived Redux-like store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
//REDUX | |
typealias Reducer<State, Action> = (inout State, Action) -> AnyPublisher<Action, Never>? | |
class Store<State, Action>: ObservableObject { | |
@Published private(set) var state: State | |
private let reducer: Reducer<State, Action> | |
var cancellables: Set<AnyCancellable> = [] | |
init( | |
initialState: State, | |
reducer: @escaping Reducer<State, Action> | |
) { | |
self.state = initialState | |
self.reducer = reducer | |
} | |
deinit { | |
cancellables.removeAll() | |
} | |
func send(_ action: Action) { | |
guard let effect = reducer(&state, action) else { | |
return | |
} | |
effect | |
.receive(on: DispatchQueue.global(qos: .userInitiated)) | |
.sink(receiveValue: send) | |
.store(in: &cancellables) | |
} | |
func derived<DerivedState: Equatable, ExtractedAction>( | |
deriveState: @escaping (State) -> DerivedState, | |
embedAction: @escaping (ExtractedAction) -> Action | |
) -> Store<DerivedState, ExtractedAction> { | |
let store = Store<DerivedState, ExtractedAction>( | |
initialState: deriveState(state), | |
reducer: { (_, action) -> AnyPublisher<ExtractedAction, Never>? in | |
self.send(embedAction(action)) | |
return Empty().eraseToAnyPublisher() | |
} | |
) | |
$state | |
.map(deriveState) | |
//.removeDuplicates() | |
.receive(on: DispatchQueue.global(qos: .userInitiated)) | |
.assign(to: \.state, on: store) | |
.store(in: &store.cancellables) | |
return store | |
} | |
} | |
class AppStore: Store<AppStore.State, AppStore.Event> { | |
struct State { | |
var profile: ProfileState | |
var settings: SettingsState | |
} | |
enum ProfileState { | |
case profileA | |
case profileB | |
} | |
enum SettingsState { | |
case settingA | |
case settingB | |
} | |
enum Event { | |
case profile(event: ProfileEvent) | |
case settings(event: SettingsEvent) | |
} | |
enum ProfileEvent { | |
case eventA | |
case eventB | |
} | |
enum SettingsEvent { | |
case eventA | |
case eventB | |
} | |
static func reduce(state: inout State, event: Event) -> AnyPublisher<Event, Never>? { | |
switch event { | |
case .profile(let event): | |
switch event { | |
case .eventA: | |
state.profile = .profileA | |
case .eventB: | |
state.profile = .profileB | |
} | |
case .settings(let event): | |
switch event { | |
case .eventA: | |
state.settings = .settingA | |
case .eventB: | |
state.settings = .settingB | |
} | |
} | |
return nil | |
} | |
} | |
let appStore = AppStore(initialState: .init(profile: .profileA, settings: .settingA), reducer: AppStore.reduce) | |
let derivedStore = appStore.derived(deriveState: \.settings, embedAction: AppStore.Event.settings) | |
///Only to see construction | |
//let derivedStoreVerbose: Store<AppStore.SettingsState, AppStore.SettingsEvent> = appStore.derived { (state) -> AppStore.SettingsState in | |
// return state.settings | |
//} embedAction: { (action) -> AppStore.Event in | |
// return AppStore.Event.settings(event: action) | |
//} | |
let mySubscription = derivedStore.$state.sink { | |
print("DerivedStore State == \($0)") | |
} | |
print("TestModel Settings------------------------") | |
appStore.send(.settings(event: .eventA)) | |
appStore.send(.settings(event: .eventB)) | |
appStore.send(.settings(event: .eventA)) | |
sleep(3) | |
print("TestModel Profile-------------------------") | |
appStore.send(.profile(event: .eventA)) | |
appStore.send(.profile(event: .eventB)) | |
sleep(3) | |
print("DerivedModel Profile----------------------") | |
derivedStore.send(.eventA) | |
derivedStore.send(.eventB) | |
derivedStore.send(.eventA) | |
derivedStore.send(.eventB) | |
sleep(3) | |
print("Bug---------------------- DerivedStore filled with last setting event sent by AppStore") | |
print(derivedStore.state) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment