1

As many of you, I have to delete my application in the simulator in order to reinstall a fresh version, where I update my database. Otherwise, it crashes with or without debug log (if it's Core Data, the debug log is pretty self explanatory).

My question is : since users won't probably delete their applications before updating through the app store, and since they'll most definitely want their "old" data after the upgrade,

Do we have to develop a database migration script, or check in the applicationDidLaunch if the database changed, at every launch ?

Thanks !

westsider
  • 4,967
  • 5
  • 36
  • 51
Thomas Joulin
  • 6,590
  • 9
  • 53
  • 88

2 Answers2

3

I would definitely give you a 1-Star if my Data is lost after an update.
Core-Data versioning and migration is set up in a few minutes, at least the light-weight-part.

Have a look at this answer to as similar question

Community
  • 1
  • 1
Matthias Bauch
  • 89,811
  • 20
  • 225
  • 247
  • then I won't tell you my app name ^^ No, seriously, if i'm asking, it's because I care about this possibility. I will definitely check you link out, but for the moment, I will build a script, since it's an sqlite app – Thomas Joulin Nov 10 '10 at 14:26
  • +1 What he said is true. You don't need - (and couldn't probably even run) a "script" to do the migration. But Core Data has stuff in it to help assist you in doing this type of version-to-version migration. And yes, I'd be royally p*ssed if I owned your app and lost my data when doing an update... PS Don't automatically trust the answer that begins with "I have not personally used CoreData so have no idea if it has special tools for handling this" – Brad Nov 10 '10 at 14:58
  • I read this question as being a general question on migration handling, not how to accomplish this in CoreData. But, since it was a bit ambiguous I prefaced it with a 'this answer doesn't apply to CoreData' comment. According to the OPs last comment he isn't, in fact, using CoreData. And yet you ding my answer? – aepryus Nov 10 '10 at 15:14
  • I'll unding it just for civility - but how would you propose he even writes a migration "script" on the iPhone? (P.S. Tried to - but vote is "locked in" unless you edit the response.) – Brad Nov 10 '10 at 15:40
  • I upvoted this answer. To be faire, aepryus answer applied exactly to my application : an sqlite-based app that needed to handle multiple version of the database. My "script" is in fact a sqlite3_exec() of some ALTER statements. For my other apps that uses Core Data, I will check out fluchtpunkt link (and thanks again) – Thomas Joulin Nov 10 '10 at 16:08
  • By "script" I just meant sequence of SQL commands. I have seen the delta strings stored as String constants in the code, but I personally just create the deltas as SQL files and store them as resources in the project. When applying the deltas, I read in the SQL files and execute the commands via SQLite. – aepryus Nov 10 '10 at 16:12
1

I have not personally used CoreData so have no idea if it has special tools for handling this. But, in general yes, you need to create and store SQL scripts that will migrate from each version of your app to the next.

When the app is installed, it should check the current version of the data model against the version of the data model used by the app. There should be a "delta" script to move from each version to each next version. Each of the deltas necessary to move from the current model to the necessary model should then be applied.

For example, if you have submitted 4 versions to the AppStore. You need a delta script to get from version 1 to 2, from 2 to 3 and from 3 to 4.

Each time the app launches it checks the current version of the data model against the executables desired version. If, for example, version 4 is installed on a device, but the user had never installed version 3, the app would launch. Check the current version of the model, which is 2. Compare it with the executable's version which is 4. And then apply the 2 to 3 script and then the 3 to 4 script.

The version number itself can be stored in the database and should be incremented after applying the deltas, either from the deltas themselves or automatically in the processing code.

The deltas can be stored either as constant strings in your code files or just as resource files in your project.

Edit: (here is my migration code)

+ (NSArray*) chop:(NSString*)sql {
    NSMutableArray* list = [[NSMutableArray alloc] init];
    NSMutableString* sb = [[NSMutableString alloc] init];
    BOOL inside = FALSE;

    for (int i=0;i<[sql length];i++) {
        if ([sql characterAtIndex:i] == '\n') continue;
        if ([sql characterAtIndex:i] == '\r') continue;

        [sb appendFormat:@"%c",[sql characterAtIndex:i]];
        if (!inside) {
            if ([sql characterAtIndex:i] == '\'')
                inside = TRUE;
            else if ([sql characterAtIndex:i] == ';') {
                [sb deleteCharactersInRange:NSMakeRange([sb length]-1,1)];
                [list addObject:sb];
                [sb release];
                sb = [[NSMutableString alloc] init];
            }
        } else {
            if ([sql characterAtIndex:i] == '\'')
                inside = FALSE;
            else if ([sql characterAtIndex:i] == '\\') {
                i++;
                [sb appendFormat:@"%c",[sql characterAtIndex:i]];
            }
        }
    }
    [sb release];
    return [list autorelease];
}

+ (void) updateObjectModel {
    [Log output:@"[migration]"];

    int version;
    @try {
        int exist = [SQL queryLong:@"SELECT COUNT(*) FROM Variables WHERE Key='objectModelVersion'"];
        if (exist)
            version = [SQL retrieveInt:@"objectModelVersion"]+1;
        else {
            [Log output:@"[bootstrapping]"];
            [SQL storeInt:@"objectModelVersion" as:1];
            version = 2;
        }
    } @catch (NSException* e) {
        [Log output:@"[initializing]"];
        version = 0;
    }

    while (TRUE) {
        NSString* filename = [NSString stringWithFormat:@"SQLite-%04d",version];
        NSString* file = [[NSBundle mainBundle] pathForResource:filename ofType:@"sql"];
        if (file) {
            [Log output:[NSString stringWithFormat:@"[%d]",version]];
            NSArray* commands = [SQL chop:[NSString stringWithContentsOfFile:file encoding:NSASCIIStringEncoding error:NULL]];
            for (NSString* command in commands)
                [SQL update:command];
            [SQL storeInt:@"objectModelVersion" as:version];
        } else break;

        version++;
    }
    [Log output:@"[/migration]\n"];
}
aepryus
  • 4,715
  • 5
  • 28
  • 41