diff --git a/Bridge.h b/Bridge.h new file mode 100644 index 0000000..7f68868 --- /dev/null +++ b/Bridge.h @@ -0,0 +1,15 @@ +// +// Bridge.h +// TasksGalore +// +// Created by Fahim Farook on 15/6/14. +// Copyright (c) 2014 RookSoft Pte. Ltd. All rights reserved. +// + +#import + +@interface Bridge : NSObject + ++(NSString *)esc:(NSString *)str; + +@end diff --git a/Bridge.m b/Bridge.m new file mode 100644 index 0000000..9c77713 --- /dev/null +++ b/Bridge.m @@ -0,0 +1,22 @@ +// +// Bridge.m +// TasksGalore +// +// Created by Fahim Farook on 15/6/14. +// Copyright (c) 2014 RookSoft Pte. Ltd. All rights reserved. +// + +#import "Bridge.h" +#import "sqlite3.h" + +@implementation Bridge + ++(NSString *)esc:(NSString *)str { + if (!str || [str length] == 0) { + return @""; + } + NSString *buf = @(sqlite3_mprintf("%q", [str cStringUsingEncoding:NSUTF8StringEncoding])); + return buf; +} + +@end diff --git a/Bridging-Header.h b/Bridging-Header.h new file mode 100644 index 0000000..5ca86c4 --- /dev/null +++ b/Bridging-Header.h @@ -0,0 +1,11 @@ +// +// Bridging-Header.h +// TasksGalore +// +// Created by Fahim Farook on 12/6/14. +// Copyright (c) 2014 RookSoft Pte. Ltd. All rights reserved. +// + +#import "sqlite3.h" +#import +#import "Bridge.h" diff --git a/README.md b/README.md index 366935c..0408925 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,10 @@ This is a basic SQLite wrapper for Swift. It is very simple at the moment and do Adding to Your Project --- -* Create your SQLite database however you like but name it `data.db` and then add the `data.db` file to your Xcode project. (If you want to name the database file something other than `data.db`, then change the `DB_NAME` constant in the `SQLiteDB` class accordingly.) -* Add SQLiteDB.swift to your project -* If you don't have a bridging header file, create one. (It's just a header file - but it's usually named Bridging-Header.h or <projectname>-Bridging-Header.h). -* If you added a bridging header file, then make sure that you modify your project settings to point to the bridging header file. This will be under the "Build Settings" for your target and will be named "Objective-C Bridging Header". -* Add the following imports to your bridging header file: -```objective-c -#import -#import -``` +* Create your SQLite database however you like, but name it `data.db` and then add the `data.db` file to your Xcode project. (If you want to name the database file something other than `data.db`, then change the `DB_NAME` constant in the `SQLiteDB` class accordingly.) +* Add all of the included source files (except for README.md, of course) to your project. +* If you don't have a bridging header file, use the included `Bridging-Header.h` file. If you already have a bridging header file, then copy the contents from the included `Bridging-Header.h` file in to your own bridging header file. +* If you didn't have a bridging header file, make sure that you modify your project settings to point to the new bridging header file. This will be under the "Build Settings" for your target and will be named "Objective-C Bridging Header". * Add the SQLite library (libsqlite3.0.dylib) to your project under the "Build Phases" - "Link Binary With Libraries" section. That's it. You're set! diff --git a/SQLiteDB.swift b/SQLiteDB.swift index d8552d7..9aabb58 100644 --- a/SQLiteDB.swift +++ b/SQLiteDB.swift @@ -37,6 +37,7 @@ extension String { } class SQLRow { + let SQLITE_DATE = SQLITE_NULL + 1 var keys:String[] = String[]() var values:Any[] = Any[]() var types:CInt[] = CInt[]() @@ -56,9 +57,20 @@ class SQLRow { if type == SQLITE_INTEGER { return val as Int } - if type == SQLITE_TEXT { - return val as String + if type == SQLITE_FLOAT { + return val as Double + } + if type == SQLITE_BLOB { + return val as NSData + } + if type == SQLITE_NULL { + return nil + } + if type == SQLITE_DATE { + return val as NSDate } + // Return everything else as String + return val as String } return nil } @@ -69,14 +81,13 @@ class SQLiteDB { let SQLITE_DATE = SQLITE_NULL + 1 var db:COpaquePointer = nil var queue:dispatch_queue_t = dispatch_queue_create("SQLiteDB", nil) + struct Static { + static var instance: SQLiteDB? = nil + static var token: dispatch_once_t = 0 + } class func sharedInstance() -> SQLiteDB! { - struct Static { - static var instance: SQLiteDB? = nil - static var onceToken: dispatch_once_t = 0 - } - - dispatch_once(&Static.onceToken) { + dispatch_once(&Static.token) { println("SQLiteDB - Dispatch once") Static.instance = self() } @@ -85,6 +96,7 @@ class SQLiteDB { @required init() { println("SQLiteDB - Init method") + assert(Static.instance == nil, "Singleton already initialized!") // Get path to DB in Documents directory let docDir:AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] let dbName:String = String.fromCString(DB_NAME) @@ -151,7 +163,7 @@ class SQLiteDB { var cSql:CString = sql.bridgeToObjectiveC().cString() var stmt:COpaquePointer = nil // Prepare - result = sqlite3_prepare_v2(self.db, cSql, 0, &stmt, nil) + result = sqlite3_prepare_v2(self.db, cSql, -1, &stmt, nil) if result != SQLITE_OK { sqlite3_finalize(stmt) let msg = "SQLiteDB - failed to prepare SQL: \(sql), Error: \(self.lastSQLError())" @@ -161,7 +173,7 @@ class SQLiteDB { } // Step result = sqlite3_step(stmt) - if result != SQLITE_OK { + if result != SQLITE_OK && result != SQLITE_DONE { sqlite3_finalize(stmt) let msg = "SQLiteDB - failed to execute SQL: \(sql), Error: \(self.lastSQLError())" println(msg) @@ -233,11 +245,23 @@ class SQLiteDB { return rows } - // SQL escape string + // SQL escape string - hacky version using an intermediate Objective-C class to make it work func esc(str: String)->String { - var cstr:CString = str.bridgeToObjectiveC().cString() -// cstr = sqlite3_vmprintf("%Q", CVaListPointer(fromUnsafePointer: &cstr)) - return "" + println("SQLiteDB - Original string: \(str)") + let sql = Bridge.esc(str) + println("SQLiteDB - Escaped string: \(sql)") + return sql + } + + // SQL escape string - original version, does not work correctly at the moment + func esc2(str: String)->String { + println("SQLiteDB - Original string: \(str)") + let args = getVaList([str]) + let cstr = sqlite3_vmprintf("%Q", args) + let sql = String.fromCString(cstr) + sqlite3_free(cstr) + println("SQLiteDB - Escaped string: \(sql)") + return sql } // Return last insert ID @@ -252,7 +276,11 @@ class SQLiteDB { // Return last SQL error func lastSQLError()->String { var err:CString? = nil - dispatch_sync(queue) { + if dispatch_get_current_queue() != queue { + dispatch_sync(queue) { + err = sqlite3_errmsg(self.db) + } + } else { err = sqlite3_errmsg(self.db) } return (err ? NSString(CString:err!) : "") @@ -356,11 +384,13 @@ class SQLiteDB { let t = mktime(&time) + diff let ti = NSTimeInterval(t) let val = NSDate(timeIntervalSince1970:ti) + return val } } // If not a text date, then it's a time interval let val = sqlite3_column_double(stmt, index) - return Double(val) + let dt = NSDate(timeIntervalSince1970: val) + return dt } // If nothing works, return a string representation let buf:UnsafePointer = sqlite3_column_text(stmt, index) @@ -368,4 +398,4 @@ class SQLiteDB { let val = String.fromCString(cstr) return val } -} \ No newline at end of file +}