GitHub - nick22dev/SQLiteQueryUtil: Set of block functions to wrap common SQLite operations on iOS in Objective-c

3 min read Original article ↗

SQLiteQueryUtil

Set of block functions to wrap common SQLite operations on iOS in Objective-c

Dependencies: libsqlite3.dylib

(libsqlite3 is included in Xcode's iOS distribution. Select this lib from Xcode Project->Build Phases->Link Binary With Libraries)

example: init

NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *databasePath = [[docsDir stringByAppendingPathComponent:@"database"] stringByAppendingPathExtension:@"sqlite"];

SQLiteQueryUtil *queryUtil = [[SQLiteQueryUtil alloc] initWithDBPath:databasePath];

example: select

/* init SQLiteQueryUtil queryUtil instance with database path */

sqlite3_int64 fooIdToQuery = 100;
__block NSMutableArray *foos = nil;

NSString *query = @"select id,name from foo where id=?;";

[queryUtil queryDB:query withBindParamsCallback:^(sqlite3_stmt *queryStatement) {

    sqlite3_bind_int64(queryStatement, 1, fooIdToQuery); // bind params start at 1

} onNextRowCallback:^(sqlite3_stmt *queryStatement, NSUInteger currentRow) {
    if(currentRow == 0) {
        foos = [[NSMutableArray alloc] init];
    }

    sqlite_int64 fooId = sqlite3_column_int64(queryStatement, 0); // column results start at 0
    const unsigned char *fooNameChars = sqlite3_column_text(queryStatement, 1);

    NSString *fooName = fooNameChars != NULL ? [[NSString alloc] initWithUTF8String:fooNameChars] : nil;

    Foo *nextFoo = [[Foo alloc] init];
    nextFoo.id = fooId;
    nextFoo.name = fooName;

    [foos addObject:nextFoo];

} onQueryCompleteCallack:^{ }];

example: insert

/* init SQLiteQueryUtil queryUtil instance with database path */

__block sqlite3_int64 insertResultId = -1;
NSString *nameToInsert = "mike";

NSString *query = @"insert into foo (name) values(?);";

[queryUtil writeQueryInDB:insertSQL withBindParamsCallback:^(sqlite3_stmt *queryStatement) {
    sqlite3_bind_text(queryStatement, 1, [nameToInsert UTF8String], -1, SQLITE_TRANSIENT); // bind params start at 1
} onNextRowCallback:^(sqlite3_stmt *queryStatement, NSUInteger currentRow) {
    insertResultId = sqlite3_last_insert_rowid(database);
} onQueryCompleteCallack:^{ }];

example: delete

/* init SQLiteQueryUtil queryUtil instance with database path */

__block BOOL success = NO;
sqlite3_int64 idToDelete = 100;

NSString *query = @"delete from foo where id=?;";

[queryUtil writeQueryInDB:insertSQL withBindParamsCallback:^(sqlite3_stmt *queryStatement) {
    sqlite3_bind_int64(queryStatement, 1, idToDelete); // bind params start at 1
} onNextRowCallback:^(sqlite3_stmt *queryStatement, NSUInteger currentRow) {
    success = YES;
} onQueryCompleteCallack:^{ }];

example: transaction (multiple deletes atomically)

/* init SQLiteQueryUtil queryUtil instance with database path */

[self.queryUtil writeTransactionWithOperations:@[^BOOL(sqlite3 *db, NSMutableDictionary *contextData) {
    __block BOOL success = NO;
    /* see delete example */
    return success;
},^BOOL(sqlite3 *db, NSMutableDictionary *contextData) {
    __block BOOL success = NO;
    /* delete from index table by id */
    return success;
}]];

example: migration (add an index)

/* init SQLiteQueryUtil queryUtil instance with database path */

int32_t currentdbVersion = [queryUtil dbVersion];

int32_t rollbackDBVersion = 1;
int32_t targetDBVersion = 2;

__block sqlite3 *transactionDBConn = nil;
int openResult = [queryUtil openDBReadWrite:&transactionDBConn];

NSString *foo_name_index_create_sql_stmtString = "create index if not exists foo_name_index on foo(name);"

[queryUtil migrate:^BOOL{
    return currentdbVersion == 1 && openResult == SQLITE_OK;
} migrate:^{
    NSLog(@"[SQLITE_MIGRATOR] Migration from v%d to v%d", currentdbVersion, targetDBVersion);
    [queryUtil writeQueryInDB:foo_name_index_create_sql_stmtString withDB:&transactionDBConn withBindParamsCallback:nil onNextRowCallback:nil onQueryCompleteCallack:nil];
    
    // update the db user version so we know migration was successful going forward
    [queryUtil setdbVersion:targetDBVersion withDB:&transactionDBConn];
    
} didMigrationSucceed:^BOOL{
    return YES;
} rollback:^{
    NSLog(@"[SQLITE_MIGRATOR] Migration Rollback to v%d", rollbackDBVersion);
    
    if (sqlite3_exec(transactionDBConn, "ROLLBACK;", 0, 0, 0) != SQLITE_OK) {
        NSLog(@"[SQLITE_MIGRATOR] SQL Error: %s",sqlite3_errmsg(transactionDBConn));
    }
    [queryUtil setdbVersion:rollbackDBVersion withDB:&transactionDBConn];
} onMigrationComplete:^(BOOL didComplete) {
    int closeResult = sqlite3_close(transactionDBConn);
    if(closeResult != SQLITE_OK) {
        NSLog(@"[SQLITE] Error failed to close db %d %s", closeResult, sqlite3_errmsg(transactionDBConn));
    }
    NSLog(@"[SQLITE_MIGRATOR] Migration from v%d to v%d %@",currentdbVersion, targetDBVersion, didComplete ? @"SUCCESS" : @"FAIL");
}