Search code examples
core-dataswiftuiios13xcode11

Input a dynamic value into @FetchRequest, to fetch a single entity from core data in SwiftUI


I saw same type of error but with different kind of code here, so I think it's better to ask a new question on this context. I have attempted to "find a specific entity" from core data by trying to pass a string variable (which use as a key to find that entity) called title into @FetchRequest. This is part of the code I have used

struct AccountMainPage: View {
    //*** User input ***
    var title: String

    //*** Core data enviroment initialisation ***
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(fetchRequest: Accounts.getSpecificAccounts(findTitle: title)) var fetchedAccount: FetchedResults<Accounts>


    var body: some View {
    //SOME CODE HERE
    }
}

The public class Accounts has the extension:

extension Accounts {
    static func getSpecificAccounts(findTitle: String) -> NSFetchRequest<Accounts> {
    let request: NSFetchRequest<Accounts> = Accounts.fetchRequest() as! NSFetchRequest<Accounts>

    let findDescriptor = NSPredicate(format: "title == %@",findTitle)

    request.predicate = findDescriptor

    return request
    }
}

However, the line with @FetchRequest(fetchRequest: Accounts.getSpecificAccounts(findTitle: title)) var fetchedAccount: FetchedResults<Accounts> has a syntax error:

Cannot use instance member 'title' within property initializer; property initializers run before 'self' is available

Is there something wrong with my code?


Solution

  • @FetchRequest is dynamic property which is initialised, as any other property, before your AccountMainPage init is called, so self is not available yet, that is why you cannot use title property which is a member of self, and that is about what compiler error tells.

    So here is a possible solution: we initialise fetch request property with stub request and then in init, which is called later, reinitialise it with real fetch request.

    Here is an approach demo (all unrelated things cut):

    struct ContentView: View {
        var title: String
    
        @Environment(\.managedObjectContext) var managedObjectContext
        @FetchRequest(fetchRequest: Accounts.fetchRequest()) var fetchedAccount: FetchedResults<Accounts>
    
        init(title: String) {
            self.title = title
            _fetchedAccount = FetchRequest<Accounts>(fetchRequest: Accounts.getSpecificAccounts(findTitle: title))
        }
    
    ...