Posts Tagged ‘Xcode’

iOS apps with initial Core Data database image

March 12, 2015

Frequently, it’s useful to ship an iOS app with a preconfigured CoreData database, so that on initial launch the data doesn’t need to be retrieved from a webservice before presenting it to the user / or immediately loaded from a plist shipping with the file.

This used to be easy pre iOS 7.0:   Launch in the simulator and ensure you’ve populated the datastore with the data you want to chip.   Then find the sqlite3 datafile on your mac.   That’s easily done with a breakpoint in the persistentStoreCorrdinator:

FindingTheDbImage

Then copy it into your project

BINGOSABI:bingosabi$ cd /Users/bingosabi/Library/Developer/CoreSimulator/Devices/
7FD9730C-F002-414D-9F72-8D6BAA750A3B/data/Containers/Data/Application/0A3AB0EE-
D557-4FE7-9683-3C337C64A7E9/Documents/db.sqlite
BINGOSABI:Documents$ cp db.sqlite ~/Projects/ExampleApp/Resources

 

In iOS 7.0 however, but Apple updated the shipping version of sqlite3 which enabled a separate journaling file.   Now a database has three files.

BINGOSABI:Documents bingosabi$ ls
db.sqlite	db.sqlite-shm	db.sqlite-wal

So in order to ship a populated databases we have to do a few extra steps.

sqlite> pragma journal_mode=off;
off
sqlite> vacuum
sqlite> .exit

Copy ONLYthe vacuumed db.sqlite to the Xcode project and include it.

BINGOSABI:bingosabi$ cd /Users/bingosabi/Library/Developer/CoreSimulator/Devices/
7FD9730C-F002-414D-9F72-8D6BAA750A3B/data/Containers/Data/Application/0A3AB0EE-
D557-4FE7-9683-3C337C64A7E9/Documents/db.sqlite
BINGOSABI:Documents$ cp db.sqlite ~/Projects/ExampleApp/Resources

If you used the default template for a Core Data app, then replace the default persistentStoreCorrdinator with the following code: (Note that you have to always touch that anyways if you’re submitting to the store due to the abort(); poison pill which Apple has in the template. Not removing the abort(); is a rejection-able offense.

So replace this:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    // Create the coordinator and store
    
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"db.sqlite"];
    NSError *error = nil;
    NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        // Report any error we got.
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey] = failureReason;
        dict[NSUnderlyingErrorKey] = error;
        error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();   //POISON PILL
    }
    
    return _persistentStoreCoordinator;
}

with this:

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    //This is required for cached data lest Apple reject for immediately writing data to Documents which isn't user data and has default iCloud backup attribute.
    NSError *error = nil;
    BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                  forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}

- (void) copyDatabaseFromBundle:(NSURL *)storeURL {
    NSError *error = nil;
    NSString *databaseName = [[storeURL lastPathComponent] stringByDeletingPathExtension];
    NSString *databaseExtension = [storeURL pathExtension];
    NSURL *shippingDatabaseImage = [[NSBundle mainBundle] URLForResource:databaseName withExtension:databaseExtension];
    
    NSURL * toURL = [[[self applicationDocumentsDirectory] URLByAppendingPathComponent:databaseName] URLByAppendingPathExtension:databaseExtension];
    
    [[NSFileManager defaultManager] copyItemAtURL:shippingDatabaseImage toURL:toURL  error:&error];
    
    [self addSkipBackupAttributeToItemAtURL:toURL];
}

- (void) deleteDatabase:(NSURL *)storeURL {
    //Delete all the write ahead log files. 
    [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
    [[NSFileManager defaultManager] removeItemAtURL:[NSURL URLWithString:[storeURL.absoluteString stringByAppendingString:@"-wal"]] error:nil];
    [[NSFileManager defaultManager] removeItemAtURL:[NSURL URLWithString:[storeURL.absoluteString stringByAppendingString:@"-shm"]] error:nil];
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (!_persistentStoreCoordinator) {
        NSManagedObjectModel *model = [self managedObjectModel];
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            // Create the coordinator and store
            _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
            NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"db.sqlite"];
            NSError *error = nil;
            NSString *failureReason = @"There was an error creating or loading the application's saved data.";
            
            if ([storeURL checkResourceIsReachableAndReturnError:&error] == NO) {
                [self copyDatabaseFromBundle:storeURL];
            }
            
            //Handle lightweight version migration
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                     [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
            
            if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
                
                //Just Start Over.
                [self deleteDatabase:storeURL];
                
                if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
                    
                    // Report any error we got.
                    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                    dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
                    dict[NSLocalizedFailureReasonErrorKey] = failureReason;
                    dict[NSUnderlyingErrorKey] = error;
                    error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
                    
                    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                }
            }
        });
        
    }
    
    return _persistentStoreCoordinator;
}

C’est tout!

Xcode Shortcuts FTW!!!

December 30, 2013

Best shortcut ever in Xcode:   ctrl-command Up Arrow   (Toggles to .h or .m file in editor)    Also Awesome… add Option into the mix to turn the secondary editor back into “Counterpart” mode.    So…    ctrl-option-command-uparrow