Use and Access Existing SQLite Database on iOS
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;
}
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
}
}
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 thesrc/sample
orsrc/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];
}
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
}