Some prerequisites before you start to import SQLite database into Core Data
- whatever sqlite addon you use for your projects, the method expects an NSDictionary with keys (sqlite column names) and the corresponding values.
- ZAssert is provided by Marcus S. Zarra (www.cimgf.com )
Here are the rules the method counts on
1. with some exceptions (see below) core data entity properties/sqlite column name must have the same
2. the values stored in sqlite must be convertible to the datatype expected by core data
Name exceptions for core data properties
1. properties of type NSString with the name = exampleNormalized
You might want to store a string value with its normalized pendant for searching purposes. Here exampleNormalized uses a method of NormalizedStringTransformer to
convert a string into its normalized form.
- (void)setExampleNormalized:(id)value
{
[self willChangeValueForKey:@"exampleNormalized"];
[self setPrimitiveExampleNormalized:[NormalizedStringTransformer normalizeString:value]];
[self didChangeValueForKey:@"exampleNormalized"];
}
In that case the method will look for a sqlite key with the extension “Normalized” stripped off and will use that value to set exampleNormalized.
2. A property with a name exampleURI stores the persistent objects URI as string value. So it acts as a relationship without using the normal core data relationship
management.
As those values can’t be provided by sqlite you have to prepare a separate dictionary (relations) containing the values.
3. NSDateAttributeType properties must end with the word “Time” respectively “Date”. For both values separate formatters can be used to convert the string value into an NSDate object.
If the method can’t find a corresponding sqlite value and the property is not optional an exception is raised.
//
// AppDelegate+ImportDB.m
// LogBook
//
// Created by Bernd Rabe on 16.09.10.
// Copyright 2010 RABE_IT Services. All rights reserved.
//
// Helper
#import "NSDateFormatter+DateFromStringExtension.h"
#import "NSManagedObject+ToolBox.h"
#import "NSEntityDescription+Toolbox.h"
// Main
#import "AppDelegate+ImportDB.h"
#import <CoreLocation/CoreLocation.h>
#import "SQLiteAccess.h"
@implementation AppDelegate (ImportDB)
#pragma mark - Common Entity Import
/*
properties - read from sqlite database (keys are value)
be aware of the fact, that the following method only works correctly
if the property names to be inserted in core data correspond to each other
except property names ending with "URI"
initialize will try to find a corresponding relation object by removing the URI part on the core data side
exception: properties of type NSString ending with Normalized
used for search in conjunction with NormalizedStringTransformer.h/.m
we look for a sqlite
breaks if an not optional property is not initialized
exception: property names ending of Type NSDateAttributeType with "Date" or "Time"
ending with Date expects a date in the format specified in Definitions.h (here yyy-MM-dd)
ending with Time expects a time in the format specified in Definitions.h (here HH:mm)
*/
- (void)initializeManagedObject:(NSManagedObject *)managedObject withEntityDescription:(NSEntityDescription *)entityDescription
properties:(NSDictionary *)properties andRelations:(NSDictionary *)relations
{
NSString *value, *valueTransformerClassName;
NSRange range;
NSAttributeDescription *desc = nil;
BOOL isOptional = YES, propertyExists = YES;
NSDictionary *allProperties = [entityDescription attributesByName];
// NSPredicate *predicateFindAllStringsWithoutURI = [NSPredicate predicateWithFormat:@"self matches '.{1,}(?<!URI)$'"];
// NSArray *filteredPropertiesWOURI = [[allProperties allKeys] filteredArrayUsingPredicate:predicateFindAllStringsWithoutURI];
for (NSString *key in allProperties) {
ZAssert(!(!isOptional && !propertyExists), @"Property %@ in %@ is not optional!\n%@", [desc name], [entityDescription managedObjectClassName], properties);
desc = [allProperties valueForKey:key];
isOptional = [desc isOptional], propertyExists = YES;
switch ([desc attributeType]) {
case NSInteger16AttributeType:
case NSInteger32AttributeType:
case NSInteger64AttributeType:
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:[NSNumber numberWithInteger:[value integerValue]] forKey:[desc name]];
}
else {
propertyExists = NO;
}
break;
case NSDoubleAttributeType:
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:[NSNumber numberWithDouble:[value doubleValue]] forKey:[desc name]];
}
else {
propertyExists = NO;
}
break;
case NSFloatAttributeType:
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:[NSNumber numberWithFloat:[value floatValue]] forKey:[desc name]];
}
else {
propertyExists = NO;
}
break;
case NSStringAttributeType:
// if the name contains Normalized we wanna
// use hte name without "Normalized" and process that
// value here
range = [[desc name] rangeOfString:@"Normalized"];
if (range.location != NSNotFound) {
value = [properties valueForKey:[[desc name] substringToIndex:range.location]];
if (value && [value isKindOfClass:[NSString class]]) {
[managedObject setValue:value forKey:[desc name]];
}
else {
propertyExists = NO;
}
}
else {
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:value forKey:[desc name]];
}
else {
propertyExists = NO;
}
}
break;
case NSBooleanAttributeType:
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:[NSNumber numberWithBool:[value boolValue]] forKey:[desc name]];
}
else {
propertyExists = NO;
}
break;
case NSDateAttributeType:
value = [properties valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
NSInteger found = [[desc name] rangeOfString:@"Date" options:NSLiteralSearch range:NSMakeRange([[desc name] length] - [@"Date" length], [@"Date" length])].location;
if (found != NSNotFound) {
[managedObject setValue:[NSDateFormatter dateFromString:value withFormat:kDateFormat] forKey:[desc name]];
}
else {
found = [[desc name] rangeOfString:@"Time" options:NSLiteralSearch range:NSMakeRange([[desc name] length] - [@"Time" length], [@"Time" length])].location;
if (found != NSNotFound) {
[managedObject setValue:[NSDateFormatter timeFromString:value withFormat:kTimeFormat] forKey:[desc name]];
}
else {
propertyExists = NO;
}
}
}
else {
propertyExists = NO;
}
break;
case NSTransformableAttributeType:
value = [properties valueForKey:[desc name]];
if (![value isKindOfClass:[NSNull class]]) {
valueTransformerClassName = [desc valueTransformerName];
NSInteger foundClass = [valueTransformerClassName rangeOfString:@"Number"].location;
if (foundClass != NSNotFound) {
NSInteger foundDescription = [valueTransformerClassName rangeOfString:@"Double"].location;
if (foundDescription != NSNotFound) {
NSNumber *number = [NSNumber numberWithDouble:[value doubleValue]];
[managedObject setValue:number forKey:[desc name]];
}
}
else {
foundClass = [valueTransformerClassName rangeOfString:@"String"].location;
if (foundClass != NSNotFound) {
[managedObject setValue:value forKey:[desc name]];
}
}
}
if (!isOptional && [managedObject valueForKey:[desc name]] == nil) {
propertyExists = NO;
}
break;
default:
break;
}
}
if (relations) {
isOptional = YES, propertyExists = YES;
NSPredicate *predicateFindURIString = [NSPredicate predicateWithFormat:@"self matches '.{1,}URI$'"];
NSArray *relationArray = [[allProperties allKeys] filteredArrayUsingPredicate:predicateFindURIString];
for (NSString *relation in relationArray) {
ZAssert(!(!isOptional && !propertyExists), @"Relation %@ in %@ is not optional!\n%@", [desc name], [entityDescription managedObjectClassName], relations);
desc = [allProperties valueForKey:relation];
isOptional = [desc isOptional], propertyExists = YES;
switch ([desc attributeType]) {
case NSStringAttributeType:
value = [relations valueForKey:[desc name]];
if ([value isKindOfClass:[NSString class]]) {
[managedObject setValue:value forKey:[desc name]];
}
else {
propertyExists = NO;
}
break;
default:
break;
}
}
}
}
#pragma mark - Init Default Database
- (void)initDefaultDB
{
NSString *indexString = nil;
// Initialize Continent/Country/SubDivision Entities Continent Init
NSArray *continents = nil, *countries = nil, *subDivisions = nil;
NSEntityDescription *continentEntityDesc = [NSEntityDescription entityForName:kClassContinent inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *countryEntityDesc = [NSEntityDescription entityForName:kClassCountry inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *subDivisionEntityDesc = [NSEntityDescription entityForName:kClassSubDivision inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *airportEntityDesc = [NSEntityDescription entityForName:kClassAirport inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *licenceTypeEntityDesc = [NSEntityDescription entityForName:kClassLicenceType inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *ratingEntityDesc = [NSEntityDescription entityForName:kClassRating inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotEntityDesc = [NSEntityDescription entityForName:kClassPilot inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *manufacturerEntityDesc = [NSEntityDescription entityForName:kClassManufacturer inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *attendantEntityDesc = [NSEntityDescription entityForName:kClassAttendant inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotMedicalEntityDesc = [NSEntityDescription entityForName:kClassPilotMedical inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotLicenceEntityDesc = [NSEntityDescription entityForName:kClassPilotLicence inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotAircaftEntityDesc = [NSEntityDescription entityForName:kClassPilotAircraft inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotFlightLogEntityDesc = [NSEntityDescription entityForName:kClassPilotFlightLog inManagedObjectContext:self.dataModel.moc];
NSEntityDescription *pilotOthersEntityDesc = [NSEntityDescription entityForName:kClassPilotOthers inManagedObjectContext:self.dataModel.moc];
continents = [[NSArray alloc] initWithArray:[self runSelectManyRowsWithSQL:@"SELECT * FROM Continents ORDER BY name;"]];
for (NSDictionary *continentItem in continents) {
Continent *newContinent = [NSEntityDescription insertNewObjectForEntityForName:kClassContinent
inManagedObjectContext:self.dataModel.moc
assignPersistentStoreSkipDefaultConfiguration:YES];
[self initializeManagedObject:newContinent withEntityDescription:continentEntityDesc properties:continentItem andRelations:nil];
}
[self.dataModel saveContext];
#pragma mark - Helper
- (NSPredicate *)predicateForPropertiesStartingWith:(NSString *)identifier
{
NSDictionary *variables = [[NSDictionary alloc] initWithObjectsAndKeys:identifier, @"IDENTIFIER", nil];
static NSPredicate *predicate = nil;
if (predicate == nil) {
predicate = [NSPredicate predicateWithFormat:@"self BEGINSWITH $IDENTIFIER"];
}
NSPredicate *findPredicate = [predicate predicateWithSubstitutionVariables:variables];
return findPredicate;
}
#pragma mark - SQLiteAccess
- (NSArray *)runSelectManyValuesWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess selectManyValuesWithSQL:query];
}
- (NSString *)runSelectOneValueWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess selectOneValueSQL:query];
}
- (NSArray *)runSelectManyRowsWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess selectManyRowsWithSQL:query];
}
- (NSDictionary *)runSelectOneRowWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess selectOneRowWithSQL:query];
}
- (NSNumber *)insertWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess insertWithSQL:query];
}
- (void)updateWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess updateWithSQL:query];
}
- (void)deleteWithSQL:(NSString *)query
{
DLog(@"%@", query);
return [SQLiteAccess deleteWithSQL:query];
}
@end
Some prerequisites before you start to import SQLite database into Core Data - whatever sqlite addon you use for your projects, the method expects an NSDictionary with keys (sqlite column...