Tag Archives: Core Data

How to instantiate an NSManagedObjectContext in iOS 9

Prior to iOS 9, Apple would do the heavy lifting and setup a Core Data stack with certain templates. One of those methods was the custom initialiser for the managed object context in the AppDelegate.m file.

Since iOS 9 however, the standard init method is deprecated and we need to use a designated initialiser instead, called initWithConcurrency. This initialiser has been around since iOS 5, but up until now it was optional to use it.

Here’s what a typical custom initialiser for a managed object context looked like prior to iOS 9:

- (NSManagedObjectContext *)managedObjectContext {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}

Opening a project with a method like this now will give us a deprecation warning and we must use initWithConcurrency instead.

This means that we now have to make a choice about what thread our managed object context is initialised on: the main queue, or perhaps a special background queue we’ve created. Our choices are:

  • NSPrivateQueueConcurrencyType
  • NSMainQueueConcurrencyType

Unless you have a private queue, the previous default behaviour can be brought back with the latter option. Here’s what the updated method would look like:

- (NSManagedObjectContext *)managedObjectContext {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }

    // since iOS 9
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}




Why am I not seeing iCloud Store Log Messages in Xcode 7

Screen Shot 2015-09-19 at 11.05.12

I was trying my hand at a new Core Data app with iCloud support today. To my surprise, none of those ubiquitous log messages appeared! You know, the ones that say “Using local storage: 1” and “Using local storage: 0” to indicate which store file is being used. I remember this working flawlessly in Xcode 5 not too long ago.

Turns out that it has to do with the user’s iCloud account, and in what way the App ID is setup. Let me save you some grey hairs and point out the following:

  • an App ID compatible with Xcode 5 will work no matter if the user has upgraded to iCloud Drive or not
  • an App ID compatible with Xcode 6 and above will ONLY work if the user HAS upgraded to iCloud Drive

You can change the behaviour of the App ID in Member Center, under Identifiers – App ID (see screenshot above). If the App ID is setup for iCloud Drive, and the user’s iCloud account has not been upgraded yet, no notifications will show up in your log files.

Upgrading to iCloud Drive

Note that you must upgrade to iCloud Drive on each device that’s currently in use. Although logic dictates that this upgrade happens on a server somewhere, and upgrading one device would be reflected on all others connected to the same iCloud account, in practice this is not the case: a second device may still not show those notifications unless you head over to Settings – iCloud and explicitly hit the “upgrade” button.

iCloud-Drive

Kudos to Zhang and his findings on this subject:





How to fetch multiple Entities with an NSFetchedResultsController

It’s easy to power a UITableView with Core Data, thanks to the NSFetchedResultsController. To display data, the latter needs an NSFetchRequest. This is great if you’re displaying the same entity over and over again in your table view.

By default and definition however, a fetch request can only ever fetch a single entity from our Core Data stack. What happens if you have two or more entities that you’d like to display in your table view? Technically we’d need multiple fetch requests for that, but how would we add all of them to the fetched results controller?

The answer is: we can’t!

But there is a workaround that may work for your project: we can make use of Entity Inheritance, which will allow us to fetch multiple entities with the same fetch request. This can be a useful timesaver. In this article I’ll explain how this works.

Entity Inheritance only makes sense if your entities are similar in structure. Imagine a flight tracking app in which you have two entities, one for incoming and one for departing flights. Both entities would perhaps have a flight number (NSString) and a time associated with them (NSDate).

 

Let’s build a project

To put this into practice, we can create one entity with these attributes, and then create a second empty entity that inherits from the first. Alternatively we can create one abstract entity with those attributes, and create two child entities that both inherit from the first – either approach will work fine. I’ll stick to the latter option for the remainder of this article.

Let’s create a new project based on the Master/Detail template and inspect the data model. It contains one Event entity that we’ll use. It already contains an NSDate attribute called timeStamp, and I’ll add another one called flightNumber (an NSString).

Screen Shot 2015-09-07 at 11.58.58

We will use this entity in our fetch request to show data in our table view, but we won’t create “events” as such to populate the app. We’ll do that with two new entities that shall be children of this parent entity. Let’s go and create those, and call them Inbound and Outbound. Leave them empty, but select Event as the parent entity on the right hand side (in the Data Model Inspector).

Screen Shot 2015-09-07 at 12.04.03

