Last active
June 5, 2024 15:31
-
-
Save appfrosch/2ab3aba7d1fc6ad1071159962627ee5c to your computer and use it in GitHub Desktop.
TCA poc with Child-to-Parent (working) and Parent-to-Child (open question) communication
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 | |
@main | |
struct poc_tca_Child_Parent_Child_CommunicationApp: App { | |
var body: some Scene { | |
WindowGroup { | |
ParentView( | |
store: Store( | |
initialState: ParentFeature.State(), | |
reducer: { ParentFeature() } | |
) | |
) | |
} | |
} | |
} | |
import ComposableArchitecture | |
@Reducer | |
struct ParentFeature { | |
@ObservableState | |
struct State: Equatable { | |
@Presents var child: ChildCase.State? | |
} | |
enum Action: Equatable { | |
case child(PresentationAction<ChildCase.Action>) | |
case child1ButtonTapped | |
case child2ButtonTapped | |
case resetButtonTapped | |
case answerFromParent(String) | |
} | |
@Reducer(state: .equatable, action: .equatable) | |
enum ChildCase { | |
case child1(Child1Feature) | |
case child2(Child2Feature) | |
//Unfortunately, I was not able to hide away the setting of the value logic | |
//to `ChildCase`, as proposed in the slack chat. | |
// var value: String { | |
// get { | |
// switch self { | |
// case let .child1(state): | |
// state.value | |
// case let .child2(state): | |
// state.value | |
// } | |
// } | |
// set { | |
// switch self { | |
// case let .child1(state): | |
// state.value = newValue | |
// case let .child2(state): | |
// state.value = newValue | |
// } | |
// } | |
// } | |
// } | |
} | |
var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case let .child(.presented(.child1(.delegate(.setValue(sentValue))))): | |
return Self.processValue(sentValue) | |
case let .child(.presented(.child2(.delegate(.setValue(sentValue))))): | |
return Self.processValue(sentValue) | |
case .child: | |
return .none | |
case .child1ButtonTapped: | |
state.child = .child1(Child1Feature.State(value: "Init State Child 1")) | |
return .none | |
case .child2ButtonTapped: | |
state.child = .child2(Child2Feature.State(value: "Init State Child 2")) | |
return .none | |
case .resetButtonTapped: | |
state.child = nil | |
return .none | |
case let .answerFromParent(sentValue): | |
print(sentValue) | |
//How can I scope down to the child here **to set the value from the parent**? | |
//https://pointfreecommunity.slack.com/archives/C04KQQ7NXHV/p1717596949744659 | |
//**Like this:** | |
// While the following works … | |
// if case .child1 = state.child { | |
// state.child?.child1?.value = sentValue | |
// } | |
// if case .child2 = state.child { | |
// state.child?.child2?.value = sentValue | |
// } | |
// … it is better to work with a `switch` here to let the compiler help | |
//you on all possible states! | |
switch state.child { | |
case .none: | |
return .none | |
case .some(.child1): | |
state.child?.child1?.value = sentValue | |
case .some(.child2): | |
state.child?.child2?.value = sentValue | |
} | |
return .none | |
} | |
} | |
.ifLet(\.$child, action: \.child) | |
} | |
} | |
extension ParentFeature { | |
static func processValue(_ sentValue: String) -> Effect<Action> { | |
let value = sentValue + " " + "(processed by parent)" | |
return .run { send in | |
await send(.answerFromParent(value)) | |
} | |
} | |
} | |
struct ParentView: View { | |
let store: StoreOf<ParentFeature> | |
var body: some View { | |
VStack { | |
if let store = self.store.scope(state: \.child?.child1, action: \.child.child1.presented) { | |
Child1View(store: store) | |
} else if let store = self.store.scope( | |
state: \.child?.child2, | |
action: \.child.child2.presented | |
) { | |
Child2View(store: store) | |
} else { | |
VStack { | |
Spacer() | |
Text("No Child selected …") | |
Spacer() | |
} | |
} | |
} | |
HStack { | |
Button("Child 1") { | |
store.send(.child1ButtonTapped) | |
} | |
Spacer() | |
Button("Reset") { | |
store.send(.resetButtonTapped) | |
} | |
Spacer() | |
Button("Child 2") { | |
store.send(.child2ButtonTapped) | |
} | |
} | |
.padding(.horizontal) | |
} | |
} | |
#Preview("Parent") { | |
ParentView( | |
store: Store( | |
initialState: ParentFeature.State(), | |
reducer: { ParentFeature() } | |
) | |
) | |
} | |
@Reducer | |
struct Child1Feature { | |
@ObservableState | |
struct State: Equatable { | |
var value: String | |
} | |
enum Action: Equatable { | |
case loadValueButtonTapped | |
case delegate(Delegate) | |
enum Delegate: Equatable { | |
case setValue(String) | |
} | |
} | |
var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .delegate: | |
return .none | |
case .loadValueButtonTapped: | |
return .none | |
} | |
} | |
} | |
} | |
struct Child1View: View { | |
let store: StoreOf<Child1Feature> | |
var body: some View { | |
VStack { | |
Spacer() | |
Text("Child 1") | |
.font(.title) | |
Text(store.value) | |
Button("Send string to parent …") { | |
store.send(.delegate(.setValue("Greetings from child 1"))) | |
} | |
Spacer() | |
} | |
} | |
} | |
@Reducer | |
struct Child2Feature { | |
@ObservableState | |
struct State: Equatable { | |
var value: String | |
} | |
enum Action: Equatable { | |
case loadValueButtonTapped | |
case delegate(Delegate) | |
enum Delegate: Equatable { | |
case setValue(String) | |
} | |
} | |
var body: some ReducerOf<Self> { | |
Reduce { state, action in | |
switch action { | |
case .delegate: | |
return .none | |
case .loadValueButtonTapped: | |
return .none | |
} | |
} | |
} | |
} | |
struct Child2View: View { | |
let store: StoreOf<Child2Feature> | |
var body: some View { | |
VStack { | |
Spacer() | |
Text("Child 2") | |
.font(.title) | |
Text(store.value) | |
Button("Send string to parent …") { | |
store.send(.delegate(.setValue("Greetings from child 2"))) | |
} | |
Spacer() | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Requirements:
Asked question on how to communicate back to the child in the TCA slack.
Simulator.Screen.Recording.-.iPhone.15.Plus.-.2024-06-05.at.17.28.44.mp4