How do I make a fetch request using NSManagedObject's new fetchRequest function?
Asked Answered
D

2

30

In iOS 10 the CoreData team added a new "fetchRequest" method to NSManagedObject. It looks like this:

public class func fetchRequest() -> NSFetchRequest<NSFetchRequestResult>

Which, from what I understand, allows us to replace this:

let request = NSFetchRequest<MyEntity>(entityName: "MyEntity")

with this:

let request = MyEntity.fetchRequest()

However, when I try to make a simple request like this:

let request = MyEntity.fetchRequest()
do {
    results = try request.execute()
} catch let error {
    print("failed to fetch coffee object: \(error)")
}

I receive the following error:

Error Domain=NSCocoaErrorDomain Code=134060 "(null)" UserInfo={message=Cannot fetch without an NSManagedObjectContext in scope}

So, clearly the error is stating that I need to bring an NSManagedObjectContext into scope. I've been looking for examples but can seem to find a full example of how to perform a request using the new API features.

Question

Using the latest Core Data API features, how do I make a simple fetch request? The underlying question is how do I bring my NSmanagedObjectCotnext into scope.

I should note that I am able to successfully make a request using the traditional syntax.

Donahue answered 15/7, 2016 at 2:46 Comment(0)
D
40

Ok, I figured out two ways of doing this. The first approach, which is similar to my example code is just simply:

var moc: NSManagedObjectContext!
let request = MyEntity.fetchRequest()
var results : [MyEntity]

 do {
     results = try moc.fetch(request)
 } catch { // error stuff}

The second approach, which was shown in WWDC 2016 uses a function named "execute" on our fetch request object.

If you view the details on NSFetchRequest you'll see a comment that clearly states that the operation must be performed with in a block.

   // Executes the fetch request using the current managed object context. This method must be called from within a block submitted to a managed object context.
    @available(iOS 10.0, *)
    public func execute() throws -> [ResultType]

It's dawned on me that this was the way to perform the fetch:

var moc: NSManagedObjectContext!
let request = MyEntity.fetchRequest()
var results : [MyEntity]

moc.perform {
    self.results = try! request.execute()
}

For those who roll their own

If you are rolling your own code you may find that your code does not compile. Included in the auto-generated code is a new method called fetchRequest that the compiler uses to pass the type along. Here's what the code looks like.

@nonobjc public class func fetchRequest() -> NSFetchRequest<MyEntity> {
    return NSFetchRequest<MyEntity>(entityName: "MyEntity");
}
Donahue answered 15/7, 2016 at 3:36 Comment(8)
With the new generic declarations in Core Data, the as! downcasting you're doing is no longer necessary-- the results of the fetch should already have a type of [MyEntity].Saiva
Hey tom, I must be doing something wrong, because the compiler is forcing me to downcast. I'll share an image.Donahue
Not sure. This is what I did and the result is an array of type [Event]?, where I can look up attributes by name.Saiva
Hey @TomHarrington I figured it out, I deleted the nonobjc class func fetchRequest() from my generated code. Once I added it back in the errors went away. Thank you kindly for point this out...Donahue
Well spotted. It's interesting that the code worked without that.Saiva
Doesn't work, says it wants cast from [NSFetchRequestResult] to [MyEntity]Scorch
@DanBeaulieu figure out what? Your answer is incorrect and does not compile. I don't use codegen for CoreData either.Scorch
@DanBeaulieu thanks Dan, that works! I defined my fetchRequest as + (NSFetchRequest<MLCloudFileModel *> *)fetchRequest; and returned result from [super fetchRequest]. That was translated properly to Swift and I was able to call fetchRequest() and receive the right entity back.Scorch
C
10

For iOS 10 (Swift 3) Following way helped me:

var userRecordsArray: [UserProfile] = []

func getData() {

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserProfile")
    let managedContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    do {
        userRecordsArray = try managedContext.fetch(fetchRequest) as! [UserProfile]
    } catch {
        print("Fetching Failed")
    }
}
Celery answered 24/1, 2017 at 7:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.