How to use the new nsPredicate dynamic property of @FetchRequest property wrapper with object passed into View

Viewed 147

My first attempt was to set the property wrapper's nsPredicate dynamic property in .onAppear, but if the view gets reinitialized for any reason, the predicate set by .onAppear is lost. So I went back to using the init pattern.

Here is what I thought should work (but doesn't) and something that does work (however mysteriously):

struct ItemEditView : View {
    
    var item: Item
    
    @FetchRequest(fetchRequest: Attribute.fetchRequestAllInOrder(), animation: .default)
    var attributes: FetchedResults<Attribute>

    init(item: Item) {
        self.item = item
        
        // This is how I would have expected to set the dynamic property at View initialization, however
        // it crashes on this statement
        attributes.nsPredicate = NSPredicate(format: "item == %@", item)
        
        // Not sure why the below works and the above does not.
        // It seems to work as desired, however it receives this runtime warning:
        // "Context in environment is not connected to a persistent store coordinator"
        $attributes.projectedValue.wrappedValue.nsPredicate = NSPredicate(format: "item == %@", item)
    }
    
    var body: some View {
        List {
            ForEach(attributes) { attribute in
                Text("Name:\(attribute.name) Order:\(attribute.order)")
            }
        }
    }
}

So, why does the first assignment to nsPredicate crash? And after commenting out that first one, why does the second one work? Is the warning message a real issue? Is there a better way to do this? It seems like there should be a simple way to do this using the new dynamic properties.

1 Answers

It turns out that (re)setting the nsPredicate property of the @FetchRequest in onAppear is really the way to go. However, to make this work, you must make sure that your View's init() method does not get called again after onAppear is called. There are several valuable hints on how to accomplish this in the Demystify SwiftUI session from this year's WWDC (WWDC21-10022).

Related