RSS

Core Data Basics Part 5 – Preloading Data

21 Feb


Introduction

Pre-loading default data into Core Data can be as simple or difficult as you make it. Some factors that may or may not complicate the task are:

  • What format the data is currently in
  • How much data you need to import

The format of the existing data is the biggest issue.  If it’s in database format your import code will be different than if it is in xml format. If it’s just a small list of things in text format then you can probably skip the whole import business and copy/paste your data directly into your code. If you want to know how to parse XML you should read and apply principles covered in Game Template Part 3 – Game Data Persistence.  I’m going to focus on where and how to write data from code into Core Data.

Prerequisites

We follow on from Part 4, so download the project from the end of Part 4 then extract and open it with Xcode.

Inserting Default Data

To insert default data we need to detect when the default import is required. One way to do that is to to select a count of items within a particular entity we’re interested in.  The most appropriate place for this check is just after Core Data has been set up – i.e. when the Persistent Store & Object Model & Context are ready.  Typically this is in AppDelegate.m so insert the following code at the top of the existing didFinishLaunchingWithOptions method:

    [self setupFetchedResultsController];
 
    if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
        NSLog(@"!!!!! ~~> There's nothing in the database so defaults will be inserted");
        [self importCoreDataDefaultRoles];
    }
    else {
        NSLog(@"There's stuff in the database so skipping the import of default data");
    }

Obviously you’re going to need the setupFetchedResultsController method so also add the following above the existing didFinishLaunchingWithOptions method. You should be familiar with setupFetchedResultsController as we have used it a few times already. I’ve customised it to fetch the Role entity:

- (void)setupFetchedResultsController
{
    // 1 - Decide what Entity you want
    NSString *entityName = @"Role"; // Put your entity name here
    NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entityName);
 
    // 2 - Request that Entity
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
 
    // 3 - Filter it if you want
    //request.predicate = [NSPredicate predicateWithFormat:@"Person.name = Blah"];
 
    // 4 - Sort it if you want
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name"
                                                                                     ascending:YES
                                                                                      selector:@selector(localizedCaseInsensitiveCompare:)]];
    // 5 - Fetch it
    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:self.managedObjectContext
                                                                          sectionNameKeyPath:nil
                                                                                   cacheName:nil];
    [self.fetchedResultsController performFetch:nil];
}

The other thing you need now are methods to import all the defaults. In these methods you could set up a whole bunch of stuff to parse and import from DB/XML/whatever. Your data mapping is really up to you to customise as you need it. You’ll get the idea from this below code, which you should paste above the setupFetchedResultsController method:

- (void)insertRoleWithRoleName:(NSString *)roleName
{
    Role *role = [NSEntityDescription insertNewObjectForEntityForName:@"Role"
                                               inManagedObjectContext:self.managedObjectContext];
 
    role.name = roleName;
 
    [self.managedObjectContext save:nil];
}
 
- (void)importCoreDataDefaultRoles {
 
    NSLog(@"Importing Core Data Default Values for Roles...");
    [self insertRoleWithRoleName:@"C/C++ Developer"];
    [self insertRoleWithRoleName:@"Obj-C Developer"];
    [self insertRoleWithRoleName:@"Java Developer"];
    [self insertRoleWithRoleName:@"ASP.NET Developer"];
    [self insertRoleWithRoleName:@"Unix Engineer"];
    [self insertRoleWithRoleName:@"Windows Engineer"];
    [self insertRoleWithRoleName:@"Business Analyst"];
    [self insertRoleWithRoleName:@"Infrastructure Manager"];
    [self insertRoleWithRoleName:@"Project Manager"];
    [self insertRoleWithRoleName:@"Operations Manager"];
    [self insertRoleWithRoleName:@"Desktop Support Analyst"];
    [self insertRoleWithRoleName:@"Chief Information Officer"];
    NSLog(@"Importing Core Data Default Values for Roles Completed!");
}

Those methods we’ve just added will be giving you a bunch of errors so you’ll need to add the following to AppDelegate.h

#import <CoreData/CoreData.h>
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

Also add the following to AppDelegate.m

@synthesize fetchedResultsController = __fetchedResultsController;

Testing the Import

If you’ve already run the Staff Manager app once (and have entered Roles into the Simulator) then you will need to click iOS Simulator > Reset Content and Settings to ensure that there is no data in the database.  Once you’ve done that run the app and you should see default roles when you select the Roles tab. Note also the log entries telling you that defaults were inserted.  They should only fire once!

That’s it for this tutorial.  I know it was a pretty short one however I wanted you to focus on just getting data  into Core Data from code.

Here’s the complete source code so far

If you liked this tutorial or found something wrong with it please let me know!

If you want to support my work and have an iPad please consider purchasing iSoccer *wink*

-Tim

Go to Part 6 or the Tutorials Index


