Search code examples
iosswiftcore-dataswiftui

SwiftUI: Filter Core Data fetch by relationship (NSPredicate)


@Environment(\.managedObjectContext) private var viewContext

@FetchRequest(
    entity: ListStruct.entity(),
    sortDescriptors: [NSSortDescriptor(keyPath: \ListStruct.name, ascending: true)],
    predicate: NSPredicate(format: "parentfolder == %@", parentFolder),
    animation: .default)
private var lists: FetchedResults<ListStruct>

var parentFolder: FolderStruct

There are two entities: FolderStruct and ListStruct. I made a tree structure with two entities. The view that I am using this code receives "var parentFolder: FolderStruct" as a parameter.

The relationship between FolderStruct and ListStruct is to-many, which means a folderStruct can hold many ListStructs. FolderStruct has a relationship named "childlists" which is to-many, and ListStruct has a relationship named "parentfolder" which is to-one.

When fetching ListStructs, I want to filter ListStructs whose parentfolder relationship is the parameter parentFolder. However, the code above makes an error that says "Cannot use instance member 'parentFolder' within property initializer; property initializers run before 'self' is available". How can I fix this problem?


Solution

  • You can supply the param you want to use in the fetch to the View's init as follows:

    private var fetchRequest : FetchRequest<Item>
    private var items: FetchedResults<Item> {
        fetchRequest.wrappedValue
    }
    
    init(folder: Folder) {
        fetchRequest = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)],
        predicate: NSPredicate(format: "folder == %@", folder),
        animation: .default)
    }
    

    Just so you know, due to a bug in FetchRequest, the body func will be called every time this View is init even if nothing has changed, so you may wish to wrap it in another View that takes the folder as a let for a layer of protection.

    Another bug is if you "dynamically change the predicate or sort descriptors" like the documentation states, then when the View containing the @FetchRequest is re-init, those changes are lost.