IPhone ios 5, recreate database on each simulator run? - iphone

How can i replace the database (with the one i am working on) in my app on each simulator run? (Still adding data and want to check that it looks good)
I tried this but it doesn't seem to be working, still using the old database:
- (void)viewDidLoad
{
dbname = #"animaldatabase.sql";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
dbpath = [documentsDir stringByAppendingPathComponent:dbname];
[self checkAndCreateDatabase];
[self readAnimalsFromDatabase];
[super viewDidLoad];
// Do any additional setup after loading the view.
mylabel.text = [NSString stringWithFormat:#"%d", [animals count]];
}
- (void) checkAndCreateDatabase{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:dbpath];
if (success) {
//return;
[fileManager removeItemAtPath:dbpath error:nil];
}
NSString *dbPathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbname];
[fileManager copyItemAtPath:dbPathFromApp toPath:dbpath error:nil];
}
Also where is the location for the iphone database? When my app copies the database into the iphone, where does it end up?
UPDATE
Added the loading code as well.
Senthilkumar:
I fail to see how this should solve the problem? Doesn't this just skip the copying or my database if it already exists?
What i want is to delete the database in my iphone app. Import the new database and then run it.

use the following code instead of that.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
databaseName=#"yourfileName.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
[self checkAndCreateDatabase];
}
-(void) checkAndCreateDatabase {
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:databasePath];
if(success) return;
else
printf("NO File found");
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}

