Reputation: 55
I use the same fetch request in multiple top level views under ContentView. I mean the same entity / predicate etc.
Previously I tried doing the request once in ContentView and passing it as an array through several layers of views. However, this stopped changes, deletes etc, being propagated across other views.
I just wondered if there is another approach which would work ?
I'm thinking that some kind of singleton approach might work, but I'm worried about the performance implications, however this might outweigh having to run the request several times.
Also, I wondered about passing a request results rather than array? However having to pass this around seems ugly.
Upvotes: 0
Views: 557
Reputation: 19884
You can use the Environment
to pass your models to children without having to passing an array through several layers of views. You start by creating your own EnvirnomentKey
public struct ModelEnvironmentKey: EnvironmentKey {
public static var defaultValue: [Model] = []
}
public extension EnvironmentValues {
var models: [Model] {
get { self[ModelEnvironmentKey] }
set { self[ModelEnvironmentKey] = newValue }
}
}
public extension View {
func setModels(_ models: [Model]) -> some View {
environment(\.models, models)
}
}
I like using ViewModifiers
to setup my environment, following the Single-Responsibility Principle:
struct ModelsLoaderViewModifier: ViewModifier {
@FetchRequest(entity: Model(), sortDescriptors: [])
var models: FetchedResults<Model>
func body(content: Content) -> some View {
content
.setModels(Array(models))
}
}
extension View {
func loadModels() -> some View {
modifier(ModelsLoaderViewModifier)
}
}
I then would add this modifier pretty high on the view hierarchy.
@main
struct BudgetApp: App {
@ObservedObject var persistenceManager = PersistenceManager(usage: .main)
let startup = Startup()
var body: some Scene {
WindowGroup {
ContentView()
.loadModels()
}
}
}
Now ContentView
can read from the environment:
struct ContentView: View {
@Environment(\.models) var models
var body: some View {
List {
ForEach(models) { model in
Text(model.name)
}
}
}
}
Upvotes: 2