So far so good! To finish off the data model, let’s create managed object subclasses so we can work with them better in our code. Head over to Editor – Create NSManagedObject Subclass and select all three entities. Xcode is a bit buggy here: make sure you place them inside your project folder, Xcode will always volunteer the folder prior to that.

Screen Shot 2015-09-07 at 12.06.07

Notice also that Xcode will place your new files above the project in the navigator. Apple are good at changing stuff without notice, but they’re not so good at fixing things (as this example shows). Simply highlight the classes and drag them into your main project’s group. Your project navigator should look like this – just to avoid any nasty error messages down the line:

Screen Shot 2015-09-07 at 12.50.20

 

Coding the interface

I’m going to keep it super simple here and leave most of the code from the template as it is. I will however create two empty methods to insert an inbound and an outbound flight to the app. We already have a plus icon at the top right that will insert an Event item. We’ll use the same location to create an outbound flight, and on the left hand side we’ll add one to create an inbound flight.

Before we begin, we’ll import all necessary header files into MasterViewController.h:

#import <UIKit/UIKit.h>
#import <CoreData/CoreData>
#import "Event.h"
#import "Inbound.h"
#import "Outbound.h"
#import "AppDelegate.h"

In addition to our three generated subclasses I’m also importing AppDelegate.h so that I can call a convenient method that will save our managed object context for us.

Next, in the MasterViewController.m file, we’ll add two methods that will create a dummy object each and inter it into the managed object context.

 

- (void)insertInbound {
    
    // create an Inbound object
    Inbound *newFlight = [NSEntityDescription insertNewObjectForEntityForName:@"Inbound" inManagedObjectContext:self.managedObjectContext];
    newFlight.timeStamp = [NSDate date];
    newFlight.flightNumber = @"Inbound Flight";
    
    // save the context (using AppDelegate method)
    AppDelegate *myAppDelegate = [UIApplication sharedApplication].delegate;
    [myAppDelegate saveContext];
}

- (void)insertOutbound {
    
    // create an Outbound object
    Outbound *newFlight = [NSEntityDescription insertNewObjectForEntityForName:@"Outbound" inManagedObjectContext:self.managedObjectContext];
    newFlight.timeStamp = [NSDate date];
    newFlight.flightNumber = @"Outbound Flight";
    
    // save the context (using AppDelegate method)
    AppDelegate *myAppDelegate = [UIApplication sharedApplication].delegate;
    [myAppDelegate saveContext];
}

When an object is created, it is populated with dummy values here: a date and time of “right now” and a simple title that shows what type of flight has been added. Obviously in a real app, this is where the actual data would be added. As soon as we save the context, our objects are stored (we’ll do that via the conveniently provided method in our AppDelegate).

We shall call the above methods with two buttons at the top left and right. Rather than add those with connections in the storyboard, we’ll do it like Apple did in the template and create those button objects in code. Apple does it in viewDidLoad, which currently looks like this:

 

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
    self.navigationItem.rightBarButtonItem = addButton;
    
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}

 

I’ll remove the edit button on the left and replace it with my own instances, each of which will call the insert methods via a selector. Here’s the updated version for our demo app:

 

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // left button
    UIBarButtonItem *inboundButton = [[UIBarButtonItem alloc]initWithTitle:@"In" style:UIBarButtonItemStyleDone target:self action:@selector(insertInbound)];
    self.navigationItem.leftBarButtonItem = inboundButton;
    
    // right button
    UIBarButtonItem *outboundButton = [[UIBarButtonItem alloc]initWithTitle:@"Out" style:UIBarButtonItemStyleDone target:self action:@selector(insertOutbound)];
    self.navigationItem.rightBarButtonItem = outboundButton;
    
    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}

 

Displaying our data

Try running the app: it should work fine and insert a new timestamp with either button. But all we see is the date right now. Let’s fix this by entering our Main.storyboard. Find the table view cell, select it and change its style from Basic to Subtitle. This will display two lines of text. Feel free to change the word Master into something more appropriate (perhaps Flight Tracker).

Screen Shot 2015-09-07 at 12.53.17

Now we’ll head back to MasterViewController.m and populate those two lines properly. There’s a method called configureCell. It currently looks like this:

 

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [[object valueForKey:@"timeStamp"] description];
}

 

