2

Looking at Apple documentation, I see that they are recommending removing Core Data initialization code out of AppDelegate. Their approach is below.

What I don't understand are the following

  1. The sentence below in the documentation. How is there a callback to app delegate? I don't see one in the code snippets below. Is it something they want us to add.

By initializing a separate controller object with a completion block, you have moved the Core Data stack out of the application delegate, but you still allow a callback to the application delegate so that the user interface can know when to begin requesting data.

  1. AppDelegate calls init of DataController and this in turn this calls initializeCoreData. But initializeCoreData sets up the persistent store co-ordinator in a background thread. Which means that if we transition to the application's first view and its view controller requests data from core-data, things are not yet set up. Won't this be an issue? Does this mean they want us to show a different launch screen & register for a callback that tells us that CoreData initialization is done before moving to actual first application view.

AppDelegate code in documentation

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [self setDataController:[[DataController alloc] init];
    // Basic User Interface initialization
    return YES;
}

DataController code in documentation

@interface MyDataController : NSObject

@property (strong) NSManagedObjectContext *managedObjectContext;

-(void)initializeCoreData;

@end

@implementation MyDataController

-(id)init {

    self = [super init];
    if (!self) return nil;

    [self initializeCoreData];

    return self;
}

- (void)initializeCoreData {

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];
    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    NSAssert(mom != nil, @"Error initializing Managed Object Model");

    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [moc setPersistentStoreCoordinator:psc];
    [self setManagedObjectContext:moc];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        NSError *error = nil;
        NSPersistentStoreCoordinator *psc = [[self managedObjectContext] persistentStoreCoordinator];
        NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
        NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
    });
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Smart Home
  • 801
  • 7
  • 26

2 Answers2

1

1)

In the Core Data programming guide you're looking at, the authors are taking Core Data out of the App Delegate (which tends to get cluttered up with a lot of extra code that developers really should split out into separate objects).

The rest of the documentation explains:

It is recommended that the Core Data stack be created within its own top-level controller object and that the application delegate initialize that controller object and hold a reference to it. This action will promote the consolidation of the Core Data code within its own controller and keep the application delegate relatively clean.

Also:

assign the adding of the persistent store (NSPersistentStore) to the persistent store coordinator (NSPersistentStoreCoordinator) to a background queue. That action can take an unknown amount of time, and performing it on the main queue can block the user interface, possibly causing the application to terminate.

Once the persistent store has been added to the persistent store coordinator, you can then call back onto the main queue and request that the user interface be completed and displayed to the user

So yes, CoreData might not necessarily be completely up by the time your first view controller is displayed, but if you have an observer looking for a NSNotification from that background queue, you can tell your UI when CoreData is ready to be relied upon.

2)

[DataController init] won't return a DataController object until initializeCoreData returns, so your UI will not display until after didFinishLaunchingWithOptions returns and you should already have a DataController object.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • I don't agree. The `initializeCoreData` has a `dispatch_async` at the end. So, it will return before this block of code executes. Hence, there is no guarantee that the block within the `disaptch_async` of `initializeCoreData` has completed when [DataController init] has returned. – Smart Home Sep 18 '16 at 18:22
  • You're right. You'll need to tell your UI that CoreData is ready to use. One way to do this is via NSNotifications, or another way is to have a "property" (e.g. a Bool?) that gets set to true so an key value observer can fire when the UI can rely upon CoreData. – Michael Dautermann Sep 19 '16 at 01:57
  • @SmartHome - the [iCloud-core-data discussion](https://developer.apple.com/library/content/documentation/DataManagement/Conceptual/UsingCoreDataWithiCloudPG/UsingSQLiteStoragewithiCloud/UsingSQLiteStoragewithiCloud.html#//apple_ref/doc/uid/TP40013491-CH3-SW1) talks about the notifications given when the temp store is replaced with the "real" store. I suspect they are suggesting code that makes it easier to switch an app with a local store to a cloud store. But oddly, they don't show *how* to handle PSCDidChange and how to redisplay the UI. The iCloud doc suggests you just `reset` the MOC. – stevesliva Sep 19 '16 at 14:13
0

The return YES from the method applicationDidFinishLaunchingWithOptions, is the key.

The initialisation of the Core Data stack is happening before the return of the method or rather before any view controller is being initialised. The Core data stack is being initialised as soon as the app is notified that it is indeed being launched. Its only after the whole initialisation process, the method returns YES. After which storyboard gets loaded along with its initial controller and then the initial controller's viewDidLoad gets loaded.

So in a way, a long way before the initial controller is even in the screen, the Core data stack is loaded.

Hope this could help you understand.

Saheb Roy
  • 5,899
  • 3
  • 23
  • 35
  • 1
    I don't agree. The `initializeCoreData` has a `dispatch_async` at the end. So, it will return before this block of code executes. Hence, there is no guarantee that the block within the `disaptch_async` of `initializeCoreData` has completed when [DataController init] has returned and hence there is no guarantee that this block has executed when `applicationDidFinishLaunchingWithOptions` has returned YES. – Smart Home Sep 18 '16 at 18:24