13 April 2010

Core Data Tutorial: Getting Started

If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting!

 

Core Data Failed Banks Model Diagram

Core Data Failed Banks Model Diagram

Of all of the ways to persist data on the iPhone, Core Data is the best one to use for non-trivial data storage. It can reduce the memory overhead of your app, increase responsiveness, and save you from writing a lot of boilerplate code.

However, the learning curve for Core Data can be quite large. That’s where this tutorial series comes in – the goal is to get you up to speed with the basics of Core Data quickly.

In this first part of the series, we’re going to create a visual data model for our objects, run a quick and dirty test to make sure it works, and then hook it up to a table view so we can see a list of our objects.

In the second part of the series, we’re going to discuss how to import or preload existing data into Core Data so that we have some good default data when our app starts up.

In the final part of the series, we’re going to discuss how we can optimize our app by using NSFetchedResultsController, to reduce memory overhead and improve response time.

Before proceeding with this tutorial, I recommend checking out my tutorial series on SQLite for iPhone Developers first. On the iPhone, the backing store for Core Data is SQLite, so it helps to have an understanding of how that works first. Plus, the app we’re making is the same app we made in that tutorial – just with Core Data this time!

Creating a Core Data Project

So let’s get started! Create a new Window-based Application, and choose “Use Core Data for storage”, and name the project “FailedBanksCD.”

Before we begin, let’s take a quick look at the project. First expand Resources and double click on FailedBanksCD.xcdatamodel. You’ll see a visual editor will pop up – this is what we’ll be using in a minute to diagram our model objects. Go ahead and close it for now.

Then take a look at FailedBanksCDAppDelegate.m. You’ll see that there are several new functions in here that are implemented for us, to set up the Core Data “stack”. One creates a managed object context, one creates a managed object model, and one creates a persistent store coordinator. Huh??

Don’t worry. The names sound confusing at first, but once you get a “mental shortcut” for what they’re all about they are easy to understand.

  • Managed Object Model: You can think of this as the database schema. It is a class that contains definitions for each of the objects (also called “Entities”) that you are storing in the database. Usually, you will use the visual editor you just peeked at to set up what objects are in the database, what their attributes, and how they relate to each other. However, you can do this with code too!
  • Persistent Store Coordinator: You can think of this as the database connection. Here’s where you set up the actual names and locations of what databases will be used to store the objects, and any time a managed object context needs to save something it goes through this single coordinator.
  • Managed Object Context: You can think of this as a “scratch pad” for objects that come from the database. It’s also the most important of the three for us, because we’ll be working with this the most. Basically, whenever you need to get objects, insert objects, or delete objects, you call methods on the managed object context (or at least most of the time!)

Don’t worry too much about these methods – you won’t have to mess with them much. However, it’s good to know that they are there and what they represent.

Defining Our Model

When we created our database tables in the SQLite tutorial, we had a single table containing all of the data for a failed bank. To reduce the amount of data in memory at once (for learning purposes), we just pulled out the subset of the fields we needed for display in our first table view.

So we might be tempted to set up our model the same way with Core Data. However, with Core data you cannot retrieve only certain attributes of an object – you have to retrieve the entire object. However, if we factor the objects into two pieces – the FailedBankInfo piece and the FailedBankDetails piece – we can accomplish the exact same thing.

So let’s see how this will work. Open up the visual model editor (expand Resources and double click FailedBanksCD.xcodedatamodel).

Let’s start by creating an object in our model – referred to as “Entity” in Core Data speak. In the top left pane, click the plus button to add a new Entity like the following:

Create Entity button in Core Data Model Editor

Upon clicking the plus, it will create a new Entity, and show the properties for the Entity in the right panel like the following:

Entity Properties in Core Data Model Editor

Name the Entity FailedBankInfo. Note that it currently lists the class as a subclass of NSManagedObject. This is the default class for Entities, which we’ll use for now – later we’ll come back and set up custom objects.

So let’s add some attributes. First, make sure that your Entity is selected by either clicking on the Entity name in the left panel, or the diagram for the entity in the diagram view. In the middle panel, click the plus button and then click “Add Attribute” like the following:

Create Attribute Button in Core Data Model Editor

In the property pane on the right, name the attribute “name” and set the Type to “String” like the following:

Setting name attribute screenshot

Now, repeat this to add two more attributes, “city” and “state”, also both strings.

Next, we need to create an entity for FailedBankDetails. Create an Entity the same way you did before, and add the following attributes to it: zip of type Int 32, closeDate of type Date, and updatedDate of type Date.

Finally, we need to associate these two types. Select FailedBankInfo, and click the plus button in the middle pane, but this time select “Add relationship”:

Add Relationship Button in Core Data Model Editor

Name the relationship “details”, and set the destination as “FailedBankDetails.”

Ok, so what did we just do here? We just set up a relationship in Core Data, which links up one entity to another entity. In this case, we are setting up a one-to-one relationship – every FailedBankInfo will have exactly 1 FailedBankDetails. Behind the scenes, Core Data will set up our database so that our FailedBankInfo table has a field for the ID of the corresponding FailedBankDetails object.

Apple recommends that whenever you create a link from one object to another, you create a link from the other object going back as well. So let’s do this.

Now add a relationship to “FailedBankDetails” named “info”, set the destination to “FailedBankInfo”, and set the inverse to “details”.

Also, set the delete rule for both relationships to “cascade”. This means that if you delete one object with Core Data, Core Data will delete the associated object as well. This makes sense in this case because a FailedBankDetails wouldn’t mean anything without a corresponding FailedBankInfo.

Testing our Model

Believe it or not, that was probably the most important thing we needed to do. Now it’s just a matter of testing out using Core Data and making sure it works!

First, let’s test out adding a test object to our database. Open FailedBanksCDAppDelegate.m and add the following to the top of applicationDidFinishLaunching:

NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *failedBankInfo = [NSEntityDescription
    insertNewObjectForEntityForName:@"FailedBankInfo" 
    inManagedObjectContext:context];
[failedBankInfo setValue:@"Test Bank" forKey:@"name"];
[failedBankInfo setValue:@"Testville" forKey:@"city"];
[failedBankInfo setValue:@"Testland" forKey:@"state"];
NSManagedObject *failedBankDetails = [NSEntityDescription
    insertNewObjectForEntityForName:@"FailedBankDetails" 
    inManagedObjectContext:context];
[failedBankDetails setValue:[NSDate date] forKey:@"closeDate"];
[failedBankDetails setValue:[NSDate date] forKey:@"updatedDate"];
[failedBankDetails setValue:[NSNumber numberWithInt:12345] forKey:@"zip"];
[failedBankDetails setValue:failedBankInfo forKey:@"info"];
[failedBankInfo setValue:failedBankDetails forKey:@"details"];
NSError *error;
if (![context save:&error]) {
    NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}

In the first line, we grab a pointer to our managed object context using the helper function that comes included with the template.

Then we create a new instance of an NSManagedObject for our FailedBankInfo entity, by calling insertNewObjectForEntityForName. Every object that Core Data stores derives from NSManagedObject. Once you have an instance of the object, you can call setValue for any attribute that you defined in the visual editor to set up the object.

So we go ahead adn set up a test bank, for both FailedBankInfo and FailedBankDetails. At this point the objects are just modified in meomry – to store them back to the database we need to call save on the managedObjectContext.

That’s all there is to it to insert objects – no SQL code necessary!

Before we try this out, let’s add some more code in there to list out all the objects currently in the database:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription 
    entityForName:@"FailedBankInfo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
    NSLog(@"Name: %@", [info valueForKey:@"name"]);
    NSManagedObject *details = [info valueForKey:@"details"];
    NSLog(@"Zip: %@", [details valueForKey:@"zip"]);
}        
[fetchRequest release];

Here we create a new object called a fetch request. You can think of a fetch request as a SELECT clause. We call entityForName to get a pointer to the FailedBankInfo entity we want to retrieve, and then use setEntity to tell our fetch request that’s the kind of Entity we want.

We then call executeFetchRequest on the managed object context to pull all of the objects in the FailedBankInfo table into our “scratch pad”. We then iterate through each NSManagedObject, and use valueForKey to pull out various pieces.

Note that even though we pulled out just the objects from the FailedBankInfo table, we can still access the associated FailedBankDetails object by acessing the details property on the FAiledBankInfo entity.

How does this work? Behind the scenes, when you access that property Core Data notices that it doesn’t have the data in the context, and “faults”, which basically means it runs over to the database and pulls in that data for you right as you need it. Pretty convenient!

Give this code a run and take a look in your output window, and you should see a test bank in your database for every time you run the program.

Seeing the Raw SQL Statements

I don’t know about you, but when I’m working on this kind of stuff I really like to see the actual SQL statements going on to understand how things are working (and make sure it’s doing what I expect!)

Once again Apple has provided an easy solution to this. Open the Executables drop-down in XCode and find your FailedBanksCD executable. Right click on that and click “Get Info.” Navigate to the Arguments tab and add the following argument: “-com.apple.CoreData.SQLDebug 1″. When you’re done it should look like the following:

Enabling SQL Statement Debugging for Core Data

Now when you run your code, in the debug output you should see useful trace statements like this showing you what’s going on:

SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?
UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?
INSERT INTO ZFAILEDBANKDETAILS(Z_PK, Z_ENT, Z_OPT, ZINFO,
    ZUPDATEDDATE, ZZIP, ZCLOSEDATE) VALUES(?, ?, ?, ?, ?, ?, ?)
INSERT INTO ZFAILEDBANKINFO(Z_PK, Z_ENT, Z_OPT, ZDETAILS, ZNAME,
    ZSTATE, ZCITY) VALUES(?, ?, ?, ?, ?, ?, ?)
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZSTATE, t0.ZCITY, t0.ZDETAILS
    FROM ZFAILEDBANKINFO t0
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZUPDATEDDATE, t0.ZZIP, t0.ZCLOSEDATE,
    t0.ZINFO FROM ZFAILEDBANKDETAILS t0 WHERE  t0.Z_PK = ?

So here we see things are working as we expect. The first two selects and update are Core Data doing some bookkeeping work keeping track of what the next ID for the entity should be.

Then we have our inserts into the details and info tables. After that, we select the entire bank info table in our query. Then as we iterate through the results, each time we access the details variable, behind the scenes Core Data faults and issues another select statement to get the data from the ZFAILEDBANKDETAILS table.

Auto Generating Model Files

So far, we’ve been using NSManagedObject to work with our Entities. This isn’t the best way to do things, because it’s quite easy to make a mistake and type an attribute name incorrectly, or set data using the wrong type, etc.

The better way to do things is to create a Model file for each entity. You can do this by hand, but XCode makes this quite easy with a class generator.

Let’s try it out. Open up FailedBanksCD.xcdatamodel, click on the FailedBankInfo entity, and go to File\New File. Select “Cocoa Touch Class”, and you should see a new template for “Managed Object Class.” Select this and click Next, and then click Next again on the following view.

Managed Object Class Template Screenshot

In the third view, XCode will allow you to select the Entities to generate classes for. To save time, make sure BOTH FailedBankInfo and FailedBankDetails are checked, and click Finish.

You should see some new files added to your project: FailedBankInfo.h/m and FailedBankDetails.h/m. These classes are very simple, and just declare properties based on the attributes you added to the entities. You will notice that the properties are declared as dynamic inside the .m files – this is because Core Data handles wiring the properties up automatically.

I did notice one problem with the autogenerated classes that I had to fix. If you look at FailedBankDetails.h, you’ll notice that the info variable is correctly declared as a FailedBankInfo class, but in FailedBankInfo.h the details variable is defined as an NSManagedObject (but it should be a FailedBankDetails object). You can fix this by adding a predeclaration of FailedBankDetails to the top of the file:

@class FailedBankDetails;

And then changing the details property declaration to the following:

@property (nonatomic, retain) FailedBankDetails * details;

