My simulator runs fine and fast. My iphone seems to be freezing at a part where I try to create and fill a database. However i prefer to use the database from the simulator and put that on the iphone so the user doesn't have to recreate the database.
What i like to know is how can i load from the database added to the folders.
I searched a lot but either it is outdated or different from what i want.
I added the database file now from the finder into the xcode project.
So if I'm correct I have to change _databasePath to point to wherever the file is, am I correct?
And if so where is it, the one from the code is here:
/var/mobile/Applications/65B5541A-1E73-46F6-AB5A-C5988003103E/Documents/paths.db
But that is no the one i dragged into xcode.
Also i looked at organizer, i can see there documents/paths.db but since it misses other files i also assume that that is the code created db and not the dragged in.
I tried to delete it as well but i can't select it.
can someone help?
in header:
#property (strong, nonatomic) NSString *databasePath;
#property (nonatomic) sqlite3 *pathDB;
in .m:
- (void) createDataBaseIfNotExist {
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the database file
_databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"paths.db"]];
NSLog(#"databasePath: %#", _databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: _databasePath] == NO) {
const char *dbpath = [_databasePath UTF8String];
if(sqlite3_open(dbpath, &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt =
"CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table");
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
So, a couple of things:
You first need to modify the createDatabaseIfNotExist to copy from the bundle if it's not found in Documents:
- (void) createDataBaseIfNotExist {
// Get the documents database path
NSString *docsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.databasePath = [docsDir stringByAppendingPathComponent:#"paths.db"]; // always use setter when setting property's value
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:_databasePath] == NO) {
// if the database doesn't exist in documents, look for it in the bundle and copy it if found
// get the bundle database path
NSString *bundleDatabasePath = [[NSBundle mainBundle] pathForResource:#"paths" ofType:#"db"];
if (bundleDatabasePath) {
// if we successfully copied from bundle, then quit
if ([fileManager copyItemAtPath:bundleDatabasePath toPath:self.databasePath error:nil])
return;
}
// otherwise, let's proceed with creating the database
if(sqlite3_open([_databasePath UTF8String], &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK) {
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table, %s", errMsg);
sqlite3_free(errMsg); // if you're going to use that fifth parameter, you must free it when you're done
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
Second, once you've run this once on the simulator, find the database in the simulator's Documents folder to your Xcode project. The simulator's files can be found in
~/Library/Application Support/iPhone Simulator/6.1/Applications/XXX/Documents
where XXX is the cryptic identifier (e.g. 85206BA6-9D03-4F18-BB0A-3B8C25B552C4). Note, by default, the Library folder is hidden, so I go to a Terminal command line and type in the following command to show it:
chflags nohidden Library
You can then add the database back to your project by dragging from Finder to Xcode's file navigator window, at which point you'll see a window like:
Make sure to check the two highlighted checkmarks to ensure that the database will be included in the bundle.
Two final observations:
Now that you have a "copy from bundle if necessary logic", it's an interesting question whether you really want the code to create the table in code at all anymore. Personally, I always just create my databases with a nice Mac graphical SQLite tool and then copy them to my project. The only time I do programmatic creating of tables is when (a) I'm deploying an update which involves new/altered tables; and (b) the user's database might contain some key data that I don't want to simply replace with the database from the bundle.
I personally always include a configuration table in my app which contains a single row for which one of the columns is the database version. Thus, my app will open the database from documents, check the version number, and if out of date (because the user only just recently upgraded their app) then update the database. This "database version number" logic is something that you really want to get in place as part of version 1.0 of your app.
Related
Previously I used the SQLite3 library that comes with Xcode, the code to create a database can be found below:
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"databases/database.sqlite"]];
NSLog(#"DB Path: %#", databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO) {
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
char *errMsg;
const char *sql_data = "CREATE TABLE IF NOT EXISTS myTableName.....";
if (sqlite3_exec(myDatabase, sql_data, NULL, NULL, &errMsg) == SQLITE_OK) {
NSLog(#"Database OK");
[self setDatabase];
} else {
NSLog(#"Data Base Fail");
}
sqlite3_close(myDatabase);
} else {
NSLog(#"Database fail");
}
}
Most recently I had the need to encrypt a database file, and performing a brief search on the internet, I found that some sites recommend using SQL Cipher.
I implemented it in my code in the way that is described in this link, and the only thing I had to do was stay with that code (which I stated earlier), and use these two commands after sqlite3_open:
const char* keyTB = [#"MySecretPassword" UTF8String];
sqlite3_key(myDatabase, keyTB, (int)strlen(keyTB));
To check if my database was encrypted I open it with a text editor and I saw this:
øøèDEÆ?>o›$™;⁄iìÚÄ’†í]¥d=ˇÓä\Êź$4áÓÈ?ïÒ[ÅaÚvÁƒ•i%í≈ª¢.£s◊Âc®Øì≈ àÜU—–}Gec‹≥’B∂¡¸¸Æ™√3Ìnú»YÆ"ß
¬?wÚ÷fñoÂ≈ÛͯzÏâ⁄˛Ct°˘ΩfìÙº0ˇfi]
‚ŸSw∂â≤≥‘=�H€BN±HÇûß…∑º.náaߨO¬ˇ¢(B¨‹óµ¬;º‹ÀÒ
Is it really that SQL Cipher encrypted my database (256-bit AES encryption)? or need to do some configuration in my code?
It is recommended that you not directly embed the password to the database, however that is not a strict requirement for SQLCipher to work. Once you have SQLCipher integrated within your application, you do simply just need to key the database with sqlite3_key once you open the connection. To verify the state of an encrypted database, typically one will run hexdump -C on the database file itself, the content should appear indecipherable.
I used the following code for database creation in Xcode. It runs smoothly up to the NSFilemanager code, but after that it will terminating to else code that says status.text=#"failed to open/create database"; so table can't be created.
I imported sqlite3.h and create sqlDatabase reference variable sqlite3 still it doesn't work.
-(void)databaseCreate
{
NSString *docsDir;
NSString *dbPath;
NSArray *dirPath;
dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSLog(#"dirpath::%#",dirPath);
docsDir=[dirPath objectAtIndex:0];
NSLog(#"document directory::%#",docsDir);
dbPath=[[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"timerpro1.db"]];
NSLog(#"database path::%#",dbPath);
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath: dbPath] == NO)
{
const char *databsPath=[dbPath UTF8String];
NSLog(#"treat filemanager");
if(sqlite3_open(databsPath,&sqlDatabase) == SQLITE_OK)
{
char *err;
NSLog(#"create inside");
const char *sql_stmt="CREATE TABLE IF NOT EXISTS PRJDATA(ID INTEGER PRIMERY KEY AUTOINCREMENT,PRJ_NAME TEXT,PRJ_DATE TEXT,TIME_POINT1 TEXT,TIME_POINT2 TEXT,TIME_POINT3 TEXT,POINT2_DIFF_MIN TEXT,POINT2_DIFF_SEC TEXT,POINT3_DIFF_MIN TEXT,POINT3_DIFF_SEC TEXT)";
if (sqlite3_exec(sqlDatabase, sql_stmt, NULL, NULL, &err)!=SQLITE_OK)
{
status.text=#"failed to create table";
}
sqlite3_close(sqlDatabase);
}
else
{
status.text=#"failed to open/create database";
}
}
[fileManager release];
}
The immediate problem is that you have to replace the reference to NSDocumentationDirectory with NSDocumentDirectory.
Two asides:
When you get failures, you should examine sqlite3_errmsg(), as you'll often get descriptive errors. For example, your sqlite3_exec() statement will fail, even after you fix the NSDocumentDirectory mistake.
If you look at the sqlite3_errmsg() error message, it will tell you that you have an error near the AUTOINCREMENT clause. If you look at the SQL carefully, you'll notice that you have misspelled PRIMARY KEY. That would be more difficult to find in the absence of the sqlite3_errmsg(), which brings our attention to the particular portion of the SQL.
You can simplify dbPath declaration:
dbPath=[docsDir stringByAppendingPathComponent:#"timerpro1.db"];`
So I'm new to XCode and I want to store my data in a database using Sqlite3. I have a class users where I have this method.
-(void)createOrOpenDB
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:#"users.db"];
char *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:dbPathString] == NO)
{
const char *dbPath = [dbPathString UTF8String];
if(sqlite3_open(dbPath, &userDB)==SQLITE_OK)
{
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS USERS (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, PASSWORD TEXT, CONFIRM TEXT, EMAIL TEXT)";
sqlite3_exec(userDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(userDB);
}
}
}
But I have also another class where I would like to create another table. But it won't work because this methods says that the database already exists.
How can I just open that database in another class to create a new table?
Assuming you stay with sqlite, you are better off having a single method that opens or creates the database. If the database is created, the entire schema gets created all at once. Now all of your classes can call the single method.
This will be makes things much easier now and later. Sometime down the road your scheme may change. This one method can also be written to update the schema as needed.
I've been developing an enterprise iPad app for a while now. Since the app development started almost 2 years ago, I needed to compile my own version SQLite from source, since the default SQLite library (the sqlite3.dylib) didn't have FTS enabled by default.
Ok, everything was working fine since then. I've been always using GCC as the project compiler.
The thing is that now I'm trying to convert my whole project to use ARC. To do so, I need to use Apple's LLVM compiler.
That's it. When I change the compiler (from GCC 4.2 to LLVM 3.1 or 4.0, without converting to ARC, and without changing anything else), my app builds fine, everything runs ok, except by my FTS queries, which don't work at all Even the simplest ones. They run and return always with no result (with a SQLITE_OK code, though).
I'm stuck here. I have already talked to an Apple engineer at WWDC'12 but we couldn't find any solution.
I guarantee that it is unlikely to be a malformed query or something like that since the app is working fine with GCC. Also, I'm able to run the queries on the Terminal version of SQLite (or using other apps, like Base)
I was also using an old version of SQLite, but I've updated to the most recent version to date (3.7.13). Everything stays the same. I've also noticed that, now (I don't know since when) the sqlite that comes with the mac supports FTS (!!!) and I was able to remove my own version and use Apple's one. The thing is, I'm having the exact same behavior.
I've been looking for a solution, but couldn't find one. I've found some bugs related to armv6 and compiler optimisations (which can be fixed by using the -mno-thumb flag), but it's not my case. I also noticed that when I analyse my custom sqlite files using Clang it points out many, many "potencial errors".
I have this non-skeptical view and I (still) don't believe that it's a LLVM or SQLite bug. I prefer to check everything that is possible before addressing them a bug. Maybe I'm forgetting to configure something or need to add some flag to the compiler that I'm not doing.
I appreciate any help. Again, the bug only occurs on projects compiled with LLVM (even with the default sqlite). If I run the same queries on the Terminal version of sqlite3, everything goes fine.
UPDATE:
This code works. It creates a new database, a new virtual table using fts, insert a couple of items and then execute the select. I'll try more complex queries later, but, for now, it seems that the issue with my app might be, as expected, a bug in my code.
NSArray *dirPaths = dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
sqlite3 *database;
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"test.db"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
NSError *error = nil;
[filemgr removeItemAtPath:databasePath error:&error];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt = "CREATE VIRTUAL TABLE IF NOT EXISTS pages USING fts3(title, body);";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
} else {
sql_stmt = "INSERT INTO pages(docid, title, body) VALUES(53, 'Home Page', 'SQLite is a software...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
sql_stmt = "INSERT INTO pages(title, body) VALUES('Download', 'All SQLite source code...');";
if (sqlite3_exec(database, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to insert");
}
}
sqlite3_stmt *statement;
const char *query_stmt = "select * from pages where body match 'soft*';";
if (sqlite3_prepare_v2(database, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(#"%# - %#", [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)],
[[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)]);
} else {
NSLog(#"no results");
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
} else {
NSLog(#"Failed to open/create database");
}
After all, I've found the bug. It was in my code. In summary, that what I've found out:
If I have something like that (I know it's weird/wrong):
int a = 0;
a = a++;
NSLog(#"%d", a);
the logged value will be 1 if this code is compiled with gcc and 0 if compiled with llvm.
I don't know why, but that's another question :)
I working on an app that takes input from a text field and puts it into a string. I have a table with a field in it that I want to check the value of the string from the input against the value in the field in the database. I'm new to iOS and fairly new to SQLite.
Code:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
lblStoreNumber.text = strStoreNumber;
lblRegNumber.text = strRegNumber;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths lastObject];
// NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"tblStore.sqlite"];
NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opened sqlite database at %#", databasePath);
//...stuff
}
else
{
NSLog(#"Failed to open database at %# with error %s", databasePath, sqlite3_errmsg(database));
sqlite3_close (database);
}
NSString *querystring;
// create your statement
querystring = [NSString stringWithFormat:#"SELECT strStore FROM tblStore WHERE strStore = %#;", strStoreNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK) //queryString = Statement
{
NSLog(#"sql problem occured with: %s", sql);
NSLog(#"%s", sqlite3_errmsg(database));
}
else
{
// you could handle multiple rows here
while (sqlite3_step(databasePath) == SQLITE_ROW) // queryString = statement
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 1)];
} // while
}
sqlite3_finalize(databasePath);
// Do something with data...
}
It gets to the line "NSLog(#"Opened sqlite database at %#", databasePath);", so it appears as though it has access to the database. However, when I run the app, I get the "NSLog(#"sql problem occured with: %s", sql);" error, which I can see in the console. Additionally, in the console, it says "No such table: tblStore".
I created the table using the Firefox add-on SQLite Manager. I added the sqlite3 library to the project. I dragged and dropped the database table I created in SQLite manager into my project, above my two AppDelegate files and my two ViewController files.
Any help or input would be greatly appreciated. Thanks!
EDIT: I have properly added the file to the project, and it appears as though the table is found now. Now I have some strange warnings, though:
"Incompatible pointer types passing 'const char *' to parameter of type 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *')"
This warning appears on the following lines of code:
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK)
while (sqlite3_step(sql) == SQLITE_ROW)
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 1)];
sqlite3_finalize(sql);
It's got something to do with "sql", but I'm unsure of what. Any suggestions?
Your code seems ok - did you copy the db to the ressource folder of your project?
EDIT
Make sure you access your db file with something like that:
- (void) initializeDB {
// Get the database from the application bundle
NSString* path = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opening Database");
}
else
{
// Call close to properly clean up
sqlite3_close(database);
NSAssert1(0, #"Error: failed to open database: '%s'.",
sqlite3_errmsg(database));
}
}
The database file you add to the project will be embedded in the main NSBundle (see [NSBundle mainBundle]).
In order to do what you want, you need to copy the database from the main bundle to the documents folder before trying to access it. Otherwise, as you are experiencing, you will not be able to find the SQLite DB on the document's folder.
You can copy your database, click finder and write this address(/Users/administrator/Library/Application Support/iPhone Simulator/6.1/Applications/) in finder click ok.
You will get documentary path.
Open your project document file and paste your database....