Be Sociable, Share!
     

    About Tim Roadley

    I'm a senior analytics software consultant at eMite Pty Ltd primarily focused on delivering business intelligence dashboards, currently for one of Australia’s major banks. I'm also working on a revamped version of eMite's iOS App for release under iOS 7. Prior to eMite, I was Infrastructure Manager at Cuscal Pty Ltd where I was heavily involved in designing and implementing a payments switch that drives 1300+ ATM’s Australia wide. I have several apps on the App Store, including Teamwork, iSoccer and now Grocery Dude and Grocery Cloud. In my down time I enjoy spending time with my wonderful wife Tracey and two lovely children Tyler and Taliah.
    20 Comments

    Posted by on February 21, 2012 in iOS Tutorials

     

    20 Responses to Core Data Basics Part 5 – Preloading Data

    1. Erasmo Pinheiro

      February 22, 2012 at 4:07 am

      Hi Tim,
      Thanks a lot.

      I’m studing CoreData for developer my first App.
      I was getting concepts piece by piece in many sites.
      All I have studied is in your very good tutorial.

      I have some concerns about leave my NSManagedObjectContext opened (kind of) while users enter in background mode.
      Do you know about something ? If it’s cause some batery lacks ?
      Thanks a lot

       
    2. Tim Roadley

      February 23, 2012 at 7:14 am

      Erasmo,

      I’m glad you like the tutorial. As far as I’m aware keeping NSManagedObjectContext open when the app enters the background doesn’t use any additional battery. Only things that stay active in the background would consume more battery I think. If you can find some further information around about this I’d be interested to learn otherwise!

      Cheers

       
      • zagoox

        February 8, 2013 at 8:09 am

        Love your tutorial! how create a UILocalizedIndexedCollation on person?

         
    3. adrian phillips

      February 27, 2012 at 2:26 am

      once a gain great job. it is quiet descriptive when it comes to core data. thanks again for taking the time to write this tutorial.

      adrian

       
    4. Chris

      March 14, 2012 at 11:11 pm

      Hi Tim,

      you are missing the “insertRoleWithRoleName:” method in the guide. Otherwise, it’s great.

      Regards,
      Chris

       
      • Tim Roadley

        March 17, 2012 at 8:20 pm

        Oh thanks for pointing that out, fixed!

         
    5. Johnny

      March 26, 2012 at 12:17 pm

      I’m trying to adopt your preloading for multiple attributes on the same entity. Can you point me to any sources that would allow me to expand on your work?

      I’m using custom table view cells, each with four labels and I can’t seem to wrap my head around the logic (I’m a web designer by trade).

      Thanks for these great tuts – my knowledge has grown by leaps and bounds!

      Take care and thanks again.

       
    6. Graham Gardiner

      May 8, 2012 at 2:24 am

      Hi Tim

      Fantastic tutorial really well laid out thank you very much.
      I have a question How would I link the Name to more than one Role.
      ie: Name: Graham Gardiner: Role: Taxi Driver, Car wash director, Panel beater
      So The name would be on a label at the top and the roles would be listed on a table below. Can you help or give me any info

      Thanks and regards
      Graham

       
    7. Swatee

      June 30, 2012 at 4:56 pm

      Hey thanx alot.. I m always confused to use to which database to be add in my app tht coredata or sqlite..
      But due to this post got relax to use coredat can be preloaded…
      Again thanx…

       
    8. TickTack

      June 30, 2012 at 6:51 pm

      No need to “iOS Simulator > Reset Content and Settings”. Just run the app, stop it and in simulator, delete applike you normally do on device. Hold icon and once it starts shaking, tap the black cross to delete. Once you run the app again, it will not have any data in CoreData database and import defaults :-)

       
    9. Propilotapps

      March 5, 2013 at 12:14 am

      Thank you for the tutorial. How can I preload data from a plist? Thank you.

       
    10. eyestach

      November 6, 2013 at 8:10 am

      Migrating some but not all data from new app version’s Database to existing Database.

      Hi Tim!

      I have pre-ordered your new book, and am eager to read it. I can only imagine your frustration from waiting for its appearance.

      I hope I have chosen the right thread to post this.

      My Core Data App ships with an Sqlite database file.
      Version 1.0 has a database with 10 recipes.
      Version,say 2.0 is shipped with 20 recipes.
      Each recipe has property idNum. The highest Recipe idNum for version 1.0 is 10.
      The highest idNum for version 2.0 is twenty.
      Therefore I want to only move the 10 recipes with idNum > 10 for the new DB to the old DB.

      When V:2.0 is installed, the local sqlite store is in the ApplicationSupport area
      and in Resources/ here is the new database file with 20 recipes, with 10 or them being new
      recipes. I want the 10 new recipes in the new db to move from the new db to the old db.
      The old DB is the one that has copied from Resources/ to AppSupportArea/ when version 1.0
      was installed. There is a singleton Entity named VersionXML in the datamodel. This entity
      has an attribute number , a float, which contains the data version number, e.g.. 1.13

      The datamodel has undergone no change between versions.

      The data for the recipes originates in XML. I made a Mac app that parses the XML file and creates an Sqlite DB.

      This is the method I have used at app version upgrade install time, to avoid the possibility of duplicates being put in the old DB. I also need to respect the user’s expectation that their edits to my preloaded recipes will not be overwritten by my original versions at the time of installation of a new version of the app.

      1. Setup two stack, one for each of the two DB’s, the new db and the old db.
      2. Obtain versionXML.num from both the new and old databases.
      3. Do a fetch to obtain the highest recipe id number in the old database.
      3. Based on a change between the two version numbers , move the 10
      new recipes, from new db to old db. This movement is accomplished by:

      -fetch all recipes into and array from new DB in Resources/.
      -pass through recipeArray and insert a clone of the newRecipe into
      the MOC for the old DB
      if (recipeInNewDb.idNum > greatestIdNumInOldDB)
      then do : [mocOld save:];

      Recipe has a one to many property categories, but the categories are the same
      for both new and old, so the Recipe clone can be pointed to pre-exiting category(s) in
      the oldMOC.

      This procedure works, but it seems cumbersome, maybe wanky.

      Is there a way to do this same thing using migration methods.
      I intuit/guess the way would be custom migration using NSEntityMigrationPolicy.
      I believe the policy for Recipe would somehow contain the ‘code” for

      if (recipeInNewDb.idNum > greatestIdNumInOldDB)

      I want the new version to support iCloud. I think I will have to add the old store to the PSC with non-iCloud options and
      then do the migration of new recipes, then delete the oldDB from the PSC and then re-add it with iCloud options. Or?

      Can you please point me towards sample code/tutorial for this? I can’t seem to find the sample code I need. Is it covered in your upcoming book? I have read
      Scott Gardner’s tutorial on “How to Perform a Lightweight Core Data Migration” at Wenderlich’s site. I have studied the Apple Docs, but apparently not enough.

      Many Thanks for all you do for us readers, Mark

       
      • Tim Roadley

        November 6, 2013 at 8:28 am

        Hello!

        Sounds like you need to use deep copy (chapter 9). A deep copy can be used to copy unique objects from one persistent store to another, including all of the related objects.

        Did you pre-order on InformIT? If you did then you have instant access to the final version on Safari Books Online today! If not, perhaps see if you can get a free trial at safaribooksonline.com

        Cheers,

        Tim

         
      • pjv12

        April 29, 2014 at 6:00 am

        eyestach, did you ever solve this? I have the exact same problem in my app as you did. If you can point me in the right direction I would be most grateful.

         
    11. eyestach

      November 6, 2013 at 9:33 am

      Hi Tim,
      It looks like I will have to wait a bit. See the chat I had at Safari Books Online
      All the best, Mark

      Kristina K: Hi Mark
      You: Hi Kristina, Learning Core Data for iOS by Tim Roadley I pre-ordered this book close to one week ago from InformIT. Tim wrote me the following today: Did you pre-order on InformIT? If you did then you have instant access to the final version on Safari Books Online today! If not, perhaps see if you can get a free trial at safaribooksonline.com How to I go about downloading this book now? Thanks so much.
      Kristina K: One moment while I look into that for you

      You: Thanks so much.
      Kristina K: One moment while I look into that for you
      Kristina K: It appears you spoke with Bonnie earlier in regards to this correct?
      You: Yes , that is correct. She indicated she could not help me.
      Kristina K: In regards to this that is correct. You will need to contact InformIT in regards to this.
      Kristina K: I do apologize for that, but they will be able to assist you in what you are asking.
      You: I really appreciate your help here!
      Kristina K: You are very welcome
      Kristina K: InformIT is who you will need to contact. We are not the publisher, nor do we own the content.
      Kristina K: You bought the material through InformIT, we do not sell books or content we are a online resource library
      You: Can I do the following here at Safari Books Online, 1. Sign up for free trial 2. Download the book 3. Cancel the free trial
      Kristina K: No, We do not have downloads, we do have offline caching through our mobile app
      You: Thanks so much Kristina
      Kristina K: If in any way there was a coupon code or something in the email you were given to have access to the book, that is different.

       
      • Tim Roadley

        November 6, 2013 at 9:36 am

        Hi Mark,

        From that conversation you can access an online version book, just not download it?

        It’s probably the best thing you can do until 23rd Nov when it’s released as an ebook.

        Cheers,

        Tim

         
    12. onram (@onesimoramosm)

      May 26, 2014 at 3:22 pm

      Great tutorial. Thanks

       

    Leave a Reply