Skip to content

Instantly share code, notes, and snippets.

@theisegeberg
Created January 14, 2024 18:27
Show Gist options
  • Save theisegeberg/53abff6e98bcb325d9b616981b2f6ff2 to your computer and use it in GitHub Desktop.
Save theisegeberg/53abff6e98bcb325d9b616981b2f6ff2 to your computer and use it in GitHub Desktop.
Composable Architecture shared state
import Foundation
import SwiftUI
import ComposableArchitecture
@Reducer
struct Denmark {
@Reducer
struct Destination {
@ObservableState
enum State {
case copenhagen(Copenhagen.State)
case odense(Odense.State)
}
enum Action {
case copenhagen(Copenhagen.Action)
case odense(Odense.Action)
}
var body: some ReducerOf<Self> {
Scope(state: \.copenhagen, action: \.copenhagen) {
Copenhagen()
}
Scope(state: \.odense, action: \.odense) {
Odense()
}
}
}
@ObservableState
struct State {
var destinationPresentation:PresentationState<Destination.State> {
get {
guard let destination else {
return .init(wrappedValue: nil)
}
let destinationState:Destination.State
switch destination {
case .copenhagen(var state):
state.count = self.count
destinationState = .copenhagen(state)
case .odense(var state):
state.count = self.count
destinationState = .odense(state)
}
return .init(wrappedValue: destinationState)
}
set {
guard let presentedState = newValue.wrappedValue else {
self.destination = nil
return
}
switch presentedState {
case .copenhagen(let state):
self.count = state.count
case .odense(let state):
self.count = state.count
self.odenseLocalCount = state.localCount
}
self.destination = presentedState
}
}
var destination:Destination.State?
var count:Int
var odenseLocalCount:Int = 0
var inlineOdense:Odense.State {
get {
.init(elevation: 0.5, count: self.count, localCount: odenseLocalCount)
}
set {
self.count = newValue.count
self.odenseLocalCount = newValue.localCount
}
}
}
enum Action {
case destination(PresentationAction<Destination.Action>)
case copenhagenTapped
case odenseTapped
case increment
case decrement
case inlineOdense(Odense.Action)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .copenhagenTapped:
state.destination = .copenhagen(.init(title: UUID().uuidString, count: 10))
case .odenseTapped:
state.destination = .odense(state.inlineOdense)
case .increment:
state.count += 1
case .decrement:
state.count -= 1
case .inlineOdense:
break
case .destination:
break
}
return .none
}.ifLet(\.destinationPresentation, action: \.destination) {
Destination()
}
Scope(state: \.inlineOdense, action: \.inlineOdense) {
Odense()
}
}
}
struct DenmarkView:View {
@State var store:StoreOf<Denmark>
var body: some View {
NavigationStack {
VStack {
Text("Denmark")
Text("\(store.count)")
Button("Increment") {
store.send(.increment)
}
Button("Decrement") {
store.send(.decrement)
}
Button("Odense") {
store.send(.odenseTapped)
}
.navigationDestination(item: $store.scope(state: \.destination?.odense, action: \.destination.odense)) { store in
OdenseView(store: store)
}
Button("Copenhagen") {
store.send(.copenhagenTapped)
}
.navigationDestination(item: $store.scope(state: \.destination?.copenhagen, action: \.destination.copenhagen)) { store in
CopenhagenView(store: store)
}
GroupBox(label: Text("Inline Odense"), content: {
OdenseView(store: store.scope(state: \.inlineOdense, action: \.inlineOdense))
.frame(width: 200, height: 200, alignment: .center
)
}).padding()
}
}
}
}
@Reducer
struct Odense {
@ObservableState
struct State {
var elevation:Double
var count:Int
var localCount:Int
}
enum Action {
case increment
case decrement
case localIncrement
case localDecrement
case dismiss
}
@Dependency(\.dismiss) var dismiss
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .increment:
state.count += 1
case .decrement:
state.count -= 1
case .localIncrement:
state.localCount += 1
case .localDecrement:
state.localCount -= 1
case .dismiss:
return .run { _ in await self.dismiss() }
}
return .none
}
}
}
struct OdenseView:View {
@State var store:StoreOf<Odense>
var body: some View {
VStack {
Text("Odense \(store.elevation)")
Text("Global: \(store.count)")
Button("Increment") {
store.send(.increment)
}
Button("Decrement") {
store.send(.decrement)
}
Text("Local: \(store.localCount)")
Button("Increment") {
store.send(.localIncrement)
}
Button("Decrement") {
store.send(.localDecrement)
}
Button("Dismiss") {
store.send(.dismiss)
}
}
}
}
@Reducer
struct Copenhagen {
@ObservableState
struct State {
var title:String
var count:Int
}
enum Action {
case increment
case decrement
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .increment:
state.count += 1
case .decrement:
state.count -= 1
}
return .none
}
}
struct CopenhagenView:View {
@State var store:StoreOf<Copenhagen>
var body: some View {
VStack {
Text("Copenhagen \(store.title)")
Text("\(store.count)")
Button("Increment") {
store.send(.increment)
}
Button("Decrement") {
store.send(.decrement)
}
}
}
}
#Preview(body: {
DenmarkView(store: .init(initialState: Denmark.State.init(count: 1), reducer: {
Denmark()
}))
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment