Showing posts with label SQLite in IOS. Show all posts
Showing posts with label SQLite in IOS. Show all posts

Tuesday, December 15, 2015

How To Use SQLite to Manage Data in iOS Apps

iOS SQLite Database


                                                               Sqlite.h


//
//  Sqlite.h
//

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface Sqlite : NSObject {
NSInteger busyRetryTimeout;
NSString *filePath;
sqlite3 *_db;
}

@property (readwriteNSInteger busyRetryTimeout;
@property (readonlyNSString *filePath;

+ (NSString *)createUuid;
+ (NSString *)version;

- (id)initWithFile:(NSString *)filePath;

- (BOOL)open:(NSString *)filePath;
- (void)close;

- (NSInteger)errorCode;
- (NSString *)errorMessage;

- (NSArray *)executeQuery:(NSString *)sql, ...;
- (NSArray *)executeQuery:(NSString *)sql arguments:(NSArray *)args;

- (BOOL)executeNonQuery:(NSString *)sql, ...;
- (BOOL)executeNonQuery:(NSString *)sql arguments:(NSArray *)args;

- (BOOL)commit;
- (BOOL)rollback;
- (BOOL)beginTransaction;
- (BOOL)beginDeferredTransaction;

@end


----------------------------------------------------------------------------------------------------------------------

                                                                Sqlite.m

//
//  Sqlite.m
//  ScireNotes
//
//  Created by Alpesh Kothari on 11/9/11.
//  Copyright (c) 2012 ScireInc. All rights reserved.
//

#import "Sqlite.h"

/* ============================================================================
 */
@interface Sqlite (PRIVATE)
- (BOOL)executeStatament:(sqlite3_stmt *)stmt;
- (BOOL)prepareSql:(NSString *)sql inStatament:(sqlite3_stmt **)stmt;
- (void) bindObject:(id)obj toColumn:(int)idx inStatament:(sqlite3_stmt *)stmt;

- (BOOL)hasData:(sqlite3_stmt *)stmt;
- (id)columnData:(sqlite3_stmt *)stmt columnIndex:(NSInteger)index;
- (NSString *)columnName:(sqlite3_stmt *)stmt columnIndex:(NSInteger)index;
@end

@implementation Sqlite

@synthesize busyRetryTimeout;
@synthesize filePath;

+ (NSString *)createUuid {
CFUUIDRef uuidRef = CFUUIDCreate(NULL);
CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
CFRelease(uuidRef);
return([(NSString *) uuidStringRef autorelease]);
}

+ (NSString *)version {
return [NSString stringWithFormat:@"%s"sqlite3_libversion()];
}

- (id)init {
if ((self = [super init])) {
busyRetryTimeout = 1;
filePath = nil;
_db = nil;
}
    
return self;
}

- (id)initWithFile:(NSString *)dbFilePath {
if (self = [super init]) {
[self open:dbFilePath];
}
return self;
}

- (void)dealloc {
[self close];
    
[super dealloc];
}

- (BOOL)open:(NSString *)path {
[self close];
if (sqlite3_open([path fileSystemRepresentation], &_db) != SQLITE_OK) {
NSLog(@"SQLite Opening Error: %s"sqlite3_errmsg(_db));
return NO;
}
    
filePath = [path retain];
return YES;
}

- (void)close {
if (_db == nilreturn;
    
int numOfRetries = 0;
int rc;
do {
rc = sqlite3_close(_db);
if (rc == SQLITE_OK)
break;
        
if (rc == SQLITE_BUSY) {
usleep(20);
            
if (numOfRetries == busyRetryTimeout) {
NSLog(@"SQLite Busy, unable to close: %@"filePath);
break;
}
else {
NSLog(@"SQLite %@ Closing Error: %s"filePathsqlite3_errmsg(_db));
break;
}
while (numOfRetries++ > busyRetryTimeout);
    
[filePath release];
filePath = nil;
_db = nil;
}

- (NSInteger)errorCode {
return sqlite3_errcode(_db);
}

- (NSString *)errorMessage {
return [NSString stringWithFormat:@"%s"sqlite3_errmsg(_db)];
}

- (NSArray *)executeQuery:(NSString *)sql, ... {
va_list args;
va_start(args, sql);
    
NSMutableArray *argsArray = [[NSMutableArray allocinit];
NSUInteger i;
for (i = 0; i < [sql length]; ++i) {
if ([sql characterAtIndex:i] == '?')
[argsArray addObject:va_arg(args, id)];
}
va_end(args);
NSArray *result = [self executeQuery:sql arguments:argsArray];
    
[argsArray release];
return result;
}

- (NSArray *)executeQuery:(NSString *)sql arguments:(NSArray *)args {
sqlite3_stmt *sqlStmt;
if (![self prepareSql:sql inStatament:(&sqlStmt)])
return nil;
    
int i = 0;
int queryParamCount = sqlite3_bind_parameter_count(sqlStmt);
while (i++ < queryParamCount)
[self bindObject:[args objectAtIndex:(i - 1)] toColumn:i inStatament:sqlStmt];
    
NSMutableArray *arrayList = [[NSMutableArray allocinit];
int columnCount = sqlite3_column_count(sqlStmt);
while ([self hasData:sqlStmt]) {
NSMutableDictionary *dictionary = [[NSMutableDictionary allocinit];
for (i = 0; i < columnCount; ++i) {
id columnName = [self columnName:sqlStmt columnIndex:i];
            // id columnData = [self columnData:sqlStmt columnIndex:i];
            NSString *columnData= [NSString stringWithUTF8String:(constchar*)sqlite3_column_text(sqlStmt, i)];
            //NSLog(@"%@",columnData);
[dictionary setObject:columnData forKey:columnName];
}
[arrayList addObject:[dictionary autorelease]];
}
    
sqlite3_finalize(sqlStmt);
    
return arrayList;
}

- (BOOL)executeNonQuery:(NSString *)sql, ... {
va_list args;
va_start(args, sql);
    
NSMutableArray *argsArray = [[NSMutableArray allocinit];
    // KP we currently have issue with having "?" in any text we want to insert.
    // To remedy this as a temporary hack I am removing the follwing check
    // This means that we will not be ablt to use "?" and associated variable
    // in a query. Currently we are not using any such query so we are safe
    // and we will not be breaking any functionality.
    
    //    NSUInteger i;
    // for (i = 0; i < [sql length]; ++i) {
    // if ([sql characterAtIndex:i] == '?')
    // [argsArray addObject:va_arg(args, id)];
    // }
va_end(args);
BOOL success = [self executeNonQuery:sql arguments:argsArray];
    
[argsArray release];
return success;
}

- (BOOL)executeNonQuery:(NSString *)sql arguments:(NSArray *)args {
sqlite3_stmt *sqlStmt;
    
if (![self prepareSql:sql inStatament:(&sqlStmt)])
return NO;
    
int i = 0;
int queryParamCount = sqlite3_bind_parameter_count(sqlStmt);
while (i++ < queryParamCount)
[self bindObject:[args objectAtIndex:(i - 1)] toColumn:i inStatament:sqlStmt];
    
BOOL success = [self executeStatament:sqlStmt];
    
sqlite3_finalize(sqlStmt);
return success;
}

- (BOOL)commit {
return [self executeNonQuery:@"COMMIT TRANSACTION;"];
}

- (BOOL)rollback {
return [self executeNonQuery:@"ROLLBACK TRANSACTION;"];
}

- (BOOL)beginTransaction {
return [self executeNonQuery:@"BEGIN EXCLUSIVE TRANSACTION;"];
}

- (BOOL)beginDeferredTransaction {
return [self executeNonQuery:@"BEGIN DEFERRED TRANSACTION;"];
}

/* ============================================================================
 *  PRIVATE Methods
 */

- (BOOL)prepareSql:(NSString *)sql inStatament:(sqlite3_stmt **)stmt {
int numOfRetries = 0;
int rc;
    
do {
rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, stmt, NULL);
if (rc == SQLITE_OK)
return YES;
        
if (rc == SQLITE_BUSY) {
usleep(20);
            
if (numOfRetries == busyRetryTimeout) {
NSLog(@"SQLite Busy: %@"filePath);
break;
}
else {
NSLog(@"SQLite Prepare Failed: %s"sqlite3_errmsg(_db));
NSLog(@" - Query: %@", sql);
break;
}
while (numOfRetries++ > busyRetryTimeout);
    
return NO;
}

- (BOOL)executeStatament:(sqlite3_stmt *)stmt {
int numOfRetries = 0;
int rc;
    
do {
rc = sqlite3_step(stmt);
if (rc == SQLITE_OK || rc == SQLITE_DONE)
return YES;
        
if (rc == SQLITE_BUSY) {
usleep(20);
            
if (numOfRetries == busyRetryTimeout) {
NSLog(@"SQLite Busy: %@"filePath);
break;
}
else {
NSLog(@"SQLite Step Failed: %s"sqlite3_errmsg(_db));
break;
}
while (numOfRetries++ > busyRetryTimeout);
    
return NO;
}

- (void)bindObject:(id)obj toColumn:(int)idx inStatament:(sqlite3_stmt *)stmt {
if (obj == nil || obj == [NSNull null]) {
sqlite3_bind_null(stmt, idx);
else if ([obj isKindOfClass:[NSData class]]) {
sqlite3_bind_blob(stmt, idx, [obj bytes], [obj length], SQLITE_STATIC);
else if ([obj isKindOfClass:[NSDate class]]) {
sqlite3_bind_double(stmt, idx, [obj timeIntervalSince1970]);
else if ([obj isKindOfClass:[NSNumber class]]) {
if (!strcmp([obj objCType], @encode(BOOL))) {
sqlite3_bind_int(stmt, idx, [obj boolValue] ? 1 : 0);
else if (!strcmp([obj objCType], @encode(int))) {
sqlite3_bind_int64(stmt, idx, [obj longValue]);
else if (!strcmp([obj objCType], @encode(long))) {
sqlite3_bind_int64(stmt, idx, [obj longValue]);
else if (!strcmp([obj objCType], @encode(float))) {
sqlite3_bind_double(stmt, idx, [obj floatValue]);
else if (!strcmp([obj objCType], @encode(double))) {
sqlite3_bind_double(stmt, idx, [obj doubleValue]);
else {
sqlite3_bind_text(stmt, idx, [[obj descriptionUTF8String], -1SQLITE_STATIC);
}
else {
sqlite3_bind_text(stmt, idx, [[obj descriptionUTF8String], -1SQLITE_STATIC);
}
}

- (BOOL)hasData:(sqlite3_stmt *)stmt {
int numOfRetries = 0;
int rc;
    
do {
rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW)
return YES;
        
if (rc == SQLITE_DONE)
break;
        
if (rc == SQLITE_BUSY) {
usleep(20);
            
if (numOfRetries == busyRetryTimeout) {
NSLog(@"SQLite Busy: %@"filePath);
break;
}
else {
NSLog(@"SQLite Prepare Failed: %s"sqlite3_errmsg(_db));
break;
}
while (numOfRetries++ > busyRetryTimeout);
    
return NO;
}

- (id)columnData:(sqlite3_stmt *)stmt columnIndex:(NSInteger)index {
int columnType = sqlite3_column_type(stmt, index);
    
if (columnType == SQLITE_NULL)
return([NSNull null]);
    
if (columnType == SQLITE_INTEGER)
return [NSNumber numberWithInt:sqlite3_column_int(stmt, index)];
    
if (columnType == SQLITE_FLOAT)
return [NSNumber numberWithDouble:sqlite3_column_double(stmt, index)];
    
if (columnType == SQLITE_TEXT) {
const unsigned char *text = sqlite3_column_text(stmt, index);
return [NSString stringWithFormat:@"%s", text];
}
    
if (columnType == SQLITE_BLOB) {
int nbytes = sqlite3_column_bytes(stmt, index);
const char *bytes = sqlite3_column_blob(stmt, index);
return [NSData dataWithBytes:bytes length:nbytes];
}
    
return nil;
}

- (NSString *)columnName:(sqlite3_stmt *)stmt columnIndex:(NSInteger)index {
    return [NSString stringWithUTF8String:sqlite3_column_name(stmt, index)];

return [NSString stringWithUTF8String:sqlite3_column_name(stmt, index)];
}

@end


----------------------------------------------------------------------------------------------------------------------

                                                                Appdelegate.m



@synthesize dbObject;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
    
    self.window = [[UIWindow allocinitWithFrame:[[UIScreen mainScreenbounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor clearColor];
    
    //initialize and mount SQLite========================//
    self.dbObject = [[Sqlite allocinit];
    NSLog(@"get DB : %d",[self.dbObject open:[self getDBPath]]);
    //=====================================//

    [[UIApplication sharedApplicationsetStatusBarStyle:UIStatusBarStyleLightContent];
    
    //[self.window setBackgroundColor:[UIColor colorWithRed:(41.0/255.0green:(47.0/255.0blue:(52.0/255.0alpha:0.5]];
    
    // define navigation controller and main view in window
    mainView = [[MainViewController alloc]init];
    navController = [[UINavigationController allocinitWithRootViewController:mainView];
    navController.navigationBarHidden = YES;
    [self.window setRootViewController:navController];
    
    [self.window makeKeyAndVisible];
    return YES;
}


// returns database path
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (NSString *)getDBPath{
    //-------------------------------------------------------------------------------------------------------------------------------------------------
    BOOL success = FALSE;
    NSString *databaseName = @"YourFileName.sqlite";
    
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMaskYES);
    NSString *documentsDir = [documentPaths objectAtIndex:0];
    NSString *databasePath = documentsDir;
    databasePath = [databasePath stringByAppendingString:[NSStringstringWithFormat:@"/%@",databaseName]];
    // 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) {
        
        NSLog(@"SQLLite file exists ! no need to copy");
    }
    else
    {
        // Get the path to the database in the application package
        NSString *databasePathFromApp = [[[NSBundle mainBundleresourcePath]stringByAppendingPathComponent:databaseName];
        NSError *error11;
        // Copy the database from the package to the users filesystem
        [fileManager copyItemAtPath:databasePathFromApp toPath:databasePatherror:&error11];
    }
    return databasePath;
}

----------------------------------------------------------------------------------------------------------------------

                                                                Appdelegate.h

@property (strongnonatomicSqlite *dbObject;