I've been creating from 0 an app I already had with UIKit + RxSwift using SwiftUI and Composable Architecture and I've found an issue I don't know how to solve.
I've 3 screens in the following order:
Home drawing a list of Orders [Order]. This orders are an scope of AppState and AppActions
Order detail using Order model and OrderActions. For this part I'm using the ForEachStore method in Orders list (home). So Order detail uses an scope of Orders.
Create order. Inside order detail I've a create button that opens a modal. It doesn't make sense to use a scope over Order detail state and actions.
How can I handle this kind of cases. What if after creation I have to navigate to another detail with the created Order?
To add a bit more to the puzzle I want to add CoreData to store all the data so:
Reading orders from CoreData for Home Orders list
Use scoped state for order detail
Insert in CoreData when creating a new Order: If this Action is part of CreateOrderAction is also has to be part of OrderDetailAction and OrdersAction?
Here's a code example:
struct OrdersView: View {
let store: Store<OrdersState, OrdersAction>
var body: some View {
// ... I'm using ForEachStore and Navigation view but just to keep the example easier I'll skip that
OrderView(
store: self.store.scope(state: \.order, action: OrdersAction.order)
)
// ...
}
}
struct OrderView<ModalContent: View>: View {
let store: Store<Order, OrderAction>
var body: some View {
// ...
.sheet(
// ...
) { _ in
CreateView(
// What scope should I use here. Create view is not related to Detail
// It may be related to Orders or just to App
store: self.store.scope(state: \.?, action: ?)
)
}
}
}
```
I have a very similar setup, including the use of Core Data.
I won't be able to provide a very detailed answer at the time being, but for the creation view part, I have the equivalent of a var new: CreateOrderState? optional property on the state of the store presenting the sheet. In my case, the sheet is presented from the parent list view, not the detail, which makes more sense for my use case: new items are created from the list, not from the detail.
For Core Data, I made some wrappers on my CoreDataManager class to produce the desired effects using Effect.result, Effect.fireAndForget, etc. depending on the use case. The important detail is that the Core Data manager is part of the Environment, and can be passed down to the components that need it. I also introduced value types to represent entities that don't need to be edited, but that's more of a personal preference.
I will try to follow that approach for Core Data, thanks!
For the architecture part it's just an example. The real question is how to handle this kind of deep chains of views. I can think of examples where navigation is like: list -> search -> detail -> list -> detail -> list -> detail... Does that mean the last bit of info is part of any of the app states? If there's a flow that will take me from the first list to Item x, that item x is part of StateA, B, C, D....?
That's a decision you can take depending on what your app is doing, where it's getting the data from, etc.
For instance you can use optional states that get only initialized when needed. In your example, it could look like:
list -> search -> detail A? -> list -> detail B? -> list -> detail C?.
In other words, detail A is lazily initialized/fetched when the user taps the corresponding row. You can of course decide if you want it to stick around or get removed when the user goes back to the search. The samples in the repository have many examples of lazy navigation that you can refer to.
Technically yes. But using optional states, you can free up resources when they are not needed. It takes some getting used to initially, but it becomes almost second nature after that.
I think this optional states are what I was looking for. I still don't like that all my states are coupled but lazy loading state makes it better. Thanks!!!