Search code examples
iosios6fmdb

fmdb path and using db 2nd time from another viewController


I am using the code below to create db in one class and using it in the viewController and same code again in another class in newViewController .
Is it correct ? Or does it create new db in its place or uses if there is an existing db ?

or Do I need to use only the path ?

When I tried extern NSString *path in the example below I received Chinese letters and could not open it.

Basically in the 2nd contoller I am only reading the data.
So I am trying to access db via path,database with path and database open.

How do it if I have used the code below to create it and I would like to open it 2nd time from another viewcontoller.
Please help.
Thanks in Advance.

In 1st Contoller:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:@"test.db"];

FMDatabase *database;
database = [FMDatabase databaseWithPath:path];
[database open];

    [database executeUpdate:@"DROP TABLE IF EXISTS **"];


    [database executeUpdate:@"CREATE  TABLE "];



    //Select query for single row
    FMResultSet *s = [database executeQuery:@"SELECT COUNT(*) FROM table"];
    if ([s next]) {
        int Count = [s intForColumnIndex:0];


    }

 //DO Something

[database close];

Above code is working perfectly in viewcontroller 1 .

Things I tried in view controller 2 .And it still did not work.

extern NSString *path;



    FMDatabase *database;
    database = [FMDatabase databaseWithPath:path];
    [database open];

I get an error here since the path is in What looks like Chinese language.

Error is EXC_BAD_ACCESS code .

Also tried global database by :

extern FMDatabase *database;

and using it as

[database open];

I still get the same error.

Finally it worked with the following code in the 2nd controller.
I just wanted to see if thats the right way to do it or can I use something as db open .

But I tried that and its not working.Also I am not using any error handler as I dont know how to do it in FMDB .
Thanks in Advance.

Please let me know if the following implementation is correct ? :

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
         NSString *docsPath = [paths objectAtIndex:0];
        NSString *newpath = [docsPath stringByAppendingPathComponent:@"test.db"];


          FMDatabase *database2 ;
         database2 = [FMDatabase databaseWithPath:newpath];

         [database2 open];

//DO Something

[database2 close];

Solution

  • As an example of how you should be checking for the success of the FMDB methods, here is an example:

    #import "FMDatabase.h"
    #import "FMDatabaseAdditions.h"
    
    - (void)performDatabaseDemonstration
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docsPath = [paths objectAtIndex:0];
        NSString *path = [docsPath stringByAppendingPathComponent:@"test.db"];
    
        // open db
    
        FMDatabase *database = [FMDatabase databaseWithPath:path];
        FMResultSet *rs;
    
        if ([database open])
        {
            // drop table
    
            if (![database executeUpdate:@"DROP TABLE IF EXISTS test"])
                NSLog(@"%s: drop error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            // create table
    
            if (![database executeUpdate:@"CREATE TABLE test(col1 INTEGER, col2 TEXT)"])
                NSLog(@"%s: create error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            // insert a row of data
    
            if (![database executeUpdate:@"INSERT INTO test VALUES(1, 'Rob')"])
                NSLog(@"%s: insert 1 error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            // insert another row of data
    
            if (![database executeUpdate:@"INSERT INTO test VALUES(2, 'Rachel')"])
                NSLog(@"%s: insert 2 error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            // Select query for single row
    
            if (!(rs = [database executeQuery:@"SELECT count(*) FROM test"]))
                NSLog(@"%s: select error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            if ([rs next]) {
                int count = [rs intForColumnIndex:0];
    
                NSLog(@"%s: count = %d", __FUNCTION__, count);
            }
    
            [rs close];
    
            // if getting single value from single row, you can use FMDatabaseAdditions.h methods:
    
            NSInteger count2 = [database intForQuery:@"SELECT col1, col2 FROM test"];
            NSLog(@"%s: count2 = %d", __FUNCTION__, count2);
    
            // let's actually retrieve data
    
            if (!(rs = [database executeQuery:@"SELECT col1, col2 FROM test"]))
                NSLog(@"%s: select error: %@", __FUNCTION__, [database lastErrorMessage]);
    
            while ([rs next]) {
                NSLog(@"%s: col1 = %@; col2 = %@", __FUNCTION__, [rs objectForColumnIndex:0], [rs objectForColumnIndex:1]);
            }
    
            [rs close];
    
            //close
    
            [database close];
        }
        else
        {
            NSLog(@"%s: open error: %@", __FUNCTION__, [database lastErrorMessage]);
        }
    }
    

    In terms of how to open a database from different controllers there are a ton of options. But I might suggest that above and beyond just retrieving some shared path/filename variable from two controllers, you might want to actually encapsulate not only the filename, but shared FMDB code, too, in an object.

    Personally, whenever I find myself writing the same or similar code in two different controllers, I ask myself whether I want to abstract that code out into its own class. In this case, for example, rather than debating how these two classes access the database filename, I would create my own own model object (a NSObject subclass), that encapsulates your SQL, does the opening of the database, etc. Then I could have my two view controllers invoke the methods from that method object, and really minimize not only redundancy in the filenames, but in the code itself, too. For example, the creating of the database path, opening the database, checking the success of the open, etc., are identical. So why not have one method in some model object, that does all of that for you.

    To make this model object accessible from both controllers, you have a bunch of options:

    • Make it a property of your first view controller, and have it pass it to your second controller during prepareForSegue;

    • Create a singleton object for it; or

    • Make that a property of your app delegate, and both of your controllers can get it from there.