2

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.

Chuck H
  • 7,434
  • 4
  • 31
  • 34
  • Why are you fetching the `item`? just wrap the passed value `@ObservedObject var item: Item` – lorem ipsum Jul 19 '21 at 23:02
  • What is the error message when it crashes? – Joakim Danielson Jul 20 '21 at 10:23
  • The crash is: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) – Chuck H Jul 21 '21 at 00:18
  • Sorry, too many Items... Actually, I'm not fetching the Item. I'm fetching all of the Attribute's that are related to the Item passed into the View. The Attribute entity has a relationship to the Item entity named "item". – Chuck H Jul 21 '21 at 00:24
  • Don’t you have a inverse relationship from Item to Attribute because then you don’t need a fetch request. Your list would be something like `ForEach(item.attributes) { attribute in` assuming the name of the inverse relationship is `attributes` – Joakim Danielson Jul 22 '21 at 11:41
  • Yes, in the simplest case, the inverse relationship could be used instead. However, when you add sorting and more complex filtering, it can be more straight forward to use a fetch request. I was hoping that the new dynamic properties would make it easier. – Chuck H Jul 23 '21 at 00:28
  • You can always create and/or set the predicate in onAppear – Joakim Danielson Jul 23 '21 at 16:52
  • Setting the predicate in onAppear was the first thing that I tried. However, if something causes the View to be reinitialized, onAppear is not called again and that new predicate is lost. – Chuck H Jul 23 '21 at 18:52
  • Does this answer your question? [How to use a @FetchRequest with the new searchable modifier in SwiftUI?](https://stackoverflow.com/questions/68530633/how-to-use-a-fetchrequest-with-the-new-searchable-modifier-in-swiftui) – lorem ipsum Aug 07 '21 at 18:37

1 Answers1

1

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).

Chuck H
  • 7,434
  • 4
  • 31
  • 34
  • Bring CoreData concurrency addresses this exact thing with a custom Binding https://stackoverflow.com/questions/68530633/how-to-use-a-fetchrequest-with-the-new-searchable-modifier-in-swiftui/68549693#68549693 – lorem ipsum Aug 07 '21 at 18:38