Maverick1st
Maverick1st

Reputation: 3770

sqlite fts does not work in adhoc version of app, but only in version started from xcode

I have a strange problem with sqlite which is driving me crazy.

I have implemented a sqlite database in my app and when i launch the app via xcode everything works fine. In Simulator as well as on my ipad. But when i build an ad-hoc version and install the ad-hoc version on my ipad the app crashes, when trying to start because it gets no data from the sql database, because there is no data in the database. At the first start the App should initialize the Database with data taken from an xml file and then access the data solely via the sqlite database. Works great if run from xcode, but not if run from ad-hoc version.

Has anyone an idea why this is so and how i can solve this issue? If sourcecode will be needed, i'll provide it.

The databasefile is contained in the bundle, and even if it wouldn't, it will be created.

Thx in advance, Maverick1st

Right now i do this in my appDidFinishLaunchingWithOptions

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
[AppState sharedInstance].s_databasePath = [documentsDir stringByAppendingPathComponent:[AppState sharedInstance].s_databaseName];



if ([self checkForSQLiteDataBaseFile])
{
    if (![self checkForSQLiteDataBaseTable]) {
        [self loadDataFromXMLIntoNSDictionary];
    }
}

-(BOOL) checkForSQLiteDataBaseFile
{
    BOOL ret = NO;
    // Check if the SQL database has already been saved to the users phone, if not then copy it over
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
    NSFileManager *fileManager = [NSFileManager defaultManager];

// Check if the database has already been created in the users filesystem
// If the database already exists then return without doing anything
    if([fileManager fileExistsAtPath:[AppState sharedInstance].s_databasePath]) 
        {
            Log2(@"databasePath: %@", [AppState sharedInstance].s_databasePath);
            ret = YES;
            return ret;
        }

// If not then proceed to copy the database from the application to the users filesystem

// Get the path to the database in the application package
    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[AppState sharedInstance].s_databaseName];

    // Copy the database from the package to the users filesystem
    [fileManager copyItemAtPath:databasePathFromApp toPath:[AppState sharedInstance].s_databasePath error:nil];
    Log2(@"databasePath: %@", [AppState sharedInstance].s_databasePath);

    ret = YES;
    return ret;
}   

And this is my documentspath: databasePath: /var/mobile/Applications/0CE5F143-04AC-4E1C-9A94-2B253F86F854/Documents/KatalogDaten

i also found out, that my database is opened by sqlite, for the following statement is true:

if(sqlite3_open([[AppState sharedInstance].s_databasePath UTF8String], &database) == SQLITE_OK)

*EDIT** after checking the filesystem of the app on my ipad with iExplorer i noticed, that the table is indeed there and has also content in itself. But since i work on a virtual table in my app because i need full-text-search i want to create a virtual table if none exists in that database and that seems to fail somehow. I'll provide further updates if i find out more.

**Edit***

So, i think i found out where the error is, but not what causes it.

The following sourcecode is executed normally, but only returns the desired result, when run from xcode on my ipad and not when run from an ad-hoc version on my ipad.

if(sqlite3_open([[AppState sharedInstance].s_databasePath UTF8String], &database) == SQLITE_OK)
{
    NSLog(@"Database opened successfully!");
    const char *sqlQuery = [@"select DISTINCT tbl_name from sqlite_master where tbl_name = ?;" UTF8String]; 
    Log2(@"sqlquery for selecting distinct table: %s", sqlQuery);
    sqlite3_stmt *compiledQuery;
    if(sqlite3_prepare_v2(database, sqlQuery, -1, &compiledQuery, NULL) == SQLITE_OK) 
    {
        sqlite3_bind_text(compiledQuery, 1, [k_db_virtualTableName UTF8String], -1, SQLITE_TRANSIENT);

        if(sqlite3_step(compiledQuery)) 
        {
            // look if virtual table already exists
            if ((char *)sqlite3_column_text(compiledQuery, 0) == nil) 
            {
                NSLog(@"Create virtual table!");
                sqlite3_finalize(compiledQuery);

                sqlQuery = [[NSString stringWithFormat:@"CREATE VIRTUAL TABLE %@ USING FTS3 (%@);", k_db_virtualTableName, keyString] UTF8String];
                Log2(@"sqlQuery: %s", sqlQuery);        
                if(sqlite3_prepare_v2(database, sqlQuery, -1, &compiledQuery, NULL) == SQLITE_OK) 
                {   
                    NSLog(@"query for virtual table is ok!");
                    if(sqlite3_step(compiledQuery)) 
                    {
                        NSLog(@"query for virtual table was executed properly!");
                        sqlite3_finalize(compiledQuery);
                        [self fillSQLiteDataBaseTable: k_db_virtualTableName WithDataFromDict: [[[xmlDictionary objectForKey:@"UHRENTABELLE"] objectForKey:@"UHR"] mutableCopy]];
                    }
                    else
                    {
                        sqlite3_finalize(compiledQuery);
                    }
                }
            } 
            else
            {
                NSLog(@"Virtual Table already exists!!");
                sqlite3_finalize(compiledQuery);
            }
        }
    }
    else
    {
        NSLog(@"The following statement is not correct: %@", sqlQuery);
    }
}
sqlite3_close(database);

The Method fillSQLiteDataBaseTable is executed normally as well when run through xcode or from ad-hoc version. Only difference is, that with the ad-hoc version the creation of the virtual table fails without the app telling me. The app just continues as if nothing had happened but when i look into the sql file afterwards there is no virtual table there when run from ad-hoc version. The virtual table only exists when run from xCode.

Upvotes: 1

Views: 619

Answers (2)

Maverick1st
Maverick1st

Reputation: 3770

I forgot to add the following line to the head of my sqlite.c file

#define SQLITE_ENABLE_FTS3

So it was basically the problem, that when i ran my version from xcode. The sqlite version in my bundle was used, which seemed to support fts3 for some reason i don't know without the need of enabling it with that define.

The sqlite version on my iPad though does not support fts. So this might be the reason my sqlite was behaving so strange.

Anyways. Now it runs perfectly smooth.

Upvotes: 0

Saurabh
Saurabh

Reputation: 22873

well.. don't write on a file if that file is in your resource bundle.. you should first copy a blank database in your app's document directory.. then you should write in that file.. apple sdk does not allow to change anything in nsbundle if you signed the app..

Here is how you can get path to your database in documents directory -

    NSString *documentsDir = [CommonFunctions documentsDirectory];

   databasePath = [[documentsDir stringByAppendingPathComponent:@"yourdatabase.sqlite"] retain];
   [self checkAndCreateDatabase];

After getting this path you should check if a database is already exists (then ok..) if not then use this below function to copy your blank database from your nsbundle to document directory -

//check if database exists else copy
-(void) checkAndCreateDatabase
{
   // Check if the SQL database has already been saved to the users phone, if not then copy it over
   BOOL success;

   // Create a FileManager object, we will use this to check the status
   // of the database and to copy it over if required
   NSFileManager *fileManager = [NSFileManager defaultManager];

   // Check if the database has already been created in the users filesystem
   success = [fileManager fileExistsAtPath:databasePath];

   // If the database already exists then return without doing anything
   if(success) return;

   // If not then proceed to copy the database from the application to the users filesystem

   // Get the path to the database in the application package
   NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"yourdatabase.sqlite"];

   // Copy the database from the package to the users filesystem
   [fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];

}

post comment if you need more help in this.

Upvotes: 2

Related Questions