Also, take a peek back in FailedBanksCD.xcdatamodel. When you look at the properties for the entities, you’ll notice that the classes have now been set automatically to the names of the autogenerated classes:

Screenshot of how Autogenerated Class name is Automatically Set

Now, let’s tweak our test code in the app delegate to use our new subclasses of NSManagedObject. First add the headers we’ll need up top:

#import "FailedBankInfo.h"
#import "FailedBankDetails.h"

Then change the code as follows:

NSManagedObjectContext *context = [self managedObjectContext];
FailedBankInfo *failedBankInfo = [NSEntityDescription
    insertNewObjectForEntityForName:@"FailedBankInfo" 
    inManagedObjectContext:context];
failedBankInfo.name = @"Test Bank";
failedBankInfo.city = @"Testville";
failedBankInfo.state = @"Testland";
FailedBankDetails *failedBankDetails = [NSEntityDescription
    insertNewObjectForEntityForName:@"FailedBankDetails" 
    inManagedObjectContext:context];
failedBankDetails.closeDate = [NSDate date];
failedBankDetails.updatedDate = [NSDate date];
failedBankDetails.zip = [NSNumber numberWithInt:12345];
failedBankDetails.info = failedBankInfo;
failedBankInfo.details = failedBankDetails;
NSError *error;
if (![context save:&error]) {
    NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
 
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FailedBankInfo" 
    inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (FailedBankInfo *info in fetchedObjects) {
    NSLog(@"Name: %@", info.name);
    FailedBankDetails *details = info.details;
    NSLog(@"Zip: %@", details.zip);
}        
[fetchRequest release];

This is pretty much the same code we had before, except instead of using NSManagedObject directly, we use our new subclasses. Now we have type safety and cleaner code!

Creating a Table View

Right click on Classes and click “Add\New File…” and pick “UIViewController subclass”, making sure “UITableVIewController subclass” is checked and “With XIB for user interface” is NOT checked. Name the class FailedBanksListViewController.

Open up FailedBanksListViewController.h and add a two member variables:

  • A member variable/property for the failedBankInfos which we’ll retrieve from the database, just like we used last time.
  • A member variable for the managed object context to use. Note we could retrieve this from the application delegate, but it’s better practice to have it passed in as a member variable to avoid interdependence.

So when you’re done it should look like the following:

#import <UIKit/UIKit.h>
 
@interface FailedBanksListViewController : UITableViewController {
    NSArray *_failedBankInfos;
    NSManagedObjectContext *_context;    
}
 
@property (nonatomic, retain) NSArray *failedBankInfos;
@property (nonatomic, retain) NSManagedObjectContext *context;
 
@end

Switch over to FailedBanksListViewController.m and add some imports, your synthesize statement, and your cleanup code:

// At very top, in import section
#import "FailedBankInfo.h"
 
// At top, under @implementation
@synthesize failedBankInfos = _failedBankInfos;
@synthesize context = _context;
 
// In dealloc
self.failedBankInfos = nil;
self.context = nil;

Then uncomment viewDidLoad and modify it to look like the following:

- (void)viewDidLoad {
    [super viewDidLoad];
 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription 
        entityForName:@"FailedBankInfo" inManagedObjectContext:_context];
    [fetchRequest setEntity:entity];
    NSError *error;
    self.failedBankInfos = [_context executeFetchRequest:fetchRequest error:&error];
    self.title = @"Failed Banks"; 
    [fetchRequest release];
 
}

This code should look pretty familiar to our test code from earlier. We simply create a fetch request to get all of the FailedBankInfos in the database, and store it in our member variable.

The rest of the mods are exactly like we did in the SQLite tutorial. For quick reference, I’ll list the remaining steps here again:

Return 1 for numberOfSectionsInTableView:

 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

Replace numberOfRowsInSection with the following:

- (NSInteger)tableView:(UITableView *)tableView 
    numberOfRowsInSection:(NSInteger)section {
    return [_failedBankInfos count];
}

Modify cellForRowAtIndexPath to look like the following:

- (UITableViewCell *)tableView:(UITableView *)tableView 
    cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = 
        [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
            reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Set up the cell...
    FailedBankInfo *info = [_failedBankInfos objectAtIndex:indexPath.row];
    cell.textLabel.text = info.name;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", 
        info.city, info.state];
 
    return cell;
}

Add an outlet into FailedBanksCDAppDelegate.h for the UINavigationController we’re about to add:

@interface FailedBanksCDAppDelegate : NSObject <UIApplicationDelegate> {
 
    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;	    
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
 
    UIWindow *window;
    UINavigationController *_navController;
}
 
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
 
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navController;
 
- (NSString *)applicationDocumentsDirectory;
 
@end

Open up Resources and double click on MainWindow.xib. Drag a Navigation Controller from the library into the MainWindow.xib. Click on the down arrow on the Navigation Controller that you just added, click on the View Controller, over in the attribute panel switch to the fourth tab, and switch the “Class” to “FailedBanksListViewController.”

Interface Builder Screenshot

Finally, control-drag from “FailedBanksCD App Delegate” in MainWindow.xib to “Navigation Controller”, and connect it to the “navController” outlet. Save the xib and close.

Now all we need to do is add a few lines to our FailedBanksCDAppDelegate.m:

// At top
#import "FailedBanksListViewController.h"
// Under @implementation
@synthesize navController = _navController;
// In applicationDisFinishLaunching, before makeKeyAndVisible:
FailedBanksListViewController *root = (FailedBanksListViewController *) [_navController topViewController];
root.context = [self managedObjectContext];
[window addSubview:_navController.view];
// In dealloc
self.navController = nil;

Compile and run the project, and if all looks well you should see the sample banks we added earlier!

Screenshot of Failed Banks App with Core Data

Where to Go From Here?

Here’s the sample code for the project so far.

So far so good – except we’re missing the actual data from the failed banks. So next in this tutorial series we’ll cover how to preload/import existing data!


Category: iPhone

Tags: , , ,

