SwiftUI dynamic Core Data @FetchRequest work-around
Introduction
Fetching from Core Data with dynamic variables is not straight forward.
Error: Cannot use instance member ‘variable’ within property initializer; property initializers run before ‘self’ is available
This blog post will provide a work-around to use dynamic data in requests.
Solution
The work-around is made in two steps:
- Move our data into its own view
- Set request’s underlying value in the init(…) method
Example code
ContentView.swift
-----------------
@State private var fruitColor: String = "red"
var body: some View {
FilteredFruitList(color: fruitColor)
}
FilteredFruitList.swift
-----------------------
@FetchRequest var fruits: FetchedResults<Fruit>
init(color: String) {
_fruits = FetchRequest<Fruit>(
sortDescriptors: [
NSSortDescriptor(keyPath: \Fruit.name, ascending: true)
],
predicate: NSPredicate(format: "color == %@", color)
)
}
var body: some View {
List(fruits) { fruit in
// …
}
}
NOTE: As of 2023-08-25, SwiftUI has a bug which prevents reliably modifying entities and have the view update consistently if entites are modified directly in the new view. If you need to modify your entities and have the view update, please see the next section.
Modifying entities, and updating the view
To reliably update entity data and update corresponding views, another workaround is required.
Say you want to toggle some attribute:
HStack {
Text(fruit.name!)
Text(fruit.eaten ? "eaten" : "uneaten")
Button("Toggle") {
fruit.eaten.toggle()
}
}
In order to properly reflect the changes made to the entity in the view, we need to wrap each entity in it’s own View with an @ObservedObject property:
struct FruitRow: View {
@ObservedObject var fruit: Fruit
var body: some View {
HStack {
Text(fruit.name!)
Text(fruit.eaten ? "eaten" : "uneaten")
Button("Toggle") {
fruit.eaten.toggle()
}
}
}
}
… and in our list provide the entity as such:
var body: some View {
List(fruits) { fruit in
FruitRow(fruit: fruit)
}
}
Closing thoughts
This is a verbose and cumbersome workaround for a problem that I wished had a simpler solution.
If you have the simple solution I am looking for, please find a way to message me – I would love to know.
References
- [?]
- Paul Hudson, Dynamically filtering @FetchRequest with SwiftUI, Hacking with Swift, Nov 29th 2021
- [?]
- Apple Inc., FetchRequest / wrappedValue, Apple Developer Documentation