Let’s zoosh it up a bit and replace it with this:

 

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    
    // grab the current object
    Event *currentFlight = [self.fetchedResultsController objectAtIndexPath:indexPath];
    
    // show the title
    cell.textLabel.text = currentFlight.flightNumber;
    
    // format the date
    NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
    formatter.dateStyle = NSDateFormatterMediumStyle;
    formatter.timeStyle = NSDateFormatterMediumStyle;
    
    // show the flight time
    cell.detailTextLabel.text = [formatter stringFromDate:currentFlight.timeStamp];
}

And there we have it: both our entities are displayed in the same table view.

Result!

iOS Simulator Screen Shot 7 Sep 2015 13.02.41

 

Extra Credit: Tinting cells by testing the entity type

There’s just one more thing: we may want to set inbound and outbound flights apart visually by tinting the cell background depending on what type of entity is displayed. For example, inbound flights could be shown as green, while outbound flights could be shown in blue.

We can do this by testing which class the current Event object belongs to. Add this code to the bottom of the configureCell method we’ve seen above:

 

    // colour the cell background by testing the class
    UIColor *green = [UIColor colorWithRed:0.8 green:1.0 blue:0.8 alpha:1];
    UIColor *blue = [UIColor colorWithRed:0.8 green:0.8 blue:1.0 alpha:1];
    
    if ([currentFlight isKindOfClass:[Inbound class]]) {
        cell.backgroundColor = green;
    } else {
        cell.backgroundColor = blue;
    }

iOS Simulator Screen Shot 7 Sep 2015 18.00.39

And there we have it!

Entity Inheritance behaves much like Class Inheritance: whatever attributes the parent entity had, the children will inherit without us having to create the same attributes again. The same goes for any code we write in the parent subclass. Obviously Entity Inheritance only makes sense if your data is similar, as it is in the flight tracker example, or perhaps in a personnel database.

 

Demo Project

I’ve got a working demo project on GitHub. It’s a fully working version of what I’ve explained in this article. Feel free to examine it:





How to implement Table Headers with an NSFetchedResultsController in Core Data

iOS Simulator Screen Shot 28 Aug 2015 15.48.03To use sections in a table view that’s powered by Core Data and a fetched results controller, we need to specify a property called sectionNameKeyPath. It’s the key to making those header titles show up. Reading Apple’s ever so colourful documentation however doesn’t give us a clue what this means in human terms.

Here’s how to implement those with a simple example.

Starting out from the Master/Detail template in Xcode, create a new project and choose Core Data. This will setup the fetched results controller along with the rest of the Core Data stack. We’ll also get one handy Event entity with a single attribute: an NSDate called timeStamp. Run the app, click the plus button at the top and we’ll see a new date is inserted into the table.

But the table has no sections. Let’s assume we’d like to create a new section for every minute that has been logged in the database. So 18:07 would have a section, 18:08, and whatever else is available.

Creating Managed Object Subclasses

First we’ll create a managed object subclass from our model so that we can write some code for this. Choose the .xcdatamodeld file in Xcode and select Editor – Create NSManagedObject Subclass. Stash the two resulting files in your project folder.

In Event.h, add another property. It won’t be saved, it’ll be calculated on the fly. We already have the timeStamp property for the date object, so let’s call the new property minuteSection. Here’s the Even.h file:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.>


@interface Event : NSManagedObject

@property (nonatomic, retain) NSDate * timeStamp;
@property (nonatomic, retain) NSString *minuteSection;

@end

In the Event.m file we’ll create a custom initialiser, which will turn our timeStamp into time string. Since this is a Core Data property we’ll have to mark it as dynamic, much like all the other Core Data properties in managed object subclasses:

#import "Event.h"


@implementation Event

@dynamic timeStamp;
@dynamic minuteSection;

- (NSString *)minuteSection {
    
    NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
    formatter.timeStyle = NSDateFormatterShortStyle;
    NSString *sectionTitle = [formatter stringFromDate:self.timeStamp];
    
    return sectionTitle;
}

@end

Here we use an NSDateFormatter that turns the long date object into something like 18:07. Every event in our database logged at 18:07 for example will get its own section with that title.

 

Tweaking the Fetched Results Controller

Next, let’s turn our attention to the NSFetchedResultsController. In the Xcode template it’s in MasterViewController.m. There’s a custom initialiser with a method that by default looks like this:

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                         initWithFetchRequest:fetchRequest
                                                         managedObjectContext:self.managedObjectContext
                                                         sectionNameKeyPath:nil
                                                         cacheName:@"Master"];

