Hi everyone!
In the company I work, we use a Redux/ELM inspired architecture since 3/4 years now. However, we are hitting some modularization issues and we are starting to explore TCA as the potential next architecture to use. I watched all the videos, but there is one thing I cannot figure out and I'm not sure whether it is a limitation of TCA, something I don't understand how to do or even a wrong approach to the problem.
We have many applications, and we share reusable libraries across our apps. Each library has its own state (with private and public information) and functionalities.
One thing we really like is to have a single state for all the information (both application ones and libraries ons) so we can store it and, with the proper abstraction, information can be shared even across libraries.
The problem I don't know how to solve is how to make so that libraries can update their internal state (through an action + reducer) but avoid that the app can mistakenly use the action that updates the internal state.
To make an example (obliviously super simplified), let's say we have the following state in the library
public struct LibState {
// the products fetched using StoreKit
// (note: in a real world example, we would not persist SKProduct instances, but
// rather we would use our own struct
public var products: [SKProduct]
// when the lib "starts" the products are fetched automatically.
// this bool keeps track of whether the fetching flow is ongoing
internal var isFetchingProducts: Bool
}
the lib may also have an action's enum like the following
public enum LibAction {
case toggleIsFetching
case didReceiveProducts([SKProduct])
}
and finally a reducer that changes the state and that will be composed in the app's reducer
// library
enum Lib {
static let reducer = Reducer<LibState, LibAction, LibEnvironment> { ... }
}
// app
let appReducer = Reducer<AppState, AppAction, AppEnvironment> { ... }
.combine(
Lib.reducer.pullback(\.libState, /AppAction.lib, LibEnvironment())
)
The problem is that the app can both see and use both LibAction.toggleIsFetching
and LibAction.didReceiveProducts
. While being able to reduce over LibAction.didReceiveProducts
could be a good thing (e.g., you could do something when products are updated), LibAction.toggleIsFetching
is a problem because it should be an internal detail of the lib. Also, in both cases the app should not be able to send those actions.
We could also make so that we don't use the TCA mechanism at all (or we use it with a separated, internal store, hidden from the app) but I really like having a single, coherent store where information is stored and shared (also to avoid issues that some web libraries, such as Flux, had in the past).