Core Database Prefill

  • Tutorial
imageOften, for the iPhone / iPad applications to work, some “default” data set is needed in the database. Unfortunately, Apple does not provide standard tools for pre-populating the application database with developers.
If the required amount of data is small, then they can be loaded into the database at the start of the application. If you need a large amount of initial information for the application to work, then such a solution will not work, forcing users to wait until all loading operations are over is a bad idea, and the customer, seeing how long your application loads, can revise plans for future cooperation.

In this article I will tell you how to quickly populate the sqlite database of an application using Core Data.



The essence of the idea:
- populate the sqlite database
- add it to the application resources
(The database of the application running on the simulator is located here: / Users /% Username% / Library / Application Support / iPhone Simulator /% iOS version% / Applications /% GUID of the application% / Documents /)
- during The first launch of the application does not create a database, but replace it with a default one.

In this article, the database is populated directly in the application (from the xml file). Filling the database, of course, can be done in many other ways, but for this you need to understand the database scheme created by Core Data, common sense and laziness made me refuse such options.

I don’t like it when I have to scroll through an uninteresting description of the whole process in such articles, but I just want to immediately see the source code of the example. Therefore, I say right away:the source code of the sample application is here (SDK 4.1)

To see how the idea works, create a small application based on the Navigation-based Application template that uses Core Data. In my example, it is called CoreDataExample.


Prefill the database and use it at the first start.
Let the source data be stored in the Events.xml file. Add this file to the application resources.

    
    
    

Generate an Event class. To do this, right-click on the CoreDataExample.xdatamodel file and select Add-> New File ...


We will have the option to create a Managed Object Class.

Once it is created, Event.m and Event.h files will be generated:
//Event.h
#import 
 
@interface Event: NSManagedObject  
{
}
 
@property (nonatomic, retain) NSDate * timeStamp;
 
@end
 
//Event.m
#import "Event.h"
 
@implementation Event 
 
dynamic  timeStamp;
 
@end


Now create a class that will save data to the database. Let's call it EventsRepository, at the same time create the EventsRepositoryDelegate protocol. We will not change the way we work with the data in the classes generated by the template, this topic can be developed into several independent articles, but we will use EventsRepository for our prefill.
//EventsRepositoryDelegate.h
 
#import 
 
@protocol EventsRepositoryDelegate
 
@property (nonatomic, retain) NSFetchedResultsController * fetchedResultsController;
 
@end
 
//EventsRepository.m
 
#import 
#import "EventsRepositoryDelegate.h"
#import "Event.h"
 
 
@interface EventsRepository: NSObject {
 
id delegate
 
}
 
@property (assign) id delegate
- (void) saveEventWithTimeStamp: (NSDate *) timeStamp;
 
@end
 
//EventsRepository.m
 
#import "EventsRepository.h"
 
 
@implementation EventsRepository
 
@synthesize delegate;
 
- (void) saveEventWithTimeStamp: (NSDate *) timeStamp {
 
NSManagedObjectContext * context = [delegate.fetchedResultsController managedObjectContext];
 
Event * event = [NSEntityDescription insertNewObjectForEntityForName: @ "Event" inManagedObjectContext: context];
 
event.timeStamp = timeStamp;
 
NSError * error = nil;
if (! [context save: & error]) {
 
NSLog (@ "Unresolved error% @,% @", error, [error userInfo]);
abort ();
}
 
[event release];
}
 
@end
 
 


We also need a class that will parse our xml file and save data through the repository. Create the EventsXmlParser class and the EventsXmlParserDelegate protocol.
//EventsXmlParser.h
 
#import 
#import "EventsXmlParserDelegate.h"
#import "Event.h"
 
 
@interface EventsXmlParser: NSObject  {
 
id delegate
}
 
@property (assign) id delegate
 
- (void) saveEventWithTimeStamp: (NSDate *) timeStamp;
 
@end
 
//EventsXmlParser.m
 
#import "EventsXmlParser.h"
 
 
@implementation EventsXmlParser 
 
@synthesize delegate;
 
