Robert Kang
Robert Kang

Reputation: 568

FMDB - Failed to modify DB from Cocoa

I am working on a Cocoa application which talks to a local SQLite database with FMDB. I ran into an issue that I can't do any insert or update operation on DB. Select queries run perfectly fine, so I would assume my db connection settings are correct.

The structure of my code is basically like this:

FMDatabase* db=[FMDatabase databaseWithPath:[[NSBundle mainBundle] pathForResource:@"DBName" ofType:@"sqlite"]];

if(![db open])
{
  NSLog(@"Could not open db.");
}

db.traceExecution=YES;
[db beginTransation];
[db ExecuteUpdate:"INSERT INTO test (title) VALUES(?)", [NSNumber numberWithInt]:2],nil];
[db commit];
[db close];

No exceptions or warnings were thrown during execution, the console output regarding db.traceExecution is like following:

<FMDatabase: 0x100511fd0> executeUpdate: BEGIN EXCLUSIVE TRANSACTION;
<FMDatabase: 0x100511fd0> executeUpdate: INSERT INTO test (title) VALUES(?);
obj: 2
<FMDatabase: 0x100511fd0> executeUpdate: COMMIT TRANSACTION;

The testing database is simply just a one column table of INT type.

Everything looks fine except that the db file is not updated at all. It's really confusing to me as the Select query works perfectly fine. I checked the path of the database, it is pointing to the right one. First I suspect it's caused by file permission, but the issue remain the same even if I allowed everyone to be able to read/write.

I have been stucked with this problems for many hours and couldn't find a proper solution. Can anyone shed some light on this? Thanks!

Upvotes: 1

Views: 584

Answers (1)

bryanmac
bryanmac

Reputation: 39296

Databases in the bundle are read only. If the file doesn't exist at the destination folder where you define, you should copy it from the bundle to the library or documents folder and then connect to that. That means it will copy on first use of that path.

Here's a function to 'prepare' the database by copying it to the destination from the bundle. It copies it to library (from my iOS app) but you can copy wherever you want. In my case, it was contacts.db.

I called this method from ensureOpened.

- (BOOL)ensureDatabasePrepared: (NSError **)error
{
    // already prepared
    if ((_dbPath != nil) &&
        ([[NSFileManager defaultManager] fileExistsAtPath:_dbPath]))
    {
        return YES;
    }

    // db in main bundle - cant edit.  copy to library if !exist
    NSString *dbTemplatePath = [[NSBundle mainBundle] pathForResource:@"contacts" ofType:@"db"];
    NSLog(@"%@", dbTemplatePath);

    NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
    _dbPath = [libraryPath stringByAppendingPathComponent:@"contacts.db"];

    NSLog(@"dbPath: %@", _dbPath);

    // copy db from template to library
    if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
    {
        NSLog(@"db not exists");
        NSError *error = nil;
        if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:&error])
        {
            return NO;
        }

        NSLog(@"copied");
    }    

    return YES;    
}

Upvotes: 1

Related Questions