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.
Related
I am using Xcode 6.4, developing apps for iOS 8 that need to use database. I inserted some information into my database ("something.db" file). I want to see the content of "something.db" to see what I inserted. How can I view its content?
This is my code for creating the database "company.db" and table "Staff"(ID PK, NAME TEXT, AGE INTEGER):
-(void) createOrOpenDB {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:#"company.db"];
char *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:dbPathString]) {
const char *dbPath =[dbPathString UTF8String];
if(sqlite3_open(dbPath, &personDB)==SQLITE_OK) {
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS Staff (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, AGE INTEGER)";
sqlite3_exec(personDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(personDB);
}
}
}
This is my code for inserting information into the table "Staff":
- (IBAction)addPersonButton:(id)sender {
char *error;
if(sqlite3_open([dbPathString UTF8String], &personDB) == SQLITE_OK) {
NSString *insertStmt = [NSString stringWithFormat:#"INSERT INTO Staff (NAME, AGE) values ('%s', '%d')", [self.nameField.text UTF8String], [self.ageField.text intValue]];
const char *insert_stmt = [insertStmt UTF8String];
if(sqlite3_exec(personDB, insert_stmt, NULL, NULL, &error) == SQLITE_OK) {
NSLog(#"Person added");
Person *person = [[Person alloc] init];
[person setName:self.nameField.text];
[person setAge:[self.ageField.text intValue]];
[arrayofPerson addObject:person];
}
sqlite3_close(personDB);
}
}
The code works well because I was able to display the information of the "Staff" table in the tableview. But how to view that information in the .db file?
p/s: I changed it to "something.xls" but Excel does not display human readable information. I also downloaded Fileviewpro but did not know how to use. Someone helps me please!
Thank you very much!
You can use any app that is designed to view the contents of an sqlite file. for example: http://sqlitebrowser.org
If you are running on simulator you can use ~/Library/Developer/CoreSimulator/Devices/1A8DF360-B0A6-4815-95F3-68A6AB0BCC78/data/Container/Data/Application/
to get to the simulator just replace the long UDID with your device.
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.
Hi in Application i have already inserted some data into my sqlite database now i want to check the particular record is already inserted or not in sqlite. I'm working in UITableView. I want to check using my product id it is a unique id. Id which comes from my server. I want check the particular id is exit in my sqlite database. I have stored my id in NSMutableArray.
My code to store the arrayid into a string in my UITableViewcell.
for (int i=0; i<[menuarray2 count]; i++) {
rowvalue =[menuarray2 objectAtIndex:i];
NSLog(#"rowid%#",rowvalue);
}
I have passed the string to my sqlite query its showing null.
-(void)checkdata:(NSString*)query{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"bp.sqlite"];
if(sqlite3_open([databasePath UTF8String],&_myDataBase) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"%#",query];
char *errmsg=nil;
if(sqlite3_exec(_myDataBase, [querySQL UTF8String], NULL, NULL, &errmsg)==SQLITE_OK)
{
NSLog(#".. id ..");
}
}
sqlite3_close(_myDataBase);
}
-(void)rowcount{
NSLog(#"rowdata%#",rowvalue);
NSString *sql = [NSString stringWithFormat:#"SELECT * from br where PID='%#'",rowvalue];
NSLog(#"%#",sql);
[self checkdata:sql];
}
I'm using above to check but its showing null in console which print the query like
SELECT * from br where PID='(null)'
Please tell me how to resolve this issue i have stuck here for long time please help me out.
Thanks..
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.
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"];`