Question

I have an SQLite database for my app. To retrieve the entities from the db I use this method:

- (sqlite3*) openDatabaseNamed:(NSString*)databaseName
{
    if(![databaseName isEqualToString:kTopInternationalDatabaseName] &&
       ![databaseName isEqualToString:kTop500RODatabaseName]){
        NSAssert(nil, @"Database does not exist!");
    }
    sqlite3 * dataBase;
    NSString * path;
    path =  [[NSBundle mainBundle] pathForResource:databaseName ofType:@"sqlite3"];
    if (sqlite3_open([path UTF8String], &dataBase) != SQLITE_OK) {
        NSString * errorString = [NSString stringWithFormat:@"[SQLITE] Unable to open database <%@> ",databaseName];
        NSAssert(nil,errorString);
    }
    return dataBase;
}

- (NSArray *) getAllEntitiesForDatabaseNamed:(NSString*)databaseName
{
  (...)
    sqlite3 * database  = [self openDatabaseNamed:databaseName];

    NSMutableArray *retval = [[NSMutableArray alloc] init];
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@",databaseName];

    NSArray *properties = [entityClass classProperties];

    if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
        == SQLITE_OK) {
        while (sqlite3_step(statement) == SQLITE_ROW) {


            for (int i=2; i<countLimit; i++){

                chars = (char *) sqlite3_column_text(statement,i+1);
                if(chars != NULL)
                {
                    containerString = [NSString stringWithUTF8String:chars];
                    if(containerString && containerString.length>0){
                        [entityModel setValue:containerString forKey:properties[i]];
                    }
                }
            }
            [retval addObject:entityModel];
        }
        sqlite3_finalize(statement);
    }
    sqlite3_close(database);
    return retval.copy;
}

Everything works as expected. For setting a custom field for an entity to a specific value in the database I use this method:

- (void)setEntity:(EntityModel *)entity favorite:(BOOL)favorite
{
    NSString *query = [NSString stringWithFormat:@"UPDATE %@ SET favorite = %i WHERE position = '%i';",kDatabaseName,favorite?1:0,entity.positionInTop];

    sqlite3_stmt *statement;
    sqlite3 * database = [self openDatabaseNamed:kTop500RODatabaseName];

    if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
        == SQLITE_OK) {
        sqlite3_step(statement);
        sqlite3_finalize(statement);
    }
    sqlite3_close(database);
}

What is happening is a little weird. If I use the update method and in the same lifecycle of the app query for all entities using getAllEntitiesForDatabaseNamed the changes I did with setEntity:Favorite: persist. On the other hand, if I use the update method, then turn off the app and restart it, the changes I did using setEntity:Favorite: are lost. Any idea on why is that?

PS: I also tried using sqlite3_exec and still the results are the same:

if (sqlite3_exec(database, [query UTF8String], NULL, NULL, NULL) != SQLITE_OK) {
    // deal with error...
    NSLog(@" -- ERROR!");
}
Était-ce utile?

La solution

The problem is that you're opening the database in the bundle, which is read only (on a device, at least). You should, instead, check to see if the database exists in your Documents folder, if not, copy it there from the bundle, and then open the database from the Documents folder.

Thus, you might do something like:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:databaseName ofType:@"sqlite"];

NSString *documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *documentsPath   = [[documentsFolder stringByAppendingPathComponent:databaseName] stringByAppendingPathExtension:@"sqlite"];

NSFileManager *fileManager = [NSFileManager defaultManager];

if (![fileManager fileExistsAtPath:documentsPath]) {
    NSError *error = nil;
    BOOL success = [fileManager copyItemAtPath:bundlePath toPath:documentsPath error:&error];
    NSAssert(success, @"%s: copyItemAtPath failed: %@", __FUNCTION__, error);
}

Having done that, you can now proceed to open the database at documentsPath.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top