menu

Core Data

“It’s all about how core data works.”

Database

Some times you need to store large amount of data or query it in a sophisticated manner. And we want it to be object-oriented.

Core data framework is very powerful.

How does it work?

  • Create a visual mapping between database and objects;
  • Create and query for objects using object-oriented API;
  • Access the “columns in the database table” using @propertys on those objects.
  • Create a core data model, the visual map in xcode.

An Object Context

You need an NSManagedObjectContext.

It is the hub around which all Core Data activity turns.

How do I get one?

There are two ways:

  1. Create a UIMangedDocument and ask for its managedObjectContext (a @property).

  2. Click the “Use Core Data” button when you create a project (only works with certain templates, then you AppDelegate will have a managedObjectContext @property).

    There will be an @property and a method saveContext generated for managing the core data.

@property (readonly, strong) NSPersistentContainer *persistentContainer;
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.

- (void)saveContext;

We’re going to focus on doing the first one UIManagedDocument.

UIManagedDocument

UIManagedDocument inherits from UIDocument which provides a lot of mechanism for the management of storage. It also good for iCloud support. Think of a UIManagedDocument as simply a container for your Core Data database.

1. Create a UIManagedDocument

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName = @"MyDocument"; 
NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];
UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url];

This creates the UIManagedDocument instance, but does not open nor create the underlying file.

2. Open or Create a UIManagedDocument

Check if the UIManagedDocument’s underlying file exists on disk:

BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]]; 

Here is an example:

self.document = [[UIManagedDocument alloc] initWithFileURL:(URL *)url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
    [document openWithCompletionHandler:^(BOOL success) {
        if (success) [self documentIsReady];
        if (!success) NSLog(@"couldn’t open document at %@", url);
    }];
} else {
    [document saveToURL:url forSaveOperation:UIDocumentSaveForCreating
      completionHandler:^(BOOL success) {
          if (success) [self documentIsReady];
          if (!success) NSLog(@"couldn't create document at %@", url);
      }];
}

- (void)documentIsReady {
   if (self.document.documentState == UIDocumentStateNormal) { 
	// start using document
	NSManagedObjectContext *context = self.document.managedObjectContext;
   } 
}

3. Saving & Closing

  • Notice that UIManagedDocuments AUTOSAVE themselves!
  • Will automatically close if there are no strong pointers left to it.

NSNotification

How would you watch a document’s managedObjectContext?

- (void)viewDidAppear:(BOOL)animated
 {
     [super viewDidAppear:animated];
     [center addObserver:self
                selector:@selector(contextChanged:)
                    name:NSManagedObjectContextDidSaveNotification
                  object:document.managedObjectContext]; //don’t pass nil here!
 }
 - (void)viewWillDisappear:(BOOL)animated
 {
     [center removeObserver:self
                       name:NSManagedObjectContextDidSaveNotification
                     object:document.managedObjectContext];
     [super viewWillDisappear:animated];
 }

Core Data

Now we use the UIManagedDocument’s managedObjectContext @property to insert/delete objects in the database and query for objects in the database.

Query

We can do this by executing an NSFetchRequest in our NSManagedObjectContext.

Four important things involved in creating an NSFetchRequest

  1. Entity to fetch (required);
  2. How many objects to fetch at a time and/or maximum to fetch (optional, default: all);
  3. NSSortDescriptor to specify the order in which the array of fetched objects are returned;
  4. NSPredicate* specifying which of those Entities of those Entities to fetch (optional, default is all of them).

Dependency Injection

Q: How to get managedObjectContext for viewController other than getting it from appDelegate?

A: Use Dependency Injection

answer on stackoverflow

Basically the caller/constructor should be setting the NSManagedObjectContext onto the called/constructed.

In your AppDelegate you should set the NSManagedObjectContext into the rootViewController that is associated with the UIWindow.

Your rootViewController should then set the NSManagedObjectContext into the next view controller and so on.

How? It is just a simple proper on the view controller class and the caller uses:

[nextViewController setManagedObjectContext:[self managedObjectContext]];

Some others may recommend a singleton but that is another deep dark pit that is best avoided.



KF

Comments