To help debug file manager operations, it helps to try and use the error parameter, like so:
NSError* error;
[fileManager copyItemAtPath:dbPathFromApp toPath:dbpath error: &error];
if (error != nil) {
NSLog(#"copyItemAtPath generated error=%#", [error localizedDescription]);
}
If you do this, the error message will get written to your debugger console / standard output window.

Related

SQLite database not loading on iphone device

When I test my app on the simulator everything works. But when I test the app on my iPhone, it doesn't load from the db.sqlite3 data base.
I tried every solution from the internet. I cleaned the app. I added the database to the build phrases - copy bundle resources etc.
Can you help me? Is there any fails in my coding?
-(void)openDataBase{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
dbPath = [documentsDirectory stringByAppendingString:#"db.sqlite3"];
//load it
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathInRessource = [[NSBundle mainBundle] pathForResource:#"db" ofType:#"sqlite3"];
//exist it?
if (![fileManager fileExistsAtPath:dbPath]) {
[fileManager copyItemAtPath:pathInRessource toPath:dbPath error:nil];
}
//open database
int result = sqlite3_open([dbPath UTF8String], &db);
if (result != SQLITE_OK) {
sqlite3_close(db);
return;
}
//if ok
NSLog(#"Data base opened");
}
At the time of opening database you can call this with its else case to get the issue.
if (sqlite3_open([getDBPath UTF8String], &sqlhandle) == SQLITE_OK)
{
NSLog(#"Database Open!");
}
else
{
NSLog(#"Select err: %s",sqlite3_errmsg(sqlhandle));
}
You will be able to get proper error message using this
I am wondring where you getting stuck, I am doing same way and its working fine to me.
BTW I have updated your code, how I am handling, please check let me know if find and trouble in this.
//My DB variable
static sqlite3 *database;
-(void)openDataBase{
if (database == NULL)
sqlite3 *mDatabse;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
dbPath = [documentsDirectory stringByAppendingString:#"db.sqlite3"];
//load it
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathInRessource = [[NSBundle mainBundle] pathForResource:#"db" ofType:#"sqlite3"];
//exist it?
if (![fileManager fileExistsAtPath:dbPath]) {
[fileManager copyItemAtPath:pathInRessource toPath:dbPath error:nil];
}
if (sqlite3_open([path UTF8String], &mDatabse) == SQLITE_OK) {
NSLog(#"Database Successfully Opened");
database = mDatabse;
} else {
NSLog(#"Error in opening database");
mDatabse = NULL;
}
}

Where does iPhone simulator put data files?

If I compile my iPhone App with a database that has been built into the bundle what path do I have to use to find the file so that I can open it, as in:
FMDatabase* db = [FMDatabase databaseWithPath:#"/???/database.sqlite"];
Inside your - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
set the following code:
databaseName=#"yourfileName.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
[self checkAndCreateDatabase];
-(void) checkAndCreateDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:databasePath];
if(success) return;
else
printf("NO File found");
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}
You can use like this regardless of simulator or device. Please dont forget to add db file to your project folder and add to your bundle.
#define DATABASE #"databasefile.sqlite"
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [[NSString alloc] initWithString:[documentsDir stringByAppendingPathComponent:DATABASE]];

how do we open an already existing database fmdb and where to include it?

I am using fmdb but i am not knowing where to add the db file.
I found this old answer that i think it doesn't fit for xcode 4.2 (since we dont have 'Resources' folder anymore).
I created a database in 'Lita', added the extension .sqlite and added the db file as follows :
then tried to use the following
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"db.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:path];
[database open];
FMResultSet *results = [database executeQuery:#"select * from tbl"];
printf("hi");
while([results next]) {
printf("hi2");
NSString *name = [results stringForColumn:#"clm1"];
NSString *text = [results stringForColumn:#"clm2"];
NSLog(#"test: %# - %#",name, text);
}
printf("done");
i am getting the 'hi' 'done' but never 'hi2'...
anyone can help?
You want to add the (compiled) database to the project as a resource, and then copy it into the documents folder of the app on first start-up.
Here is an example method that does the work, call it from the applicationDidFinishLaunching and use the resultant FMDatabase * for all you queries.
- (FMDatabase *)openDatabase
{
NSFileManager *fm = [NSFileManager defaultManager];
NSString *documents_dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *db_path = [documents_dir stringByAppendingPathComponent:[NSString stringWithFormat:#"database.s3db"]];
NSString *template_path = [[NSBundle mainBundle] pathForResource:#"template_db" ofType:#"s3db"];
if (![fm fileExistsAtPath:db_path])
[fm copyItemAtPath:template_path toPath:db_path error:nil];
FMDatabase *db = [FMDatabase databaseWithPath:db_path];
if (![db open])
NSLog(#"Failed to open database!");
return db;
}
I am working with the same issues right now. But to answer your question: the documents folder is the folder that is to be used in the device.
Put the database in the the supporting files folder.
Here is what works for me:
//hide status bar for full screen view
[[UIApplication sharedApplication]setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
//set reference to database here
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.databasePath = [documentDir stringByAppendingPathComponent:#"RPRA.sqlite"];
[self createAndCheckDatabase];
// Override point for customization after application launch.
return YES;
}
//method used to create database and check if it exists
-(void) createAndCheckDatabase
{
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:self.databasePath];
if(success) return;
//else move database into documents folder
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"RPRA.db"];
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
}
- (void)ensureDBFile
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dbFilePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"retails.sqlite"]];
NSLog(#"Database Filepath Delgate = %#", dbFilePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL dbFileExists = [fileManager fileExistsAtPath:dbFilePath];
if(!dbFileExists){
//Copy the db file if it didn't exist
NSString *bundledDbPath = [[NSBundle mainBundle] pathForResource:#"retails" ofType:#"sqlite"];
if (bundledDbPath != nil) {
BOOL copiedDbFile = [fileManager copyItemAtPath:bundledDbPath toPath:dbFilePath error:nil];
if (!copiedDbFile) {
NSLog(#"Error: Couldn't copy the db file from the bundle");
}
}
else {
NSLog(#"Error: Couldn't find the db file in the bundle");
}
}
self.db = [FMDatabase databaseWithPath:dbFilePath];
[self.db setShouldCacheStatements:NO];
[self.db open];
if([self.db hadError]) {
NSLog(#"Database Error %d: %#", [self.db lastErrorCode], [self.db lastErrorMessage]);
}
}

copying plist to document directory

How do I copy an existing plist I have in the app build to the Document directory?
My plist is basically an array of dictionary, I have successfully copy the plist to the directory using the following:
BOOL success;
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"States.plist"];
success = [fileManager fileExistsAtPath:filePath];
if (success) return;
NSString *path = [[NSBundle mainBundle] pathForResource:#"States" ofType:#"plist"];
success = [fileManager copyItemAtPath:path toPath:filePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to copy Plist. Error %#", [error localizedDescription]);
}
However, when I try to access the NSDictionary it gives me:
CoreAnimation: ignoring exception: -[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
Why is this? I have to relaunch the app again and now I can change the dictionary stored in my array
This is the way I copy the *.plist file to documents folder. Hope this helps.
+ (NSString*) getPlistPath:(NSString*) filename{
//Search for standard documents using NSSearchPathForDirectoriesInDomains
//First Param = Searching the documents directory
//Second Param = Searching the Users directory and not the System
//Expand any tildes and identify home directories.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:filename];
}
+ (void) copyPlistFileToDocument{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [Utilities getPlistPath:#"AppStatus.plist"];
if( ![fileManager fileExistsAtPath:dbPath])
{
NSString *defaultDBPath = [[NSBundle mainBundle] pathForResource:#"AppStatus" ofType:#"plist"];
BOOL copyResult = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if(!copyResult)
NSAssert1(0, #"Failed to create writable plist file with message '%#'.", [error localizedDescription]);
}
}
Just create a new pList in the documents directory, and set to the contents of the existing plist you have.

iPhone SDK: SQLite error: no such table:, even though a terminal query returns successful

I have a problem with sqlite database integration in my app.
My app uses a translator style function, whereby a users text entry is converted into another way of wording the same word, the word alternatives are listed in a SQLite database.
Basically the code is designed to take text, split it into individual words, and search for them on the database, and return the result for each word, however, the application ends up not returning results and I get the error "error: no such table: dictionary" Even though a query on terminal returns results successfully.
Can anyone give me a hand in identifying what's been done wrong? Thanks
This is the code I used:
-(void)translate{
//take input and break into an array
NSString *clearText = [[NSString alloc] init];
clearText=inputBox.text;
NSArray *words = [[NSArray alloc] init];
words= [clearText componentsSeparatedByString:#" "];
numOfWords=words.count;
NSString *newText=#"";
//open database
sqlite3 *db = [self getNewDBConnection];
//loop through array
for(i=0;i<numOfWords;i++){
sqlite3_stmt *resultStatement= nil;
NSString *res = [NSString stringWithFormat:#"select * from dictionary where plain='%#'",[words objectAtIndex:i]];
if((sqlite3_prepare_v2(db, [res UTF8String], -1, &resultStatement, nil))!=SQLITE_OK){
NSLog(#"Error getting result, maybe word not found\n");
//NSLog(#"tried query:%#",res);
NSLog(#"error: %s", sqlite3_errmsg(db));
}
else{
if(sqlite3_step(resultStatement)==SQLITE_ROW){
//in the line below, 1 is the column number of the replacement word
NSString *add = [[NSString alloc] initWithUTF8String: (char*)sqlite3_column_text(resultStatement,1)];
newText=[newText stringByAppendingString:add];
[add release];
}
}
sqlite3_finalize(resultStatement);
}
//output result
outputBox.text=newText;
sqlite3_close(db);
}
-(sqlite3 *) getNewDBConnection{
sqlite3 *newDBconnection;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data9.sqlite"];
if (sqlite3_open([path UTF8String], &newDBconnection) == SQLITE_OK) {
NSLog(#"Database Successfully Opened");
} else {
NSLog(#"Error in opening database");
}
return newDBconnection;
}
This is the code used to copy the database:
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"data9.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data9.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Based on the missing table, it looks like it is creating a new database at the path. So either the path is wrong, or it is possible that you might not have copied the master database to that location. The SQLite file is usually in the bundle and needs to be copied first.
Major issues from my perspective.
I went so far as to delete the entire database capability and then ran the app and it still opened the database.
I have this code in applicationDidFinishLaunchingWithOption in the appDelegate. It is designed for start-up to check a valid database is avaailable, so mostly it is never executed. I removed the conditional statement around the copy, and inserted a removeItemAtPath statement to delete the file. Ran it once, and my problem all went away (or at least those with the database, by biggest problem is still user error :-))
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"languageDB.sqlite"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
NSError *error;
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"language.sqlite"];
NSString *writableDBPath = [docsDir stringByAppendingPathComponent:#"languageDB.sqlite"];
if ([filemgr fileExistsAtPath: databasePath ] == NO){
[filemgr copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
NSLog(#"copy error %#",error);
}
Something else you might want to make sure of if you're adding a table to an existing database and then trying to re-add it to your project is that you delete the copy of the database in the location where Xcode is copying it prior to loading your app in the simulator. For instance, when I was having this problem, I had to go into /Users/<user>/Library/Application Support/iPhone Simulator/6.0/Applications/1234DFE1-5AA9-4CFF-0235-3D98961E9281/Documents/ to delete the database there. When I cleaned and re-ran the simulator, it found my new table just fine.