Take a look at the sectionNameKeyPath property here: it’s set to nil, meaning we’re not displaying any sections. To tell the controller which property we want to use as section criteria, all we have to do is provide the string of the property we’d like to use. In our case, that’s minuteSection. Quick modification:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
                                                         initWithFetchRequest:fetchRequest
                                                         managedObjectContext:self.managedObjectContext
                                                         sectionNameKeyPath:@"minuteSection"
                                                         cacheName:nil];

I’ve also taken the liberty to remove the cache and set it to nil, we don’t really need it here.

Showing the Section Titles

Right now the app will run as usual, but we still won’t see any new sections. Even though they are displayed, we don’t see them separated from the cells. What we need to do is add a method to our table view delegate and make those sections show up with the right titles.

In MasterViewController.m, hunt for a pragma mark called Table View. You’ll find methods to show how many rows and sections there are, both of which are being read from the fetched results controller. Examine the code to spot some similarities to what we’re doing next.

Add a method called titleForHeaderInSection, it comes up as part of the automatic code completion as soon as you type “- table” (it’s a long list). Here’s what we need to add to it for those section headers to show up:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    
    id  sectionInfo = [self.fetchedResultsController.sections objectAtIndex:section];
    return [sectionInfo name];
}

It’s only two lines of code, but they’re rather long. Here we grab the controller’s current section and call it’s name method, which returns the string we’re looking for (i.e. 18:07, the hour and minute we’d like to use).

Run the app and add a few values to it. On every new minute, a new section is created. When you remove values, empty sections are automatically removed as well.

Further Reading

Demo Project

I’ve got a working demo project on GitHub – feel free to check it out:

iOS Simulator Screen Shot 28 Aug 2015 15.48.03





How to reset your iCloud Ubiquitous Container

In this screencast I’ll show you how to reset your iCloud Ubiquity Container from an iOS device. This will clear up anything you’ve saved in iCloud Documents and iCloud Core Data and also simulates “first installation”.

Sometimes when you test the same app over and over the container can get corrupted, error messages start flooding in and ultimately your app doesn’t behave as expected – making you doubt your sanity, alienating your friends and family and giving you more grey hair then you need.

If the above still doesn’t work, turn your devices off and on again, restart Xcode and reboot your Mac – a useful tip often forgotten with pesky bugs that don’t make sense (thanks to Eric for reminding me of this).

In a nutshell:

  • remove your app from all devices – this is important because running apps, or existing apps launched will assume there’s data in iCloud and they will try to communicate with your (defunct) container
  • remote all data from the container as described in the video (on iOS, head over to Settings – iCloud – Manage Storage – find your app and select it, hit edit and delete all data)
  • now re-deploy the app to each device via Xcode

For Core Data: the first app that’s launched checks to see if data exists in iCloud, and if not will create the iCloud store. Subsequent apps will see this has been done and read the changes from those mysterious log files.

Removing the container in code

You can also call a method on the NSPersistentStoreCoordinator class to remove the entire ubiquity container. Here’s how to do that:

- (void)deleteCloudContainer {
    
    // remove this store's iCloud Content
    NSPersistentStore *currentStore = self.managedObjectContext.persistentStoreCoordinator.persistentStores.lastObject;
    NSError *error = nil;
    [self.managedObjectContext reset];
    [self.managedObjectContext.persistentStoreCoordinator removePersistentStore:currentStore error:nil];
    
    if (![NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:currentStore.URL options:currentStore.options error:&error]) {
        NSLog(@"Could not remove iCloud Container. Reason: %@", error.localizedFailureReason);
    }
}

This does pretty much the same thing as the manual method, although it may take a moment. Works in iOS 7, but I’ve not had much luck in iOS 8 Beta 5 (in fact, I’ve not had much luck with anything in iOS 8 Beta 5).





Core Dara Nugget #1: How to speak Core Data

In this screencast I’ll talk you through the lingo of Core Data: those scary classes and expressions that you’ll frequently come across. In fact, this is the start of a new series:

Core Data Nuggets are bite-sized chunks about the framework. Dip in and out or watch them all in a row and learn how this super complicated framework works and what it has to offer.

Don’t get overwhelmed by Core Data: it wants to help – it’s just not designed with humans in mind.

As always, enjoy!