Is it okay to have isolated local stores?

I am quite new to TCA and currently trying to use it in a new project. I really like the concept and can see a lot of advantages of this architecture but there are still a view things which are not clear yet.

To my understanding, all local state, actions and environment will be part of the parent state, actions and environment and in the end part of the global app state, etc. This makes sense to me for most use cases but sometimes it feels like it would be better to have isolated stores. So my question is: Is it okay to have local isolated stores?

For example lets say the user can start an interactive tutorial somewhere in the app. This Tutorial will be opened in a new view but has nothing else shared with the app. Can I instantiate a new store for that view without reusing anything from the parent store?
Or what if I want to have a SwiftUI component in my app which enables the user to select, take and modify a picture. Maybe I want to reuse this component even in non TCA applications so it exposes a single Binding with the picture to its parent. It would make sense to have a local store within that view then right? Or would this totally contradict the idea behind TCA?

Yeah that's definitely ok. We've never tried it personally, but I see no reason that it shouldn't work. One thing to keep in mind is if the isolated store ever needs to communicate to the main store you will have to resort to the standard patterns you would employ in a vanilla SwiftUI application, such as passing around closures to be invoked, posting notifications, etc., but it should all just work.

Maybe I want to reuse this component even in non TCA applications so it exposes a single Binding with the picture to its parent.

This makes sense too, and sounds like a good idea to not force every user of the component to use TCA. We've done something similar, though in the other direction. We have built vanilla SwiftUI components using just bindings, and then created helpers to layer on top to make it TCA friendly. For example, in the isowords code base we have a BottomMenu that follows this pattern, and even in the TCA library itself we have helpers to make alerts/action sheets in SwiftUI more TCA-friendly.

1 Like

Thanks Brandon!
Yes I think that makes sense. Also to keep in mind is where the store instance will be instantiated/live. Of course a isolated store only works it the state and everything else is truly isolated and should not be preserved when the view is not visible for example. And probably most of the time this won't be the case

We've done something similar, though in the other direction. We have built vanilla SwiftUI components using just bindings, and then created helpers to layer on top to make it TCA friendly

I will look into the example code you shared. We have a lot of small vanilla SwiftIUI components but didn't feel the need to add an additional TCA layer in-between.

(and thank you for all the time and work you are putting into TCA and this community)

I have been thinking of using independent stores for Core Data.

The idea is to initialise the store with an object id and let the reducer interact with Core Data through the environment, subscribe to updates, save, etc.

I thought it might make more sense when working with a fetch results controller than trying to scope the state of each cell, subscribed to store updates and use recursive reducers.

@mbrandonw @stephencelis what do you think of that approach?

Is there way the store's environment from outside the store.

It would be nice if I could create an independent store and just grab the environment instead of passing it around when creating viewControllers.

If there is no other option you could store environment objects as a singleton, then you don't need to pass it around. Or write your own @Environment property wrapper.

But you will quickly run into problems with testing when using singletons excesivly.

I use UserDefaults to store things. Technically UserDefaults, CoreData and other Storage is always some kind of "singleton"; you can access the same data anywhere in your code. Although you have to be careful with threading and simultaneously access. Thats why I only access UserDefaults in the RootStore and in use state models in other parts of the app. The root reducer then is responsible for "syncing" the data with userDefaults.