Skip to content

Instantly share code, notes, and snippets.

@GreatApe
Last active February 6, 2023 21:29
Show Gist options
  • Save GreatApe/b2cc42d05471ac50cf85089b583d1551 to your computer and use it in GitHub Desktop.
Save GreatApe/b2cc42d05471ac50cf85089b583d1551 to your computer and use it in GitHub Desktop.
import SwiftUI
import Dependencies
import Combine
import ComposableArchitecture
struct ContentView: View {
private let myStore: StoreOf<MyReducer> = Store(initialState: .init(number: 1), reducer: MyReducer())
private let otherStore: StoreOf<OtherReducer> = Store(initialState: .init(number: 2), reducer: OtherReducer())
var body: some View {
VStack {
MyView(store: myStore)
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
OtherView(store: otherStore)
}
.padding()
}
}
struct MyView: View {
let store: StoreOf<MyReducer>
var body: some View {
WithViewStore(store, observe: \.number) { viewStore in
Button("MyView: \(viewStore.state)") {
viewStore.send(.update)
}
.onAppear {
viewStore.send(.start)
}
}
}
}
struct OtherView: View {
let store: StoreOf<OtherReducer>
var body: some View {
WithViewStore(store, observe: \.number) { viewStore in
Button("OtherView: \(viewStore.state)") {
viewStore.send(.update)
}
.onAppear {
viewStore.send(.start)
}
}
}
}
extension EffectTask where Failure == Never {
static func observe<S: AsyncSequence>(_ sequence: S, action: @escaping (S.Element) -> Action) -> Self {
run { send in
for try await value in sequence {
await send(action(value))
}
}
}
}
struct MyReducer: ReducerProtocol {
@Dependency(\.player) var player
struct State {
var number: Int
}
enum Action {
case start
case update
case numberChanged(Int)
}
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .start:
return .observe(player.numbers, action: Action.numberChanged)
case .update:
player.update()
case .numberChanged(let number):
state.number = number
}
return .none
}
}
struct OtherReducer: ReducerProtocol {
@Dependency(\.player) var player
struct State {
var number: Int
}
enum Action {
case start
case update
case numberChanged(Int)
}
func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
switch action {
case .start:
return .observe(player.numbers, action: Action.numberChanged)
case .update:
player.update()
case .numberChanged(let number):
state.number = number
}
return .none
}
}
struct PlayerKey: DependencyKey {
static var liveValue: Player { .init() }
}
extension DependencyValues {
var player: Player {
get { self[PlayerKey.self] }
set { self[PlayerKey.self] = newValue }
}
}
struct Player {
var numbers: AsyncPublisher<PassthroughSubject<Int, Never>> { numberSubject.values }
private let numberSubject = PassthroughSubject<Int, Never>()
func update() {
let newNumber = Int.random(in: 0...10)
numberSubject.send(newNumber)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment