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.

Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135

2 Answers2

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");
}
Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135
  • 1
    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]`. – Tom Harrington Jul 15 '16 at 03:52
  • Hey tom, I must be doing something wrong, because the compiler is forcing me to downcast. I'll share an image. – Dan Beaulieu Jul 15 '16 at 03:53
  • 1
    Not sure. [This is what I did](https://gist.github.com/atomicbird/efbcb04eef24f074ad32dc6f9318965f) and the result is an array of type `[Event]?`, where I can look up attributes by name. – Tom Harrington Jul 15 '16 at 03:59
  • 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... – Dan Beaulieu Jul 15 '16 at 04:05
  • Well spotted. It's interesting that the code worked without that. – Tom Harrington Jul 15 '16 at 04:06
  • Doesn't work, says it wants cast from [NSFetchRequestResult] to [MyEntity] – pronebird Nov 17 '16 at 01:18
  • @DanBeaulieu figure out what? Your answer is incorrect and does not compile. I don't use codegen for CoreData either. – pronebird Nov 17 '16 at 11:04
  • @DanBeaulieu thanks Dan, that works! I defined my fetchRequest as `+ (NSFetchRequest *)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. – pronebird Nov 17 '16 at 15:03
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")
    }
}