Posts Tagged ‘Development’

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!

iOS 8 Suggested Apps

October 15, 2014

New in iOS 8 are lock-screen suggested apps on iOS 8.    There has been much speculation on how there work, but no definitive confirmation from Apple, even in their forums.   I now have a request from a client to leverage these, which is difficult given the typical opacity from Apple.      It seems here is what is known:

1) Based on location (DUH)

2) Installed apps use a different suggestion mechanism than apps which aren’t installed.

3) Installed apps can pay attention to app established geofences, etc.  And if you’re within one of their registered geofences, (or an iBeacon range) then an app will appear on the lockscreen in the lower left.

4) Apps that haven’t been installed rely on some undisclosed Apple Algorithm, likely related to their correlation of an App, and their Maps data for that’s businesses locations.  There doesn’t appear to be anyway to influence Apple on this currently through the app submission process… However I have a few levers to pull to ferret this out.  This is the use case which I think most App developers are interest in, as it’s a novel way to promote an app apart from the chaos that is the app store.

5) Yes they can be turned off in Settings->iTunes & App Store->My Apps / App Store…     annoying that most the posts are on how to disable the feature, rather than how to leverage them.

6) Feature is enabled by default.

Will test this out in the next few days and report back.

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