- (void) parser: (NSXMLParser *) parser 
didStartElement: (NSString *) elementName 
   namespaceURI: (NSString *) namespaceURI 
  qualifiedName: (NSString *) qualifiedName 
 attributes: (NSDictionary *) attributeDict {
 
if ([elementName isEqualToString: @ " ]) {
 
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat: @ "dd.MM.yyyy"];
 
NSDate * timeStamp = [[[[NSDate alloc] init] autorelease];
 
timeStamp = [dateFormatter dateFromString: (NSString *) [attributeDict objectForKey: @ "timeStamp"]];
 
[delegate.eventsRepository saveEventWithTimeStamp: timeStamp];
 
[dateFormatter release];
}
}
 
- (void) dealloc {
 
[super dealloc];
}
 
@end
 
//EventsXmlParserDelegate.h
 
#import 
#import "EventsRepository.h"
 
 
@protocol EventsXmlParserDelegate
 
@property (nonatomic, retain) EventsRepository * eventsRepository;
 
@end


It remains only to add the use of our new classes in the RootViewController.
Firstly, the RootViewController will implement both of our new protocols, and secondly, add the Property EventsRepository.
// the whole header file RootViewController.h
 
#import 
#import 
#import "EventsXmlParserDelegate.h"
#import "EventsRepositoryDelegate.h"
#import "EventsXmlParser.h"
#import "EventsRepository.h"
 
@interface RootViewController: UITableViewController  {
 
@Private
    NSFetchedResultsController * fetchedResultsController_;
    NSManagedObjectContext * managedObjectContext_;
EventsRepository * eventsRepository_;
}
 
@property (nonatomic, retain) NSManagedObjectContext * managedObjectContext;
@property (nonatomic, retain) NSFetchedResultsController * fetchedResultsController;
@property (nonatomic, retain) EventsRepository * eventsRepository;
 
@end
 
// and what you need to remember to add to RootViewContorller.m for property eventsRepository
...
@synthesize eventsRepository = eventsRepository_;
...
- (EventsRepository *) eventsRepository {
 
if (eventsRepository_! = Nil) {
        return eventsRepository_;
    }
 
eventsRepository_ = [[EventsRepository alloc] init];
eventsRepository_.delegate = self;
 
return eventsRepository_;
}
...
 
- (void) dealloc {
   [eventsRepository_ release];
    [fetchedResultsController_ release];
    [managedObjectContext_ release];
    [super dealloc];
}


Prefill the base. To do this, add the populateDataBase method to the RootViewController and call it on viewDidLoad.
- (void) populateDataBase {
 
NSLog (@ "populate");
 
NSURL * url = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource: @ "Events" ofType: @ "xml"]];
NSXMLParser * xmlParser = [[NSXMLParser alloc] initWithContentsOfURL: url];
 
EventsXmlParser * eventsXmlParser = [EventsXmlParser new];
[eventsXmlParser setDelegate: self];
 
[xmlParser setDelegate: eventsXmlParser];
 
BOOL success = [xmlParser parse];
 
if (success)
NSLog (@ "Data Base has been populated");
else
NSLog (@ "Error: Data Base hasn't been populated");
 
[eventsXmlParser release];
[xmlParser release];
}
 


Run the application - the database will be full. Copy the created database to the application directory and add the database file to the project (for example, to the Resources group) (I remind you where it is / Users /% Username% / Library / Application Support / iPhone Simulator /% iOS version% / Applications /% GUID of the application% / Documents /) and remove the populateDataBase function call - we no longer need to prefill the base of their xml file. Let's remove the application from the simulator, for this you can either bang the application folder in / Users /% UserName% / Library / Application Support / iPhone Simulator /% iOS version% / Applications /, or delete it through the simulator (it is deleted on the device) - hold down the left button click on the application icon, release it, when the icon starts to oscillate, click on the appeared cross - delete.

It remains for us to tell our application not to create the database at the first start, but to copy the database that we created in its place.
To do this, go to the CoreDataExampleAppDelegate class and replace the line in the getter persistentStoreCoordinator getter
NSURL * storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @ "CoreDataExample.sqlite"]];

on the
NSString * storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @ "CoreDataExample.sqlite"];
 
NSFileManager * fileManager = [NSFileManager defaultManager];
 
if (! [fileManager fileExistsAtPath: storePath]) {
 
NSString * defaultStorePath = [[NSBundle mainBundle] pathForResource: @ "CoreDataExample" ofType: @ "sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath: defaultStorePath toPath: storePath error: NULL];
}
}
 
NSURL * storeURL = [NSURL fileURLWithPath: storePath];


Hope this way helps you. I will be glad to see in your comments your solutions to such problems.


Also popular now: