Parent-Child relationship in TCA (Send back data from Child to Parent)

I'm trying to make a project using SwiftUI and TCA

I saw A tour of Composable architecture series and CaseStudies too

But i can't get any clue for my situation

It's kind of a Todo app, but when the user taps the "Add" button, the Form view presented by the sheet and the user fills the form. At last, the user tap register button in Form View is dismissed and List should be reloaded.

How can implement this feature using TCA

I tried to using shared state but i won't work

Please give me some clue

I am making a similar CRUD app. In that app I have a ThingListState and ThingState as well as ThingListAction and ThingAction.

When creating my appReducer I use thingListReducer and thingReducer that was pulled back into thingListReducer with a selectionState property on ThingListState and case thingAction(ThingAction) on ThingListAction.

This way my thingListReducer can implement case .thingAction(.savedAThing) and catch the functionality of child reducer (thingReducer).

Same thing here. In my situation, I have a WelcomeView in which you can click either "Login" or "Register". Clicking the latter will open the RegisterView and after a successful login I want to show the "DashboardView". I have all the views setup separately, but it's not that clear to me yet how to glue everything together.

My initial thinking was to show the WelcomeView on app start and make the WelcomeView automatically forward to the DashboardView in case the user is signed in. The DashboardView should also be shown if the user signs in via LoginView or right after app start if the user is already signed in. But after reading @piupiu's answer, I feel like I'd need to put the entire "first screen" logic into the appReducer / AppState somehow on a top level.

As a beginner with TCA, I find the Case Studies not very clear/guided, their names are quite abstract and while there's a description for each, it's not clear to me at all which of the case studies applies in my situation:

  1. Do I need to "Pullback and Combine" something (and what exactly?) to get this working?
  2. Do I need to use "Optional State" for each possible initial screen?
  3. Do I need to use "Shared State" to pass login information back to a parent screen?

While I think I might figure it out by trying things out and understanding things better, I feel like the learning curve could be much flatter if there was a case study explicitly mentioning one or multiple ways to do authentication, which is one of the most common cases in app development.

The "TicTacToe" example is cheating here and isn't very helpful although it technically has a login flow, because it neither has a register screen, nor does it automatically sign in if user is signed in already on app start. And it also kind of cheats with navigation because it's using sheets, which is quite uncommon for signed in state. A much more realistic example should be added to the Case Studies IMHO.

I would hold a user variable in AppState that shows whether the user has authenticated. You then could pullback loginReducer to appReducer on a loginAction case path and pass the user after successful login/registration. The AppState would the be able to change the view hierarchy based on the values of user: LoginView if user == nil, ProfileView if user != nil and you could implement these using NavigationLink() with tag I think.

@eimantas Thanks for helping out. But what is a pullback even? Is there any good explanation of it with usage examples somewhere, freely accessible?

There are plenty of usage examples in case studies of TCA. Regarding the explanation - the TCA documentation site has a decent description in terms of library concepts.

I use pullback every time when I want some parent state know about any child state changes. Consider the following

struct ParentState: Equatable {
  var childGreeting: String
  var childState: ChildState {
    get { .init(greeting: childGreeting) }
    set { childGreeting = newValue.greeting }
}

struct ChildState: Equatable {
  var greeting: String
}

enum ParentAction {
  case parentDidIt
  case childDidIt(ChildAction)
}

enum ChildAction {
  case tellParentImHungry
}

Now when implementing the reducer for the whole system you'd do something like this:

let familyReducer = parentReducer
  .combine(
    with: childReducer.pullback(
      state: \.childState, // this is a childState that we'll [plug back into]/[pull out from] parentState,
      action: /ParentAction.childDidIt, // this is the parent state action case that will be triggered in reducer to handle child action
      environment: ()
    )

The pullback operator converts the reducer of ChildState and ChildAction to a reducer that operates on ParentState and ParentAction types.

1 Like

Thanks for the explanation. I still feel like this is the most unnatural and weirdest part of TCA I've come across so far. It feels counter-intuitive, maybe it's just the naming of the APIs, but I'll probably get used to it. Now that I see your code, I remember this was presented in the "Tour" videos, but their applicability to Login logic was not clear to me.

I'll probably share my solution here, both for others who come across the same problem to copy & paste and also for people to be able to correct any misunderstandings I might still have.

You can read more about where pullback comes from here: https://www.pointfree.co/blog/posts/22-some-news-about-contramap

1 Like

Thank you for the link, just read the post and the entire concept is still feeling counter-intuitive to me.

The example code there also explains very well why it's hard for me to grasp: They are not only trying to shorten code and trying to be as implicit as possible about what's going on there (all those $0 / $1), they also only provide abstract examples that are far away at least from my own development experience. Also, there's usage of even more context things like Ouverture to understand that code – where does the get function even come from in the last code sample, is it part of Overture?

I think this post only explains well to people who already knew what countermap was doing to understand why pullback seems to be a better name. For me, who never used countermap, nor knows what the Overture library is for and also didn't watch their episode on Equating, the examples in there are not helpful at all.

While I "understand" what I'm reading, it's hard for me to get a feeling for what pullback does and therefore decide when to use it. I'll probably just need to use it a few times to get that. Then, maybe, I can explain it in my own words. The sample code you provided me above is much more useful to me than that blog post, so thanks again for that.

1 Like

Interesting. Did you watch the video? I found the idea was much simpler to understand from watching the first few mins of the video than it was from reading got article.

The idea of contramap is discuss in great detail in the subscription videos that build up towards the Composable Architecture. This video is the one that contains the discussion around contramap. Episode #14: Contravariance

The essential idea started from the map function.

The map function says "If you give me a function from A to B, then I've return a function from Containers of A to Containers of B"

The contramap function flips that around a bit. It can seem very counter-intuitive to begin with but once you begin to see how it is used it slowly becomes clear.

It is probably something that you have used in the past without thinking to give it a name. A lot of functional programming has clicked for me in that way. I'll see some text about currying a function or higher order functions and think "so what? I'm never going to need this" and then suddenly I'll find that I'm actually using the same idea without calling it currying or higher order.

I do not have access to the video, so no, I did not watch it. Not sure if you’re question was targeted to me.

The video in the article doesn't require a subscription... Some news about contramap

1 Like

Ah, yes that video I just watched. But it only explains why the renaming makes sense. The concept itself is not explained there, really.

But I already figured out for my own usage what pullback does, so I’m good by now. I do still think there should be a better explanation of it though where the code is kept as detailed as possible without any shortcuts or additional concepts that are distracting and just confusing. The pullback is complicated enough in itself. Keypaths or that get function just make it harder to understand things, it‘s obvious that that article & video fall short in giving a TCA-beginner friendly explanation. I have the feeling it‘s explixitly targeted towards subscribed watchers of Pointfree.

That's a fair point. The series of videos that eventually get to creating pullback start from taking a deep dive into some fairly complex Computer Science ideas.

It started with look at how map works and then looking at it from a very abstract perspective of how map in general works. It's a difficult subject to summarise in a video never mind in a few words on a blog.

One thing I would say though is that this is not a TCA issue. The function contramap is not a TCA thing. It is a computer science thing that sits along subjects like covariance and contravariance.

1 Like
Terms of Service

Privacy Policy

Cookie Policy