views:

747

answers:

4

I am in the process of (finally) loading my App onto an iPhone device for testing. So far I have only tested the App in the simulator. The application is data-centric and uses an SQLite database. I have created an appropriate SQLite database with sample data which I used in the simulator. How can I copy this .sqlite3 file to the actual iPhone?

This was easy with the simulator as I could just copy the .sqlite3 file into the ~/Library/Application Support/iPhone Simulator/User/Applications/{UUID}/Documents folder, but I cannot figure out how to get this into the same location on the actual iPhone device. Recreating the database from scratch on the phone is not really an option, as I have already done all the hard-yards creating the database on the Mac.

+5  A: 

Why not add it to your application bundle? Right-click on the Resources folder in your project window in Xcode, then select Add > Existing Files... to add your sqlite database.

You would then load the file from the application bundle:

NSString *databasePath = [[NSBundle mainBundle] pathForResource:@"mySQLiteDatabaseFile" ofType:@"sqlite3"];
NSData *databaseData = [NSData dataWithContentsOfFile:databasePath];
// ...
Alex Reynolds
Thanks Alex. But can I write back to the database if it has been included in the application bundle?
Skoota
No, but you can copy this "preinitialized", basic database from there to the `Documents` folder, which is writeable. The nice thing is that this approach also allows the user to "reset" the application.
Alex Reynolds
Thanks Alex. That's a very good approach.
Skoota
+7  A: 

As Alex said, you have to add your database file as a resource to your project. This will instruct Xcode to bundle your database file in your .app. That file however is read-only, it's up to you to copy it over to your apps document folder (on the device or simulator, same thing), where it becomes writable basically.

Here's the code that I use for this kind of thing below. The nice thing about this code is that it will automatically refresh the writable copy in your apps document folder whenever you change the "main" copy bundled in the .app. Use 'pathLocal' to open your (writable) database...

The function below returns YES when the operation was successful (whether a copy was needed or not). You're free to change that with whatever suits you :)

NSString *pathLocal, *pathBundle;

// Automatically copy DB from .app bundle to device document folder if needed
- (BOOL)automaticallyCopyDatabase {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
    NSString *documentsDir = [paths objectAtIndex:0];
    pathLocal = [documentsDir stringByAppendingPathComponent:@"mydb.sqlite"];
    pathBundle = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"mydb.sqlite"];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDictionary *localAttr = [fileManager fileAttributesAtPath:pathLocal traverseLink:YES];
    BOOL needsCopy = NO;
    if (localAttr == nil) {
        needsCopy = YES;
    } else {
        NSDate *localDate;
        NSDate *appDBDate;
        if (localDate = [localAttr objectForKey:NSFileModificationDate]) {
            NSDictionary *appDBAttr = [fileManager fileAttributesAtPath:pathBundle traverseLink:YES];
            appDBDate = [appDBAttr objectForKey:NSFileModificationDate];
            needsCopy = [appDBDate compare:localDate] == NSOrderedDescending;
        } else {
            needsCopy = YES;
        }
    }
    if (needsCopy) {
        NSError *error;
        BOOL success;
        if (localAttr != nil) {
            success = [fileManager removeItemAtPath:pathLocal error:&error];
        }
        success = [fileManager copyItemAtPath:pathBundle toPath:pathLocal error:&error];
        return success;
    }
    return YES;
}
Zoran Simic
That's fantastic. Thanks for this code Zoran. It will make things a bit (i.e. a lot) easier for me :) I particularly like that the code will refresh the writable copy when the main copy changes.
Skoota
A: 

here how i done this create a class called DBManagement and in m file implement these methods.also put this line in implementation file above @implementation line

static sqlite3 *CHManagement = nil;

-(NSString *) getDBPath
{
 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
 NSString *documentsDirectory = [paths objectAtIndex:0];
 NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:@"/CHManagement.sqlite"];
  return MyDatabasePath;

 }

-(void) checkDataBase
{
NSLog(@"CheckDatabase Called");
NSFileManager *fileManager=[NSFileManager defaultManager];
NSError *error=[[[NSError alloc]init] autorelease]; 
NSString *dbPath = [self getDBPath];
BOOL success=[fileManager fileExistsAtPath:dbPath];
if(!success)
{
 NSString *defaultDBPath=nil;
 defaultDBPath=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"CHManagement.sqlite"];
 success=[fileManager copyItemAtPath:defaultDBPath  toPath:dbPath error:&error];
 if(!success)
 {
   NSAssert1(0,@"failed to create database with message '%@'.",[error localizedDescription]); 
  }
 }
 else
 {
 sqlite3 *MyDatabase;
 NSInteger VersionNumber=0;
 sqlite3_stmt *CompiledStatement=nil;
 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
 NSString *documentsDirectory = [paths objectAtIndex:0];
 NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:@"/CHManagement.sqlite"];
 if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK)
 {
  sqlite3_prepare_v2(MyDatabase, "select appVersionNo from VersionInfo", -1, &CompiledStatement, NULL);
  while(sqlite3_step(CompiledStatement) == SQLITE_ROW)
  {
   VersionNumber=sqlite3_column_int(CompiledStatement,0);
  }
  sqlite3_reset(CompiledStatement);
  sqlite3_finalize(CompiledStatement);
  sqlite3_close(MyDatabase);
 }
 if (VersionNumber!=1)
 {
  [self deleteDatabase];
  NSString *defaultDBPath=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"CHManagement.sqlite"];
  success=[fileManager copyItemAtPath:defaultDBPath  toPath:dbPath error:&error];
  if(!success)
  {
   NSAssert1(0,@"failed to create database with message '%@'.",[error localizedDescription]); 
   }
  }
 }
 }

- (void)deleteDatabase
{
NSLog(@"Delete Database Called");
NSError **error=nil;
NSFileManager *FileManager = [NSFileManager defaultManager];
NSString *databasepath = [self getDBPath];
  BOOL success = [FileManager fileExistsAtPath:databasepath];
if(success) {
[FileManager removeItemAtPath:databasepath error:error];
}
 }

with these methods you can easily load database into iphone document folder of the app. Sorry about the formatting. and appVersionNo is a field in a tableCalled versionInfo the default value is 1

Rahul Vyas
A: 

I got error and warning messages of this function "-(void) checkDataBase" in the line:

sqlite3 *MyDatabase; NSInteger VersionNumber=0; sqlite3_stmt *CompiledStatement=nil; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:@"/CHManagement.sqlite"]; if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK) { sqlite3_prepare_v2(MyDatabase, "select appVersionNo from VersionInfo", -1, &CompiledStatement, NULL); while(sqlite3_step(CompiledStatement) == SQLITE_ROW) { VersionNumber=sqlite3_column_int(CompiledStatement,0); } sqlite3_reset(CompiledStatement); sqlite3_finalize(CompiledStatement); sqlite3_close(MyDatabase); } if (VersionNumber!=1) { [self deleteDatabase]; NSString *defaultDBPath=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"CHManagement.sqlite"]; success=[fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error]; if(!success) { NSAssert1(0,@"failed to create database with message '%@'.",[error localizedDescription]); } } }

test