90 Comments

  1. Markk (2 comments) says:

    Ray, thanks for a very helpful tutorial!

    I do have a problem, though – I got to the end and when I ran the program, I couldn’t see the TableView at all – just a blank screen.

    Any ideas?

  2. Ray Wenderlich (874 comments) says:

    @Markk – Hm, that is odd! I just ran through the tutorial from scratch to make sure I wasn’t missing any steps, and the table view showed up for me.

    Two things to try: first I just uploaded the sample code to the project – take a look and see if there are any differences between your project and that.

    Second, based on what you’re describing I suspect it’s probably to do with the way the navigation controller and table view controller are hooked together. Double check that the navigation controller you added to the MainWindow.xib is hooked into the outlet on the app delegate, and that the root view controller in the navigation controller is set to FailedBanksListViewController.h.

    If you have any further problems, drop me an email :]

  3. Markk (2 comments) says:

    Hi Ray,

    I found a few changes to make in your sample code, but the main thing I needed to add was a connection between File’s Owner and the app delegate. After that it worked fine!

    Thanks again for an excellent tutorial.

  4. GS (1 comments) says:

    Thanks for a really clear and well paced tutorial. I have done some Core Data work but never really understood why it worked. I even have hand crafted SQLite data correctly and transplanted into my app as prefilled and now with your help I can start to understand how to make it all work.

    I have an application to write that needs data relationships coupled with drill down table views to 4 levels. The data is related by integer ID’s similar to INNER JOIN SQL statements.

    Using SQL would be a cinch. But attempting through the ORM esque nature of Core Data is certainly challenging.

    Have you got any plans to cover how to achieve similar query filtering and displaying in Core Data?

    Thanks again. Really good tutorial.

  5. Ray Wenderlich (874 comments) says:

    @GS: Cool, thanks! Yeah I agree, I really like to understand how a technology like this works rather than treating it like “magic”, helps me understand it much better.

    I hadn’t really thought about future Core Data tutorials that much yet, but that one definitely sounds like a good idea! I added it to my potential idea list.

  6. Adam Burkepile (2 comments) says:

    Very good tutorial, just learned Core Data this week but wish I would have found this tutorial first. Not an extremely easy concept to grasp and Apple’s own documentation is almost useless unless you already understand how it works.

  7. Tom Ortega (2 comments) says:

    Great tutorial. I opened up the Apple Core Data docs a few weeks ago and thought, “I know this has got to be easy, but I can’t see how at this moment.”

    You’re tutorial definitely proved my thoughts were right. I just needed the right teacher.

    My donation should be in your PayPal account. I hope the others who enjoy this tutorial also back up their opinion with a donation.

  8. Ray Wenderlich (874 comments) says:

    @Tom: Thank you so much! I really appreciate it. Also, I just checked out your blog and I love it! Added to my RSS reader :]

    Thanks again!

  9. Sebastian (7 comments) says:

    Good example, Thanks.

    I really would like to see you extend the tutorial to add new data by input and save it.

  10. Sebastian (7 comments) says:

    Just ran into a problem while playing around with this. How do you actually access the FailedBankDetails ?

    I added a DetailViewController to display all infos and I can get all FailedBankInfo, but as soon as I try to access the details, i get a EXC_BAD_ACCESS.

    FailedBankDetails *details_ = bankinfo.details;

    Thanks

  11. Ray Wenderlich (874 comments) says:

    @Sebastian: Hmm, I’m not quite sure. I updated the sample project to include an example of accessing the bank details (check out didSelectRowAtIndexPath and you’ll see an NSLog statement), and I didn’t get an exception.

    Check it out and let me know if you’re still having any issues!

  12. Sebastian (7 comments) says:

    Thanks for looking into it. I actually found my MISTAKE and it had nothing todo with how I access the details at all.

    Stupid me released the details, so it had to run into an Exception.

  13. Sebastian (7 comments) says:

    There is still one question remaining, how do I migrate to a new structure by adding a new attribute or another Entity when I update the app ?

    Doing it and updating the Managed Object Class does not seem to be enough.

  14. Ray Wenderlich (874 comments) says:

    @Sebastian: You can do this in Core Data with migrations (lightweight migrations and standard migrations). You can read up on these in the Core Data guides.

  15. Alan McKean (1 comments) says:

    Excellent tutorial, Ray. Regarding migration and version, there is a good tutorial on migrating and versioning from Tim Isted. You can find it here:
    http://www.timisted.net/blog/archive/core-data-migration/. One gotcha to remember is to ‘Build->Clean All Targets’ after creating a new model version to clean out the old model. This will eliminate errors when two models exist.

  16. Ray Wenderlich (874 comments) says:

    @Alan: Cool Alan, thanks for the tip on the migration tutorial, that looks great!

  17. James (5 comments) says:

    Thanks so much for tutorials! I went through the Apple tutorial and it just didn’t do it for me. Yours are great! (BTW, there’s a tiny typo near the beginning where you have “DeleteMe” in a couple of places, which were meant to be “FailedBanksCD.”)

  18. Ray Wenderlich (874 comments) says:

    @James: Oops, nice catch! Post updated with that fix.

  19. Charles Turner (1 comments) says:

    Hi Ray-

    I have to add my voice to this being a really wonderful tutorial. The way you build from the simplest possible things that work is very helpful, and I’ve learned a lot about developing iOS apps in addition to Core Data.

    I went through and built the project for 3.2 and the iPad, and all went well. Some small niggles I discovered:

    1) You need to return 1 in numberOfSectionsInTableView.

    2) You have to init UITableViewCell with UITableViewCellStyleSubtitle to get what’s shown in your last screenshot.

    3) You probably should refer to “FailedBanksCDAppDelegate.m” in the line toward the end: “Now all we need to do is add a few lines to our FailedBanksAppDelegate.m”

    But these are all on the level of typos.

    Thanks agin, and keep ‘em coming!

    Best wishes, Charles

  20. JJEnden (1 comments) says:

    Hi Ray,
    It looks great! I got it working and it looks like a worthwile alternative for SQL. The only problem is I get a warning I am not comfortable with after having defined my second object: linking the second object to the first works fine. But when I link the first to the second, I get the warning ‘ìncompatible objective-C types ‘struct Struct2*’, expected ‘structNSSet*’ when passing argument 1 of ‘setStruct2:’ from distinctive Objective-C type’.
    I am not sure what to do with this warning.
    Please let me know your thoughts on this.

  21. Ray Wenderlich (874 comments) says:

    @Charles: Awesome, thanks for pointing out those typos! I have updated the tutorial with the fixes.

    @JJEnden: Hm without seeing the code I’m not sure what’s going on, but from the compiler warning it sounds like something is expecting to see a NSSet pointer, but instead has a Struct2 pointer instead.

  22. neil... (1 comments) says:

    Ray, thank you. Unquestionably the best CoreData tutorial around, particularly because I finally understand how to handle multiple entities with relationships.

  23. Alex Tau (9 comments) says:

    I reached the point where you run it for the first time (just before ‘Seeing the Raw SQL Statements’). It crashes with the following error:

    Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: the entity FailedBankDetails is not key value coding-compliant for the key “updatedDate”.’ I have no idea how to fix it. Please help. Thanks!

  24. Ray Wenderlich (874 comments) says:

    @neil: Thanks for the kind words!

    @Alex: Hm, I haven’t had that error myself, but here’s a guide that might help you track down the cause:

    http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/Troubleshooting.html

  25. Kheldar (2 comments) says:

    Hi Ray,
    thanks for the tutorial, it’s invaluable.
    However, I’ve got the question: how did you find out about the “-com.apple.CoreData.SQLDebug 1″ argument? I’m willing to wager that source has a LOT of other information I’d be interested in :D

  26. Ray Wenderlich (874 comments) says:

    @Kheidar: If I recall, I got it from the Core Data Performance section of the Core Data Programming Guide:

    http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html

  27. Ray Wenderlich (874 comments) says:

    @mmaic: Yeah that’s a great tutorial, thanks for pointing it out. Another one I really liked was the following, which is great for understanding how things fit together:

    Core Data Command Line Utility Tutorial

  28. Leonardo (1 comments) says:

    Very good and clear tutorial.
    One question, I already developed an iPhone app, and I am now in need of writing some way of persisting and retrieving objects. Do you think it’s possible to add Core Data on such existing project, and convert already developed classes in Entity ?

    thanks

  29. Adam Burkepile (2 comments) says:

    @Leonardo : Here is a good tutorial I found on adding CoreData to an existing project

    http://wiresareobsolete.com/wordpress/2009/12/adding-core-data-existing-iphone-projects/

  30. Ali (4 comments) says:

    hi , and thank you for your helpful tutorial , i do my project as you explain in this article but i have this problem:
    *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘+entityForName: could not locate an NSManagedObjectModel for entity name ‘phoneBook”
    *** Call stack at first throw:

    thank you

  31. Ray Wenderlich (874 comments) says:

    @Adam: Thanks for helping out Leonardo!

    @Ali: Looks like you might not have set up the phoneBook entity? Check capitalization too.

  32. Sebastian (7 comments) says:

    Thanks for the tutorial. I was stuck with some problems and your walk-through helped me a lot.

    Saludos

  33. srikanth (4 comments) says:

    Hi Ray, first thank u for your useful tutorial very helpfull but i had a following errors while executing the project hope that u sorted out

    Thanks
    srikanth

  34. srikanth (4 comments) says:

    /Users//Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksCDAppDelegate.navController — attribute must have a defined type

    /Users/Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksCDAppDelegate.persistentStoreCoordinator — attribute must have a defined type

    /Users//Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksCDAppDelegate.managedObjectModel — attribute must have a defined type

    /Users//Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksCDAppDelegate.window — attribute must have a defined type

    /Users//Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksCDAppDelegate.managedObjectContext — attribute must have a defined type

    /Users//Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksListViewController.context — attribute must have a defined type

    /Users/krithik/Desktop/srikanth/FailedBanksCD/FailedBanksCD.xcdatamodel:0:0 FailedBanksListViewController.failedBankInfos — attribute must have a defined type

  35. Ray Wenderlich (874 comments) says:

    @srikanth: I just downloaded the sample project and tried it out, and didn’t get any errors like that. Try comparing your project to the sample project and see if there is any differences.

  36. apoorv (1 comments) says:

    Hi Ray,
    I am implementing one application with core data.I get error : Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (2 inserted, 0 deleted).’
    Please help me.

  37. Ray Wenderlich (874 comments) says:

    @apoorv: I’ve seen that error before when my data model did not match the table view’s data model. Make sure that if you’re deleting anything from your model that you also remember to call [tableView delete/insertRowsAtIndexPaths:...] etc.

  38. Robert Nall (3 comments) says:

    As others in the comments have stated, great tutorial! I’ve been running through the tutorials on Apple and in Dudney and Adamson’s “iPhone SDK Development” on CoreData and I’ve been able to get the tutorials to work, but it all seemed rather mystical and I was never successful in applying the concepts to my own project. Your tutorial gave me a bare-bones understanding of CoreData I was missing from the other tutorials. I especially liked simply using the console to checked that it worked. (I’m pretty new to objective-C and working with Apple’s SDKs and not a heavy coder besides, so the console idea was a new one for me.)

    Couple questions I have just related to coding practices (if you don’t mind answering):
    a) What’s the difference between doing memory cleanup with say “property = nil” versus something like a “[property release]” statement I generally see in Apple’s example dealloc methods.
    b) When do I use “_property” instead of “self.property?”

  39. Robert Nall (3 comments) says:

    In case anyone else ran into the same question, here’s a pretty thorough discussion of my question “a” above referring to the use of self.property=nil vs. [property release] in -dealloc (oh, and from other sources I’ve found that property=nil is a big no-no):

    http://www.iphonedevbook.com/forum/viewtopic.php?f=25&t=3009

  40. Ray Wenderlich (874 comments) says:

    @Robert: Thanks for the kind words! Yeah I know the feeling, the way I like to learn is go to from the bare-bones on-up so I completely understand what’s going on…

    Great read in that post, by the way, thanks for sharing! I didn’t realize that you shouldn’t call self.property = nil in dealloc due to the object already being dealloced – good to know. Weird I’ve never seen any problems with it though. I don’t know why it would be a problem to use self.property = nil in other places, though, since it’s basically equivalent to [_property release], _property = nil.

  41. Robert Nall (3 comments) says:

    @Ray: Yeah, the gist I got from the forum discussion above was exactly as you say: self.property = nil is good every where else, but I guess what they were getting as is that on rare occasions one could run into problems calling self.property = nil in -dealloc simply because that infers the call [self property:nil] and, as they say, it’s generally bad form to call methods on an object once that object’s gotten a -dealloc call.

  42. jj (1 comments) says:

    Thank you! you save me

  43. luca (3 comments) says:

    hi Ray and tks,
    i’m studing your tutorial right now,
    and i’ve got this question (don’t really know if it’s a mistake or not):
    every time i start the app on my iPhone a new item (always the same “Test Bank”) is added as new record in the file “FailedBanksCD.sqlite”…
    and the same is done on the simulator (but “just” everytime i build and run the project again, not at every start of the app). I controlled the file FailedBanksCD.sqlite found in the application support of the iphone simulator, and i found more records of the same bank…
    It seems to me that some check was missed at start-Up, as a controller to see if the file already exist in the sandBox documents folder of the app…

    or do i miss something?

    tks
    luca

  44. luca (3 comments) says:

    ooops,
    sorry ray,
    just started to read the part 2 of your tutorial…
    forget (and forgive) my last comment/question

    and tks again,
    luca

  45. Kheldar (2 comments) says:

    Hello again!
    I have encountered Alex Tau’s error above stating:
    Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[ setValue:forUndefinedKey:]: the entity someEntity is not key value coding-compliant for the key “someKey”.

    For me the reason was I had instanciated my value with [[ myValueClass alloc] init]
    instead of the required wrapper-making
    [NSEntityDescription insertNewObjectForEntityForName:@"myKey" inManagedObjectContext:context];

    Hope this helps,
    Kheldar

  46. Ray Wenderlich (874 comments) says:

    @Kheldar: That’s good to know, thanks for sharing so others can benefit!

  47. Mike O'Dell (2 comments) says:

    Thanks for the great tutorial. Like many others I have struggled to wrap my head around this topic. I have been working on an ipad for a few weeks and tutorials like this have been so helpful.

  48. Ray Wenderlich (874 comments) says:

    @Mike: Yeah for some reason I had trouble getting started with Core Data as well, hence why I wanted to write something like this :] Glad it helped!

  49. Kimura S. (3 comments) says:

    Hi Ray, great tutorial, it helps alot! Thanks! But I have a question though… when I tried to do a datamodel with a 1 to many relationship, that relationship is a NSset, so when I try to use your method to create the database with

    [failedBankInfo setValue:failedBankDetails forKey:@"details"];

    it crashed with the following error

    Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Unacceptable type of value for to-many relationship: property = “details”; desired type = NSSet; given type = FailedBankDetails; value = (entity: FailedBankDetails; id: 0x630e820 ; data: {……..etc

    Can you give me a hint on that like how should I set the [failedBankInfo setValue:failedBankDetails forKey:@"details"]; ?

    million thanks~

  50. jscore (7 comments) says:

    Great tutorial, but quick question.

    I see you initialized the context in the app delegate, but in the view controller, you’ve also set a context as a member variables and using that (but without the same initialization as app delegate). How’s the context setup in the second case?

    I’m getting a runtime error for this same reason.

    Thanks.

  51. suprety (2 comments) says:

    Excellent Tutorial!!!
    I was ignoring the CoreData for some time as it looked complicated. Your article has opened my eye.

    Thank you.

  52. Ray Wenderlich (874 comments) says:

    @Kimura: It seems like you’re trying to assign a single FailedBankDetails object to the “details” member variable, which is set to accept an NSSet.

    So what you should do is create an NSSet, add the failed bank details to that, then assign that to the “details” member.

    @jscore: The trick is I set the context in FailedbankcsCDAppDelegate, inside applicationDidFinishLaunching. Love your Gravatar btw! :]

    @superty: Thanks, I was intimidated by it at first too but have grown to really appreciate it!

  53. Tom C (2 comments) says:

    Hey Ray, great stuff! As an objective-C beginner it’s been very helpful in many ways. Having a similar problem to Kimura. Can you expand (maybe with code) on what you mean by “create an NSSet, add the failed bank details to that, then assign that to the “details” member”?

    Thanks and sorry for the newbe question.

  54. Tom C (2 comments) says:

    I think I figured it out:

    NSSet *failedBankDetailsSet = [[NSSet alloc] initWithObjects:failedBankDetails, nil];
    failedBankDetails.info = failedBankInfo;
    failedBankInfo.details = failedBankDetailsSet;

    Now for the next question though, what if I want to add more detail records to an existing failedBankInfo? How would I go about that? Is there documentation somewhere you could point me to?

  55. Chris Ebright (2 comments) says:

    Ray, I was wondering if it is possible to import a sqlite schema for the Object model, instead of having to manually create in the Core data graphical utility? Something like a SQL script to create a new database. Thanks.
    Chris

  56. Srikanth (4 comments) says:

    Hi, Ray,
    I need one help please help me out,
    In the table view cells their should be a static text ,
    this text should be disappear as soon as the other item is come into that cells… how will achieve this..

    Thanks
    srikanth

  57. Ray Wenderlich (874 comments) says:

    @Tom: For every collection class in Foundation, there are two types:

    * Immutable. This means you can’t modify it after you create it. Example: NSSet.
    * Mutable. This means you can modify it after you create it. Example: NSMutableSet.

    So if you want to add more details later, you’ll want to use a NSMutableSet. If you look up the documentation, you’ll see that adding a new element to a NSMutableSet is as easy as calling an addObject: method on it.

    @Chris: Hm good question. I haven’t heard of such a thing, but there may well be something that does that. If you find one let me know!

    @Srikanth: I don’t see what this has to do with Core Data, but… it sounds like you are just trying to have placeholder rows in your tables? Why not just update the cell’s textLabel to the actual value once you get the data you’re looking for?

  58. jacob (2 comments) says:

    Absolutely brilliant stuff! I’ve been banging my head against core data tutorials for days. This is the first one that makes sense. Thank you so much for this!

  59. jacob (2 comments) says:

    One question – why do you have the instance variables _var and the properties var? Why on the @synthesize lines do you set var=_var?

    I don’t understand why you do this, or what the thinking is for using one or the other. And I am not sure when I would want to access the instance variable vs. the property in my own program.

  60. Ray Wenderlich (874 comments) says:

    @jacob: Awesome, glad it helped! Regarding the @synthesize question, it’s basically a coding style preference I find helps me keep things clear. Check my reply to @Jason in this tutorial for more details:

    http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial

  61. soso (3 comments) says:

    it is a very helpful article for the fresh coder~thx

  62. soso (3 comments) says:

    a question i got is the explaination
    about the NSManagedObjectContext ,you descripe it as a “scratch pad”,does it mean a temp space for the data out of the database?

  63. Jonny (1 comments) says:

    Hey Ray,
    I loved your tutorial!
    I searched the web for hours and couldn’t find anything helping me … but then I found this one!
    BUT, I have a problem!
    If I do this:
    hsname1.text = [highscore1 name];
    in the same method I do this:
    [highscore1 setValue:hsnameInput.text forKey:@"name"];
    everything works,
    but if I do this:
    hsname1.text = [highscore1 name];
    in a method without this:
    [highscore1 setValue:hsnameInput.text forKey:@"name"];
    my app crashes!

    Please help, Jonny!

  64. Ray Wenderlich (874 comments) says:

    @soso: You’re exactly right!

    @Jonny: I wonder if maybe hsname is null?

  65. soso (3 comments) says:

    i’d like to interpret your tutorial into my language,of course with your website on the top,can i have your permission of doing this?

  66. Ray Wenderlich (874 comments) says:

    @soso: If you’re interested in hearing my translation policy for this blog, please email me for further details.

  67. Sreeni (1 comments) says:

    Thanks for the great tutorial!!I very much appreciate it.I am kinda new with Core Data in Xcode.I have one question on:”Finally, control-drag from “FailedBanksCD App Delegate” in MainWindow.xib to “Navigation Controller”, and connect it to the “navController” outlet. Save the xib and close.”
    The step in quotes from your article above, I am unable to figure how to accomplish.Can you give more detail instructions?Thank you.

  68. Ray Wenderlich (874 comments) says:

    @Sreeni: Thanks! To answer your question, here’s another try to explain it better:

    1) Double click on MainWindow.xib. This opens up Interface Builder.
    2) You’ll see a window with the first line being “File’s Owner”, then “First Responder”, then “FailedBanksCD App Delegate.” Hold down the control button on your keyboard, click on “FailedBanksCD App Delegate” and keep the button held down, drag your mouse over top the “Navigation Controller” entry in the same window (which draws a line), and release your mouse button.
    3) A window will pop up with outlets to choose from – select the navController entry.
    4) Go to File\Save to save the XIB.

    Hope this helps!

  69. MaKo (4 comments) says:

    hi Ray, thank you first of all, been very helpfull,
    one question,
    when using the above tutorial in iphone simulator 4. 0 works fine,
    but with ipad simulator 3.2
    the same code (not porting yet!, just changing simulator,. which works with other programs)
    I get the app closing done , and the error in console>
    *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Can’t merge models with two different entities named ‘FailedBankInfo”
    2010-11-25 12:01:12.907 FailedBanksCD[1041:207] Stack: (
    44988496,
    43217708,
    …)
    terminate called after throwing an instance of ‘NSException’

    whats happening?
    thank you, really appreciated!

    also, why do I get in the case of the iphone app,
    a very long list of the same arguments, like the db is writing on the next tables, and showing all the last ones,
    can I delete the db to clean it?

    thanks

  70. MaKo (4 comments) says:

    hi, I just deleted the old FailedBanksCD.sqlite
    and now I can see the new data!! in ipad and iphone

    thank you again for your great tutorial!!
    the data for other newbies is in>
    users/you/Library/Application Support/Iphone Simulator/(version 3.2 ipad, 4.01 iphone!)/Applications/NumberGenerated(click to see the xib name of your project of interest!)

    Thanks man wowww

  71. Ray Wenderlich (874 comments) says:

    @MaKo: Cool glad you found the tutorial helpful, and glad you got it working! :]

  72. AD (10 comments) says:

    Will you cover how to do Parent Child relationships and how to enforce them with Core Data, ie: Company -< Employees and how to do joins, etc.

  73. Richard (6 comments) says:

    I’m adding core data to an app that involves a starting view which creates a Tab Bar + Navigation View set up that has a UITableView. I think my difficulty in adding Core Data relates to adapting these two lines for it:

    FailedBanksListViewController *root = (FailedBanksListViewController *) [_navController topViewController];
    root.context = [self managedObjectContext];

    Where should I pass the managedObjectContext to if the UITableView isn’t generated right away in the App Delegate? Also, what is the point of the FailedBanksListViewController root you created? It’s not referenced anywhere else in your program.

    Thank you so much for this tutorial!

  74. Ray Wenderlich (874 comments) says:

    @AD: Thanks for the suggestion, added to the list!

    @Richard: Another strategy is to have your table view get the managedObjectContext from the Application Delegate, rather than the other way around.

    For your second question, note that code doesn’t create a FailedBanksListViewController, it just gets a reference to the one already created (via loading MainWindow.xib) so that it can set the managedObjectProperty property on it.

  75. Vincent (2 comments) says:

    Hi Ray,

    I have been fighting my way around xcode this weekend as a newbie and this was an excellent tutorial to get me up to speed on using core data and understanding it as well.

    The only thing I don’t get is this connecting outlet stuff. But guess I need to fight a little more on a Inteface Builder tutorial somewhere this week.

    Thanks so much!

  76. Vaish (1 comments) says:

    Thanks Ray, for such a good tutorial.

    In the initial sections of the tutorial, we log the Name and Zip values to check if the are giving the right answers. This works perfectly fine for the first time.

    But what happens the second time I run the project is that the values are printed again. So if I run the project 5 times after making some minor changes here and there, the debugger gives the NSLog output of Name and Zip 5 times in a row.

    I placed this code in the appdelegate didFinishLaunching method.

    Can you please explain the reason why is this so or am I doing something silly here?

    Thanks

  77. David DelMonte (4 comments) says:

    Ray, this was really helpful. THANKS.. I had the CD stuff working, but I didn’t really understand what I had made (using CoreBooks as a base and adding other methods).

    One question.. Now the app is working, how do you suggest populating the database – I have thousands of records to enter!

    Many thanks again

    David

  78. David DelMonte (4 comments) says:

    Sorry, one other issue.. I expected that – when I install on a test phone, that whatever test data was in the Simulator, would appear in the phone. This doesn’t happen..

    Is my expectation correct, and if so, now what did I do?

    Thanks again!

  79. Rene Pardon (1 comments) says:

    This is one of the best tutorials i’ve ever seen.
    Thank you for sharing this nice explanation. This helped me a lot, because i no longer have to use SQLite manually as i did before :)

    regards
    René

  80. Ray Wenderlich (874 comments) says:

    @Vincent: Cool! As far as connecting outlets goes, you might find this tutorial series helpful:

    http://www.raywenderlich.com/1797/how-to-create-a-simple-iphone-app-tutorial-part-1

    @Vaish: That’s because the way the code is written, it inserts the test data every time the app is run. So if it’s already there, it will be added again. If you don’t want that behavior, you could modify it to check if it’s there first before inserting or some such.

    @David: As far as populating the database goes, you can either create a Mac app to import the data from whatever format you may have (another DB, XML file, etc.), which is the best way but also a good amount of work, or I have an alternative method described here:

    http://www.raywenderlich.com/980/core-data-tutorial-how-to-preloadimport-existing-data

    As far as the Simulator/Phone goes, they have completely different file systems and they are not synchronized, so any test data that you add to the Simulator will not appear on the iPhone (unless you included it in the app itself).

    @Rene: Thanks so much for the kind words! :]

  81. David DelMonte (4 comments) says:

    “As far as the Simulator/Phone goes…”

    Got it. Thanks again Ray.. That’s why I need an desktop app to populate the app for distribution… hmmm..

    I have one more question. I’ve got my version of the CoreDataBooks app to work ok. I’m having a hard time figuring how to add another level.

    Using the CDB example, there is a root view and a detail view. But all items are in one table.

    I have books, authors and shops..

    a. Should I use three tables, as I would in mySQL, or XML or what..?

    In the CDB example, it’s easy to access the details by referring to the rootview.

    How do you refer to top view elements from a subview, or from a separate table?

    I really appreciate the help..

    David

    ps.. I will have about 2-400 top records, 1200-2400 detail records, and about 10 times that in lowest level records..

  82. Bartosz (1 comments) says:

    Hey

    It’s a very useful article.

    Do you know maybe how to add Core Data model file to a project using some Static Library? The thing is, that this Core Data model file, should be invisible for person, who is using this Static Lib?
    thanks for help

    cheers

  83. David DelMonte (4 comments) says:

    Hi again, moving forward, I have two tables much as in your tutorial. I would appreciate help – my head is in a knot – trying to figure out how to make the link between the two tables.

    Here, I’m selecting a row:

    if(indexPath.section == 1){
    switch (indexPath.row) {
    case 0:
    cell.textLabel.text = @”Present”;
    cell.detailTextLabel.text = verb.name;
    break;
    case 1:
    cell.textLabel.text = @”Past Simple”;
    cell.detailTextLabel.text = verb.simplePast;
    break;
    case 2:
    cell.textLabel.text = @”Past Continuous”;
    cell.detailTextLabel.text = verb.contPast;
    break;
    case 3:
    cell.textLabel.text = @”Future Simple”;
    cell.detailTextLabel.text = verb.simpleFuture;
    break;

    and here, I’m trying to use the row index to populate the next view:

    // Create and push the Person view controller.

    PersonsViewController *personsViewController = [[PersonsViewController alloc] initWithStyle:UITableViewStyleGrouped];
    Persons *selectedPerson = (Persons *)[[self fetchedResultsController] objectAtIndexPath:indexPath];

    NSLog(@”Section: %@”, indexPath.row);

    // Pass the selected person to the new view controller.
    personsViewController.person = selectedPerson;

    NSLog(@”Person: %@”, person );

    [self.navigationController pushViewController:personsViewController animated:YES];
    [personsViewController release];

    The NSLog test returns a null for person..

    If this is not the right place to post this, please forgive me.. Brain damage…

  84. Jad (1 comments) says:

    Hi Ray,
    Thanks for the great tutorial. I do have one question relating to your response to an earlier question:

    @Richard: Another strategy is to have your table view get the managedObjectContext from the Application Delegate, rather than the other way around.

    Is there any chance you could show us how this is achieved?

    Thanks again!

  85. liamk (1 comments) says:

    Just as a note if anyone else may have got stuck (like me) on the last step when we link the outlet to the navController.

    If you control-drag “FailedBankCD App Delegate” into “Navigation Controller” and nothing happens, it’s because the outlet hasn’t been identified yet by the Interface Builder. To fix this, simply hit the Build&Run button on Xcode, which will give you an error but force you to save the files you recently modified. You can ignore the error. After this, go back to the Interface Builder and try the control-drag procedure again, at which point you should now be able to do the last step.

    Hope it helps.

    Oh, and also thanks for a very well made Tutorial, Ray, I loved it :D

I'd love to hear your thoughts!