i got a sqlite database but something is wrong with my statement this is my method to open the database and to retrieve some data i need:
- (void)createEditableCopyOfDatabaseIfNeeded
{
// First, test for existence.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"monsterDB.DB"];
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success)
{
// TODO: auch aktuelle version ? -> server check
return;
}
// TODO: The writable database does not exist, so copy the default from server to the appropriate location.
}
- (sqlite3 *)getDBConnection
{
[self createEditableCopyOfDatabaseIfNeeded];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"monsterDB.DB"];
// Open the database. The database was prepared outside the application.
sqlite3 *newDBConnection;
if (sqlite3_open([path UTF8String], &newDBConnection) == SQLITE_OK)
{
NSLog(#"Database Successfully Opened :)");
}
else
{
NSLog(#"Error in opening database :(");
}
return newDBConnection;
}
- (DEMonster *)getEventByType:(NSString *)type
{
DEMonster *result = nil;
sqlite3 *connection = [self getDBConnection];
NSString *textString = [NSString stringWithFormat:#"SELECT * FROM monsters WHERE type = '%#' ORDER BY RANDOM() LIMIT 1;", #"city"]; // TODO: Hier type nehmen für testzwecke nur city
const char *text = [textString UTF8String];
sqlite3_stmt *select_statement;
if (sqlite3_prepare_v2(connection, text, -1, &select_statement, NULL) != SQLITE_OK)
{
// error
NSLog(#"Error");
sqlite3_close(connection);
return result;
}
if (sqlite3_step(select_statement) == SQLITE_ROW)
{
result = [[DEMonster alloc] init];
result.name = [[NSString alloc] initWithUTF8String:(char const *)sqlite3_column_text(select_statement, 1)];
result.attacks = [[NSMutableArray alloc] initWithObjects:[[NSString alloc] initWithUTF8String:(char const *)sqlite3_column_text(select_statement, 2)], [[NSString alloc] initWithUTF8String:(char const *)sqlite3_column_text(select_statement, 3)], nil];
result.defenses = [[NSMutableArray alloc] initWithObjects:[[NSString alloc] initWithUTF8String:(char const *)sqlite3_column_text(select_statement, 4)], [[NSString alloc] initWithUTF8String:(char const *)sqlite3_column_text(select_statement, 5)], nil];
int length = sqlite3_column_bytes(select_statement, 6);
NSData *imageData = [NSData dataWithBytes:sqlite3_column_blob(select_statement, 6) length:length];
result.monsterIcon = [UIImage imageWithData:imageData];
result.attackValue = sqlite3_column_double(select_statement, 12);
result.defenseValue = sqlite3_column_double(select_statement, 13);
result.attackValues = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithDouble:sqlite3_column_double(select_statement, 8)], [NSNumber numberWithDouble:sqlite3_column_double(select_statement, 9)], nil];
result.defenseValues = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithDouble:sqlite3_column_double(select_statement, 10)], [NSNumber numberWithDouble:sqlite3_column_double(select_statement, 11)], nil];
}
sqlite3_finalize(select_statement);
sqlite3_close(connection);
return (result);
}
Basically the problem is in the "getEventByType" method. Somehow i always get the "Error" NSLog, because my statement does not return "SQLITE_OK".
The output looks like this:
2014-04-15 22:24:15.805 MonsterSafari[3701:60b] Database Successfully Opened :)
2014-04-15 22:24:15.806 MonsterSafari[3701:60b] Error
As you can see the database looks to be opened successfully, but somehow the statement looks to be wrong. When i run the exact same statement in my sqlbrowser (some app i use to browse the sqlite database) the query works just fine. Any idea what could possibly be wrong ?
Thanks in advance
Related
I am new to iOS. I am trying to implement a SQLite database.
My problem is that when I go to fetch data from database, it takes a lot of time even though there are only 50 records inside the tables.
I did make a separate method for fetching data because many times I need to retrieve data for different tables.
My method:
-(NSArray*)fetch_data:(NSString *)table_name fields_arr:(NSMutableArray *)fields_arr{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent: #"test.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
sqlite3_stmt *SelectStatement;
NSMutableDictionary *record_dict;
NSString *SelectQry=#"select ";
NSString *lastField = [fields_arr lastObject];
for (int i =0; i<[fields_arr count]; i++) {
if (fields_arr[i] == lastField) {
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"%# ",fields_arr[i]]];
} else {
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"%#,",fields_arr[i]]];
}
}
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"from %#",table_name]];
const char *query_stmt = [SelectQry UTF8String];
NSMutableArray *resultArray = [[NSMutableArray alloc]init];
if (sqlite3_prepare_v2(database,
query_stmt, -1, &SelectStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(SelectStatement) == SQLITE_ROW)
{
record_dict=[[NSMutableDictionary alloc]init];
for (int i = 0; i<[fields_arr count]; i++) {
NSString *field_value = [[NSString alloc]init];
field_value = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(SelectStatement, i)];
[record_dict setValue:field_value forKey:fields_arr[i]];
}
[resultArray addObject:record_dict];
}
return resultArray;
//sqlite3_reset(statement);
sqlite3_finalize(SelectStatement);
}
else{
sqlite3_finalize(SelectStatement);
return nil;
}
}
In your code you are opening database connection for each time method is called for fetching data. This is not appropriate way. You must establish connection only once and use it.
+(sqlite3 *)openDatabase{
if (database == NULL) {
NSString* str_path=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [str_path stringByAppendingPathComponent:strDatabaseName];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
NSLog(#"Database Successfully Opened");
}
else {
NSLog(#"Error in opening database");
database = NULL;
}
}
return database;}
You can call this on app launch from app delegate and define this method in a class where you are defining other DB methods. I hope it will reduce some overhead and will reduce some time too.
I'm working on an app that displays various feeds using ASIHTTPRequest. The user can add new sources to the app that are stored in an SQLite database. Currently I give the feeds to the app in the viewDidLoad method of my FeedsViewController but i want to be able to retrieve the data that contains the link for the source and store it in an array to use it.
Currently the app looks like and below the current code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.title =#"Lajmet";
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
//self.tabBarController.tabBar.tintColor = [UIColor blackColor];
self.allEntries = [NSMutableArray array];
self.queue = [[NSOperationQueue alloc] init];
self.feeds = [NSMutableArray arrayWithObjects:#"http://pcworld.al/feed",#"http://geek.com/feed", #"http://feeds.feedburner.com/Mobilecrunch",#"http://zeri.info/rss/rss-5.xml",
nil];
[self.tableView reloadData];
[self refresh];
}
My view that contains the function (AddSourcesViewController) to add and delete new sources looks like this and here the code:
- (void)viewDidLoad
{
[super viewDidLoad];
arrayOfSource = [[NSMutableArray alloc]init];
[[self myTableView]setDelegate:self];
[[self myTableView]setDataSource:self];
[self createOrOpenDB];
// Display sources in table view
sqlite3_stmt *statement;
if (sqlite3_open([dbPathString UTF8String], &sourceDB)==SQLITE_OK) {
[arrayOfSource removeAllObjects];
NSString *querySql = [NSString stringWithFormat:#"SELECT * FROM SOURCES"];
const char* query_sql = [querySql UTF8String];
if (sqlite3_prepare(sourceDB, query_sql, -1, &statement, NULL)==SQLITE_OK) {
while (sqlite3_step(statement)==SQLITE_ROW) {
NSString *name = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
NSString *category = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
SourceDB *source = [[SourceDB alloc]init];
[source setName:name];
[source setLink:link];
[source setCategory:category];
[arrayOfSource addObject:source];
}
}
}
[[self myTableView]reloadData];
}
- (void)createOrOpenDB
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [path objectAtIndex:0];
dbPathString = [docPath stringByAppendingPathComponent:#"sources.db"];
char *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:dbPathString]) {
const char *dbPath = [dbPathString UTF8String];
//creat db here
if (sqlite3_open(dbPath, &sourceDB)==SQLITE_OK) {
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS SOURCES (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, LINK TEXT, CATEGORY TEXT)";
sqlite3_exec(sourceDB, sql_stmt, NULL, NULL, &error);
sqlite3_close(sourceDB);
}
}
}
- (IBAction)addSourceButton:(id)sender
{
char *error;
if (sqlite3_open([dbPathString UTF8String], &sourceDB)==SQLITE_OK) {
NSString *inserStmt = [NSString stringWithFormat:#"INSERT INTO SOURCES(NAME,LINK,CATEGORY) values ('%s', '%s','%s')",[self.nameField.text UTF8String], [self.linkField.text UTF8String],[self.categoryField.text UTF8String]];
const char *insert_stmt = [inserStmt UTF8String];
if (sqlite3_exec(sourceDB, insert_stmt, NULL, NULL, &error)==SQLITE_OK) {
NSLog(#"Source added");
SourceDB *source = [[SourceDB alloc]init];
[source setName:self.nameField.text];
[source setLink:self.linkField.text];
[source setCategory:self.categoryField.text];
[arrayOfSource addObject:source];
}
sqlite3_close(sourceDB);
}
}
- (IBAction)deleteSourceButton:(id)sender
{
[[self myTableView]setEditing:!self.myTableView.editing animated:YES];
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
SourceDB *s = [arrayOfSource objectAtIndex:indexPath.row];
[self deleteData:[NSString stringWithFormat:#"Delete from sources where name is '%s'", [s.name UTF8String]]];
[arrayOfSource removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
-(void)deleteData:(NSString *)deleteQuery
{
char *error;
if (sqlite3_exec(sourceDB, [deleteQuery UTF8String], NULL, NULL, &error)==SQLITE_OK) {
NSLog(#"Source deleted");
}
}
- (IBAction)showSourceButton:(id)sender
{
sqlite3_stmt *statement;
if (sqlite3_open([dbPathString UTF8String], &sourceDB)==SQLITE_OK) {
[arrayOfSource removeAllObjects];
NSString *querySql = [NSString stringWithFormat:#"SELECT * FROM SOURCES"];
const char* query_sql = [querySql UTF8String];
if (sqlite3_prepare(sourceDB, query_sql, -1, &statement, NULL)==SQLITE_OK) {
while (sqlite3_step(statement)==SQLITE_ROW) {
NSString *name = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
NSString *category = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
SourceDB *source = [[SourceDB alloc]init];
[source setName:name];
[source setLink:link];
[source setCategory:category];
[arrayOfSource addObject:source];
}
}
}
[[self myTableView]reloadData];
}
Now i created a method in my FeedsViewConrtoller:
-(void)ReadData
{
[self createOrOpenDB];
feedsArray = [[NSMutableArray alloc] init];
const char *dbPath = [dbPathString UTF8String];
if (sqlite3_open(dbPath, &sourceDB)==SQLITE_OK)
{
const char *sqlstmt = "SELECT LINK FROM SOURCES";
sqlite3_stmt *completedstmt;
if (sqlite3_prepare_v2(sourceDB, sqlstmt, -1, &completedstmt, NULL)==SQLITE_OK)
{
while(sqlite3_step(completedstmt)==SQLITE_ROW)
{
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(completedstmt, 2)];
SourceDB *source = [[SourceDB alloc]init];
[source setLink:link];
[feedsArray addObject:link];
}
}
}
}
And when i change
self.feeds = [NSMutableArray arrayWithObjects:#"http://pcworld.al/feed",#"http://geek.com/feed", #"http://feeds.feedburner.com/Mobilecrunch",#"http://zeri.info/rss/rss-5.xml",
nil];
to:
self.feeds = [NSMutableArray arrayWithArray:feedsArray];
i get the following error:
2013-08-14 15:03:55.328 ShqipCom[3128:c07] (null)
What am i doing wrong here?
Thanks a lot!
Granit
In ReadData, you're returning one column, but then retrieving the third column with:
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(completedstmt, 2)];
That should be:
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(completedstmt, 0)];
A couple of other thoughts:
Part of the reason it's hard for us to answer this is that you aren't reporting SQLite errors. I'd suggest logging when a SQLite test failed, e.g.:
if (sqlite3_prepare_v2(sourceDB, sqlstmt, -1, &completedstmt, NULL)==SQLITE_OK
{
while(sqlite3_step(completedstmt)==SQLITE_ROW)
{
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(completedstmt, 2)];
SourceDB *source = [[SourceDB alloc]init];
[source setLink:link];
[feedsArray addObject:link];
}
}
whereas you might want something like:
if (sqlite3_prepare_v2(sourceDB, sqlstmt, -1, &completedstmt, NULL)!=SQLITE_OK
{
NSLog(#"%s: prepare error: %s", __FUNCTION__, sqlite3_errmsg(sourceDB));
return;
}
int rc;
while ((rc = sqlite3_step(completedstmt)) == SQLITE_ROW)
{
NSString *link = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(completedstmt, 2)];
SourceDB *source = [[SourceDB alloc]init];
[source setLink:link];
[feedsArray addObject:link];
}
if (rc != SQLITE_DONE)
{
NSLog(#"%s: step error: %s", __FUNCTION__, sqlite3_errmsg(sourceDB));
return;
}
You really should be checking the result of every SQLite call, and reporting an error message if not successful. Otherwise you're flying blind.
On your INSERT statement, are you 100% sure that those three fields will never have an apostrophe in them? (Also, if the user is entering any of this data, you have to be concerned about SQL injection attacks.) Generally you'd use ? placeholders in your SQL, you'd prepare the SQL with sqlite3_prepare_v2, and then use sqlite3_bind_text to bind text values to those placeholders, and then you'd sqlite3_step and confirm a return value of SQLITE_DONE.
Note, checking each and every sqlite3_xxx function call return result and doing all of the necessary sqlite3_bind_xxx calls is a little cumbersome. In your next project, if you want to streamline your SQLite code, you might want to consider using FMDB.
Your can add any object in NSMutableArray by
[myArray name addObject:mYString]; // add whatever object here;
if you want to add Array to you NSMutableArray
[myArray addObjectsFromArray:anOtherArray];
I have a fully populated database in SQLite that I'd like to use in my new app. It's rather large, so I'd like to avoid changing it to another format if possible. How can I use this database in such a way that it ships with my app?
EDIT: If I just drop the file into my Supported Files directory, for example, how can I access it? How do I reference it?
SQLite database interaction can be made simple and clean by using FMDB Framework. FMDB is an Objective-C wrapper for the SQLite C interface.
Reference worth reading:
FMDB Framework Docs
Sample Project With Storyboard
Initial Setup
Add the SQLite DB like any other file in your application's bundle then copy the database to documents directory using the following code then use the database from the documents directory
First download the FMDB framework
Extract the framework now copy all the file from src/fmdb folder (not the src/sample or src/extra folders).
Click your project in the left column of Xcode.
Click the main target in the middle column.
Click the “Build Phases” tab.
Expand the arrow next to “Link Binary With Libraries”.
Click the “+” button.
Search for libsqlite3.0.dylib and double click it.
Copying your existing database into app's document in didFinishLaunchingWithOptions: and maintain the database path through out the application.
In your AppDelegate add the following code.
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
// Application Start
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Function called to create a copy of the database if needed.
[self createCopyOfDatabaseIfNeeded];
return YES;
}
#pragma mark - Defined Functions
// Function to Create a writable copy of the bundled default database in the application Documents directory.
- (void)createCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Database filename can have extension db/sqlite.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appDBPath = [documentsDirectory stringByAppendingPathComponent:#"database-name.sqlite"];
success = [fileManager fileExistsAtPath:appDBPath];
if (success) {
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"database-name.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:appDBPath error:&error];
NSAssert(success, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
YourViewController.m
Select Query
#import "FMDatabase.h"
- (void)getAllData {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"database-name.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *sqlSelectQuery = #"SELECT * FROM tablename";
// Query result
FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
while([resultsWithNameLocation next]) {
NSString *strID = [NSString stringWithFormat:#"%d",[resultsWithNameLocation intForColumn:#"ID"]];
NSString *strName = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"Name"]];
NSString *strLoc = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"Location"]];
// loading your data into the array, dictionaries.
NSLog(#"ID = %d, Name = %#, Location = %#",strID, strName, strLoc);
}
[database close];
}
Insert Query
#import "FMDatabase.h"
- (void)insertData {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"database-name.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *insertQuery = [NSString stringWithFormat:#"INSERT INTO user VALUES ('%#', %d)", #"Jobin Kurian", 25];
[database executeUpdate:insertQuery];
[database close];
}
Update Query
- (void)updateDate {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"fmdb-sample.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *insertQuery = [NSString stringWithFormat:#"UPDATE users SET age = '%#' WHERE username = '%#'", #"23", #"colin" ];
[database executeUpdate:insertQuery];
[database close];
}
Delete Query
#import "FMDatabase.h"
- (void)deleteData {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"database-name.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *deleteQuery = #"DELETE FROM user WHERE age = 25";
[database executeUpdate:deleteQuery];
[database close];
}
Addition Functionality
Getting the row count
Make sure to include the FMDatabaseAdditions.h file to use intForQuery:.
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
- (void)gettingRowCount {
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"database-name.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSUInteger count = [database intForQuery:#"SELECT COUNT(field_name) FROM table_name"];
[database close];
}
Add the Sqlite DB like any other file in your application bundle
Copy it to documents directory via code and use it .The purpose of this is that updating content in sqlite is possible in Documents directory only
-(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:_databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:_databasePath error:nil];
}
- (id)init {
if ((self = [super init]))
{
_databaseName = DB_NAME;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
_databasePath = [documentsDir stringByAppendingPathComponent:_databaseName];
if (sqlite3_open([[self dbPath] UTF8String], &_database) != SQLITE_OK)
{
[[[UIAlertView alloc]initWithTitle:#"Missing"
message:#"Database file not found"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil]show];
}
}
return self;
}
This following methods will help you to handle database
Method for copy database in document directory if not exist
-(void)copyDatabase
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *insPath = [NSString stringWithFormat:#"Instamontage.sqlite"];
destPath = [documentsDirectory stringByAppendingPathComponent:insPath];
NSString *srcPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:insPath];
// NSLog(#"\n src %# \n dest %#", srcPath, destPath);
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
NSError *error;
NSLog(#"not exist");
[[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:destPath error:&error];
}
else
{
NSLog(#"exist");
}
}
Method for insert/deleting/updating table
-(BOOL)dataManipulation: (NSString *)query
{
BOOL result=NO;
if (sqlite3_open([destPath UTF8String], &connectDatabase)==SQLITE_OK)
{
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(connectDatabase, [query UTF8String], -1, &stmt, NULL)==SQLITE_OK)
{
sqlite3_step(stmt);
result=YES;
}
sqlite3_finalize(stmt);
}
sqlite3_close(connectDatabase);
return result;
}
Method for getting rows from table
-(NSMutableArray *)getData: (NSString *)query
{
NSMutableArray *arrData=[[NSMutableArray alloc]init];
if (sqlite3_open([destPath UTF8String],&connectDatabase)==SQLITE_OK)
{
sqlite3_stmt *stmt;
const char *query_stmt = [query UTF8String];
if (sqlite3_prepare_v2(connectDatabase,query_stmt, -1, &stmt, NULL)==SQLITE_OK)
{
while (sqlite3_step(stmt)==SQLITE_ROW)
{
NSMutableDictionary *dictResult=[[NSMutableDictionary alloc] init];
for (int i=0;i<sqlite3_column_count(stmt);i++)
{
NSString *str;
if (sqlite3_column_text(stmt,i)!=NULL)
{
str = [NSString stringWithUTF8String:(char *)sqlite3_column_text(stmt,i)];
}
else
{
str=#"";
}
[dictResult setValue:str forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(stmt,i)]];
}
[arrData addObject:dictResult];
}
sqlite3_finalize(stmt);
}
sqlite3_close(connectDatabase);
}
return arrData;
}
Above methods in swift will written as below
Method for copy database in document directory if not exist
func copyDatabaseToDocumentDirectory() {
let directoryList = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
var documentDirectory = directoryList.first
documentDirectory?.append("/DatabasePract1.sqlite")
print(documentDirectory!)
if !FileManager.default.fileExists(atPath: documentDirectory!) {
let databaseBundlePath = Bundle.main.path(forResource: "DatabasePract1", ofType: "sqlite")
do {
try FileManager.default.copyItem(atPath: databaseBundlePath!, toPath: documentDirectory!)
self.databasePath = documentDirectory
} catch {
print("Unable to copy database.")
}
} else {
print("database exist")
self.databasePath = documentDirectory
}
}
Method for insert/deleting/updating table
func dataManipulation(query: String) -> Bool {
var database: OpaquePointer?
var result = false
if (sqlite3_open(databasePath, &database) == SQLITE_OK) {
var queryStatement: OpaquePointer?
if (sqlite3_prepare_v2(database, query, -1, &queryStatement, nil) == SQLITE_OK) {
sqlite3_step(queryStatement)
result = true
} else {
let errmsg = String(cString: sqlite3_errmsg(database)!)
print("error preparing insert: \(errmsg)")
}
sqlite3_finalize(queryStatement)
}
sqlite3_close(database)
return result
}
Method for getting rows from table
func fetchData(_ query: String) -> [[String:Any]] {
var database: OpaquePointer?
var arrData: [[String:Any]] = []
if (sqlite3_open(databasePath, &database) == SQLITE_OK) {
var stmt:OpaquePointer?
if sqlite3_prepare(database, query, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(database)!)
print("error preparing insert: \(errmsg)")
return arrData
}
while(sqlite3_step(stmt) == SQLITE_ROW) {
var dictData: [String: Any] = [:]
for i in 0..<sqlite3_column_count(stmt) {
var strValue = ""
if (sqlite3_column_text(stmt, i) != nil) {
strValue = String(cString: sqlite3_column_text(stmt, i))
}
let keyName = String(cString: sqlite3_column_name(stmt, i), encoding: .utf8)
dictData[keyName!] = strValue
}
arrData.append(dictData)
}
sqlite3_close(database)
}
return arrData
}
Using swift, singleton class and FMDB. you can use below code to achieve it very easily.
Download example
import Foundation
class LocalDatabase: NSObject {
//sharedInstance
static let sharedInstance = LocalDatabase()
func methodToCreateDatabase() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory:NSURL = urls.first { // No use of as? NSURL because let urls returns array of NSURL
// exclude cloud backup
do {
try documentDirectory.setResourceValue(true, forKey: NSURLIsExcludedFromBackupKey)
} catch _{
print("Failed to exclude backup")
}
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("contact.db")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("contact", withExtension: "db") {
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch _ {
print("Couldn't copy file to final location!")
}
} else {
print("Couldn't find initial database in the bundle!")
}
}
} else {
print("Couldn't get documents directory!")
}
return nil
}
func methodToInsertUpdateDeleteData(strQuery : String) -> Bool
{
// print("%#",String(methodToCreateDatabase()!.absoluteString))
let contactDB = FMDatabase(path: String(methodToCreateDatabase()!.absoluteString) )
if contactDB.open() {
let insertSQL = strQuery
let result = contactDB.executeUpdate(insertSQL,
withArgumentsInArray: nil)
if !result {
print("Failed to add contact")
print("Error: \(contactDB.lastErrorMessage())")
return false
} else {
print("Contact Added")
return true
}
} else {
print("Error: \(contactDB.lastErrorMessage())")
return false
}
}
func methodToSelectData(strQuery : String) -> NSMutableArray
{
let arryToReturn : NSMutableArray = []
print("%#",String(methodToCreateDatabase()!.absoluteString))
let contactDB = FMDatabase(path: String(methodToCreateDatabase()!.absoluteString) )
if contactDB.open() {
let querySQL = strQuery
let results:FMResultSet? = contactDB.executeQuery(querySQL,
withArgumentsInArray: nil)
while results?.next() == true
{
arryToReturn.addObject(results!.resultDictionary())
}
// NSLog("%#", arryToReturn)
if arryToReturn.count == 0
{
print("Record Not Found")
}
else
{
print("Record Found")
}
contactDB.close()
} else {
print("Error: \(contactDB.lastErrorMessage())")
}
return arryToReturn
}
}
To copy .sqlite file in directory...
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Database filename can have extension db/sqlite.
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"MapView.sqlite"];
success = [fileManager fileExistsAtPath:databasePath];
// if (success){
// return;
// }
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"MapView.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:databasePath error:&error];
if (!success) {
//NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
else
{
NSLog(#"Database created successfully");
}
To select Data From Database...
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &mapDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM maplatlong"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(mapDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *cityN = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
NSString *lat = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
NSString *longi = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
[cityName addObject:cityN];
[latitude addObject:lat];
[longitude addObject:longi];
}
sqlite3_finalize(statement);
}
sqlite3_close(mapDB);
}
I have an app that reads from sqlite database,data is read and included in the objects using this method ....I checked with NSLog
#import "ViewController1.h"
#import "Ricetta.h"
#import "AppDelegate.h"
static sqlite3_stmt *leggiStatement = nil;
#interface ViewController1 ()
#end
#implementation ViewController1
#synthesize Webmain, oggetto2;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//percorso file su cartella documents
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *path = [documentsDir stringByAppendingPathComponent:#"Rice.sqlite"];
//controllo se il file esiste
if(![[NSFileManager defaultManager] fileExistsAtPath:path])
{
//se non esiste lo copio nella cartella documenti
NSString *pathLocale=[[NSBundle mainBundle] pathForResource:#"Rice" ofType:#"sqlite"];
if ([[NSFileManager defaultManager] copyItemAtPath:pathLocale toPath:path error:nil] == YES)
{
NSLog(#"copia eseguita");
}
}
[self personalizzaAspetto];
[self carica_ID];
// NSString * query = #" SELECT Immagine, Titolo, Descrizione FROM LIBRO";
// NSArray * arrayQuery = [[NSArray alloc] initWithObjects:#"Immagine",#"Titolo",#"Descrizione",nil];
// NSArray * arrayElementi = [self caricaValoriMain:query :arrayQuery];
Webmain= [[UIWebView alloc]initWithFrame:CGRectMake(0, 0, 320, 365)];
NSString *htmlString =[NSString stringWithFormat:#"<html> \n"
"<head> \n"
"<style type=\"text/css\"> \n"
"body {font-family: \"%#\"; font-size: %#;}\n"
"</style> \n"
"</head> \n"
"<body><center><img src='%#'/></center></body><center><h1>%#</h1></center><body bgcolor=\"#FFFFFF\" text=\" #ffa500\">%#</body></html>" ,#"futura",[NSNumber numberWithInt:15],oggetto2.Immagine,oggetto2.Titolo,oggetto2.Descrizione];
[Webmain loadHTMLString:htmlString baseURL:nil];
[self.view addSubview:Webmain];
-(void)carica_ID{
sqlite3 *database = NULL;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *dbPath = [documentsDir stringByAppendingPathComponent:#"Rice.sqlite"];
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
if(leggiStatement==nil){
const char *sql = "select Immagine,Titolo,Descrizione from LIBRO WHERE RicettaID=1";
if(sqlite3_prepare_v2(database, sql, -1, &leggiStatement, NULL) != SQLITE_OK)
NSAssert1(0, #"Errore creazione compiledState '%s'", sqlite3_errmsg(database));
}
//while(sqlite3_step(leggiStatement) == SQLITE_ROW)
if(SQLITE_DONE != sqlite3_step(leggiStatement))
{
NSString *titolo = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(leggiStatement, 1)];
NSLog(#"%#",titolo);
oggetto2.Titolo=titolo;
NSString *descrizione = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(leggiStatement, 2)];
NSLog(#"%#",descrizione);
oggetto2.Descrizione = descrizione;
NSString *image= [[NSString alloc]initWithUTF8String:(char *)sqlite3_column_text(leggiStatement, 0)];
NSLog(#"%#",image);
oggetto2.Immagine= image;
}
sqlite3_finalize(leggiStatement);
}
sqlite3_close(database);
}
#end
My problem is that I can not put them in webMain...objects in webMain remain empty.
I do not use Xib.
In the code snippet provided, you never perform the alloc and init of oggetto2. Thus, it is nil, and thus attempts to set its properties will achieve nothing.
In addition to your existing NSLog statements, I'd also suggest doing a NSLog of the htmlString right before you perform loadHTMLString, because it's easier to see what's going on with your HTML by looking at the source, rather than trying to make inferences from a blank web view.
Unrelated to your problem, but you probably should not have code that could potentially reusing your static sqlite3_stmt after you've finalized it. The first time you call carica_ID you would initialize the static leggiStatement. But you end up doing a sqlite_finalize but don't set leggiStatement to nil. If you ever called this method a second time, it won't sqlite3_prepare_v2 again, but you will have freed the resources associated with your prior leggiStatement.
A couple of easy fixes:
do not make leggiStatement a static global, but rather make it a local, non-static variable of the method;
if you do sqlite3_finalize, make sure you set leggiStatement to nil as well; or
don't call sqlite3_finalize, but rather just call sqlite3_reset, which will reset the prepared statement, but won't release its resources.
I am honestly not able to figure out when to release these objects. Could anyone guide me in the right direction?
+ (DrawData*) load {
DrawData *newDrawData = [[DrawData alloc] init];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *fm = [NSFileManager defaultManager];
BOOL keepLoading = YES;
int curIndex = 0;
while ( keepLoading ) {
BoardData *tmpBoard = [[BoardData alloc] init];
NSString *imageFilename = [[NSString alloc] initWithFormat:#"iCanvas_image_%d.png", curIndex];
NSString *metadataFilename = [[NSString alloc] initWithFormat:#"iCanvas_metadata_%d.txt", curIndex];
NSString *layersFilename = [[NSString alloc] initWithFormat:#"iCanvas_layers_%d.dat", curIndex];
imageFilename = [documentsDirectory stringByAppendingPathComponent:imageFilename];
metadataFilename = [documentsDirectory stringByAppendingPathComponent:metadataFilename];
if ( [fm fileExistsAtPath:imageFilename] && [fm fileExistsAtPath:metadataFilename] ) {
NSString *metadataFile = [[NSString alloc] initWithContentsOfFile:metadataFilename];
NSArray *metadata = [metadataFile componentsSeparatedByCharactersInSet:
[NSCharacterSet whitespaceCharacterSet]];
tmpBoard.drawImageTypeOverlay = [[metadata objectAtIndex:0] intValue];
tmpBoard.brushSize = [[metadata objectAtIndex:1] floatValue];
tmpBoard.brushColorRed = [[metadata objectAtIndex:2] floatValue];
tmpBoard.brushColorGreen = [[metadata objectAtIndex:3] floatValue];
tmpBoard.brushColorBlue = [[metadata objectAtIndex:4] floatValue];
tmpBoard.brushColorAlpha = [[metadata objectAtIndex:5] floatValue];
tmpBoard.isErasing = [[metadata objectAtIndex:6] intValue];
tmpBoard.eraseSize = [[metadata objectAtIndex:7] floatValue];
tmpBoard.isSelected = [[metadata objectAtIndex:8] intValue];
tmpBoard.layers = [NSMutableArray arrayWithContentsOfFile:layersFilename];
[newDrawData addBoard:tmpBoard];
}
else {
keepLoading = NO;
}
[tmpBoard release];
}
[pool release];
return newDrawData;
}
You should get in the habit of either:
1) Adding to autorelease pool
NSString *imageFilename = [[[NSString alloc] initWithFormat:#"iCanvas_image_%d.png", curIndex] autorelease];
OR
2) sending a release message (when done working with it)
[imageFilename release];
to variables you declare with this pattern:
NSString *imageFilename = [[NSString alloc] initWithFormat:#"iCanvas_image_%d.png", curIndex];
Which is basically
[[blah alloc] init];
Read more: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/
I'm not sure if you want to return autoreleased newDrawData - check your ownership logic.
imageFilename and metadataFilename are overwritten directly after +alloc/-initWithFormat:
metadataFile is allocated but not released.
Points 2 and 3 are repeated in a while loop which may increase the number of actual leaks.