How do I pass a callback function to sqlite3_exec in swift? How do I pass a callback function to sqlite3_exec in swift? sqlite sqlite

How do I pass a callback function to sqlite3_exec in swift?


Swift 2.2 provides two options for implementing the sqlite3_exec callback function: (1) a global, non-instance func procedure or (2) a non-capturing literal {} closure.

The sqlite.org's "SQLite in 5 minutes or less" example is implemented in a Swift Xcode7 project here.

Readable typealias

typealias sqlite3 = COpaquePointertypealias CCharHandle = UnsafeMutablePointer<UnsafeMutablePointer<CChar>>typealias CCharPointer = UnsafeMutablePointer<CChar>typealias CVoidPointer = UnsafeMutablePointer<Void>

Callback Approach

func callback(    resultVoidPointer: CVoidPointer, // void *NotUsed     columnCount: CInt,               // int argc    values: CCharHandle,             // char **argv         columns: CCharHandle             // char **azColName    ) -> CInt {    for  i in 0 ..< Int(columnCount) {        guard let value = String.fromCString(values[i])         else { continue }        guard let column = String.fromCString(columns[i])         else { continue }        print("\(column) = \(value)")    }    return 0 // status ok}func sqlQueryCallbackBasic(argc: Int, argv: [String]) -> Int {    var db: sqlite3 = nil     var zErrMsg:CCharPointer = nil    var rc: Int32 = 0 // result code    if argc != 3 {        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))        return 1    }    rc = sqlite3_open(argv[1], &db)    if  rc != 0 {        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )        sqlite3_close(db)        return 1    }    rc = sqlite3_exec(db, argv[2], callback, nil, &zErrMsg)    if rc != SQLITE_OK {        print("ERROR: sqlite3_exec " + String.fromCString(zErrMsg)! ?? "")        sqlite3_free(zErrMsg)    }    sqlite3_close(db)    return 0}

Closure Approach

func sqlQueryClosureBasic(argc argc: Int, argv: [String]) -> Int {    var db: sqlite3 = nil     var zErrMsg:CCharPointer = nil    var rc: Int32 = 0    if argc != 3 {        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))        return 1    }    rc = sqlite3_open(argv[1], &db)    if  rc != 0 {        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )        sqlite3_close(db)        return 1    }    rc = sqlite3_exec(        db,      // database         argv[2], // statement        {        // callback: non-capturing closure            resultVoidPointer, columnCount, values, columns in            for i in 0 ..< Int(columnCount) {                guard let value = String.fromCString(values[i])                 else { continue }                guard let column = String.fromCString(columns[i])                 else { continue }                print("\(column) = \(value)")            }            return 0        },         nil,         &zErrMsg    )    if rc != SQLITE_OK {        let errorMsg = String.fromCString(zErrMsg)! ?? ""        print("ERROR: sqlite3_exec \(errorMsg)")        sqlite3_free(zErrMsg)    }    sqlite3_close(db)    return 0}


This is (currently) not possible. The Xcode 6 beta 4 release notes state:

However, you cannot call a C function pointer or convert a closure to C function pointer type.

As a workaround, you could put sqlite3_exec together with its callback into aC wrapper function and call that from Swift.


I wanted to provide an update to @l --marc l answer for Swift 3 and Linux which helped me get up and running. Thank you @l --marc l !

Callback Approach

func callback(    resultVoidPointer: UnsafeMutablePointer<Void>?, // void *NotUsed     columnCount: Int32, // int argc    values:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, // char **argv         columns:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? // char **azColName    ) -> CInt {    var dic: [String:String] = [:]    for  i in 0 ..< Int(columnCount) {        guard let value = values?[i]         else { continue }        guard let column = columns?[i]         else { continue }        let strCol = String(cString:column)         let strVal = String(cString:value)        dic[strCol] = strVal        //print("\(strCol) = \(strVal)")    }    resultSet.append(dic)    return 0 // status ok}func sqlQueryCallbackBasic(dbStr:String, query:String) -> Int {    var db: OpaquePointer?     var zErrMsg:UnsafeMutablePointer<Int8>?    var rc: Int32 = 0 // result code    rc = sqlite3_open(dbStr, &db)    if  rc != 0 {        print("ERROR: sqlite3_open " + String(sqlite3_errmsg(db)) ?? "" )        sqlite3_close(db)        return 1    }    rc = sqlite3_exec(db, query, callback, nil, &zErrMsg)    if rc != SQLITE_OK {        let errorMsg = zErrMsg        print("ERROR: sqlite3_exec \(errorMsg)")        sqlite3_free(zErrMsg)    }    sqlite3_close(db)    return 0}

Closure Approach

func sqlQueryClosureBasic(dbStr:String, query:String) -> Int {    var db: OpaquePointer?     var zErrMsg:UnsafeMutablePointer<Int8>?    var rc: Int32 = 0    rc = sqlite3_open(dbStr, &db)    if  rc != 0 {        print("ERROR: sqlite3_open " + String(sqlite3_errmsg(db)) ?? "" )        sqlite3_close(db)        return 1    }    rc = sqlite3_exec(        db,      // database         query, // statement        {        // callback: non-capturing closure            resultVoidPointer, columnCount, values, columns in            var dic: [String:String] = [:]            for i in 0 ..< Int(columnCount) {                guard let value = values?[i]                else { continue }                guard let column = columns?[i]                else { continue }                let strCol = String(cString:column)                let strVal = String(cString:value)                dic[strCol] = strVal                //print("\(strCol) = \(strVal)")            }            resultSet.append(dic)            return 0        },         nil,         &zErrMsg    )    if rc != SQLITE_OK {        let errorMsg = zErrMsg        print("ERROR: sqlite3_exec \(errorMsg)")        sqlite3_free(zErrMsg)    }    sqlite3_close(db)    return 0}

Test

import Glibcvar resultSet: [[String: String]] = [[:]]//sqlQueryClosureBasic(dbStr:"Sqlite_Test.db", query:"SELECT * FROM Employee")sqlQueryCallbackBasic(dbStr:"Sqlite_Test.db", query:"SELECT * FROM Employee")for row in resultSet {    for (col, val) in row {        print("\(col): \(val)")    }}