0
votes

I'm suddenly beset by Core Data problems in the form of managedObjectContexts that are nil two-thirds of the time: the onset more or less coincides with upgrading to Xcode 8. The new NSPersistentContainer class seemed to offer an opportunity to solve my problems and get rid of some ugly code. Unfortunately, I cannot make it work.

In AppDelegate.h

@property (readonly, strong, nonatomic) NSPersistentContainer *persistentContainer;

In AppDelegate.m:

@synthesize persistentContainer = _persistentContainer;
- (NSPersistentContainer *)persistentContainer
{
    if (!_persistentContainer) {
        _persistentContainer = [NSPersistentContainer persistentContainerWithName:@"I_WILL_PERSIST"];
        __block BOOL storesWereLoaded = NO;
        [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *psd, NSError *error) {
            if (error) {
                NSLog(@"loadPersistentStoresWithCompletionHandler: persistent container could not load store %@. Error: %@",
                      psd, error.debugDescription);
            } else {
                storesWereLoaded = YES;
            }
        }];
        if (storesWereLoaded) {
            NSLog(@"Stores were loaded");
        } else {
            NSLog(@"Stores were not loaded");
        }
    }
    return _persistentContainer;
}

The app is a little test app, with an arrayController bound (in IB) to App Delegate.persistentContainer.viewContext. App Delegate is connected and not nil. The persistentContainer getter is called repeatedly but the NSLog's inside the block never fire and the console shows:

Cannot perform operation without a managed object context 2016-09-24 19:34:39.207 I_WILL_PERSIST[5588:180829] ( 0 CoreFoundation
0x00007fff8da994f2 __exceptionPreprocess + 178 1 libobjc.A.dylib
0x00007fff8ed6173c objc_exception_throw + 48 2 CoreFoundation
0x00007fff8db004bd +[NSException raise:format:] + 205 3 AppKit
0x00007fff85d411c4 -[_NSManagedProxy _managedObjectContext] + 66

Does the problem lie in my shaky block-programming skills? Is it a signing / permissions issue (the app is not Sandboxed, code signing automatic)? What's with Core Data all of a sudden?

====EDIT==== After upgrading to Mac OS 10.12 (Sierra), NSPersistentContainer fails with error:

2016-09-28 20:55:53.256588 osPersist[1936:41151] [error] error:
-addPersistentStoreWithType:SQLite configuration:(null) URL:file:///Users/user/Library/Application%20Support/osPersist/osPersist.sqlite options:{
    NSInferMappingModelAutomaticallyOption = 1;
    NSMigratePersistentStoresAutomaticallyOption = 1; } ... returned error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2} with userInfo dictionary {
    reason = "Failed to create file; code = 2"; } 2016-09-28 20:55:53.256747 osPersist[1936:41151] Unresolved error Error Domain=NSCocoaErrorDomain Code=512 "The file couldn’t be saved." UserInfo={reason=Failed to create file; code = 2}, {
    reason = "Failed to create file; code = 2"; }

Why the database creation fails is not clear: it happens both with and without code-signing. I've made a bug report and I've opened a support request.

1
I see a few bugs, but none that I would think would lead to that error. 1) you're testing storesWereLoaded immediately after invoking an asynchronous block. 2) If loading fails, you don't release the coordinator, which means it will exist, but be uninitialized, the next time through. This will cause the if statement to be skipped. - Avi
Thanks for your feedback, but what makes you think that loadPersistentStoresWithCompletionHandler: is an asynchronous block? Technically, you're correct that even when loadPersistentStoresWithCompletionHandler:, _persistentContainer still could have been initialized. As it happens, persistentContainerWithName: already returns nil so in practice loadPersistentStoresWithCompletionHandler: is called twice (through the bindings). I'm not even sure loadPersistentStoresWithCompletionHandler: is necessary -- the comments in the header suggest it is not. - Elise van Looij
I don't know of an Apple API that takes a completion block for a synchronous call. - Avi
Right. Anyway, I did some research and as far as I can make out, completion handlers are the equivalent of delegate calls or notifications. So, loadPersistentStores does whatever it does on a secondary thread and one it is done, the block is called and executes on the main thread. Except, in my case it is never called ... because persistentContainerWithName: returns nil and persistentContainerWithName: doesn't perform anything on a nil object. - Elise van Looij
hope you are using an iOS 10 simulator? i got same problem when i run on iOS 9 simulator with Xcode 8. - Shabeer Ali

1 Answers

2
votes

As soon as I upgraded to Sierra (MacOS 10.12) NSPersistentContainer started failing with errors. So, you can implement NSPersistentContainer in Xcode 8 running on MacOS 10.11 and your project will compile and run, but NSPersistentContainer won't do squat. I've filed a bug report and I hope that Apple will add preprocessor directives to the class, but they don't seem very interested.