在Swift中访问SQLite数据库


103

我正在寻找一种使用Swift代码在我的应用程序中访问SQLite数据库的方法。

我知道我可以在Objective C中使用SQLite包装器并使用桥接头,但是我希望能够完全在Swift中完成此项目。如果可以的话,有没有办法做到这一点,有人可以将我指向一个引用,该引用显示了如何提交查询,检索行等。



1
我应该把数据库文件放在哪里?
C. Feliana '18

1
@ C.Feliana-应用程序支持目录是一个不错的地方,例如let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path
罗布

Answers:


143

尽管您可能应该使用许多SQLite包装器之一,但是如果您想知道如何自己调用SQLite库,则可以:

  1. 配置您的Swift项目以处理SQLite C调用。如果使用Xcode 9或更高版本,则可以执行以下操作:

    import SQLite3
  2. 创建/打开数据库。

    let fileURL = try! FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("test.sqlite")
    
    // open database
    
    var db: OpaquePointer?
    guard sqlite3_open(fileURL.path, &db) == SQLITE_OK else {
        print("error opening database")
        sqlite3_close(db)
        db = nil
        return
    }

    注意,我知道在打开失败后关闭数据库似乎很奇怪,但是sqlite3_open 文档明确指出我们必须这样做以避免内存泄漏:

    无论错误是否在打开时发生,与数据库连接句柄关联的资源都应通过将其传递给sqlite3_close()不再需要时释放。

  3. 使用sqlite3_exec执行SQL(如创建表)。

    if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error creating table: \(errmsg)")
    }
  4. 使用sqlite3_prepare_v2与准备SQL ?,这是我们将绑定值的占位符。

    var statement: OpaquePointer?
    
    if sqlite3_prepare_v2(db, "insert into test (name) values (?)", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing insert: \(errmsg)")
    }
    
    if sqlite3_bind_text(statement, 1, "foo", -1, SQLITE_TRANSIENT) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding foo: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting foo: \(errmsg)")
    }

    注意,它使用可以如下实现SQLITE_TRANSIENT常量:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. 重置SQL以插入另一个值。在此示例中,我将插入一个NULL值:

    if sqlite3_reset(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error resetting prepared statement: \(errmsg)")
    }
    
    if sqlite3_bind_null(statement, 1) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding null: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting null: \(errmsg)")
    }
  6. 完成准备好的语句以恢复与该准备好的语句关联的内存:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. 准备用于从表中选择值的新语句,并循环检索值:

    if sqlite3_prepare_v2(db, "select id, name from test", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing select: \(errmsg)")
    }
    
    while sqlite3_step(statement) == SQLITE_ROW {
        let id = sqlite3_column_int64(statement, 0)
        print("id = \(id); ", terminator: "")
    
        if let cString = sqlite3_column_text(statement, 1) {
            let name = String(cString: cString)
            print("name = \(name)")
        } else {
            print("name not found")
        }
    }
    
    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  8. 关闭数据库:

    if sqlite3_close(db) != SQLITE_OK {
        print("error closing database")
    }
    
    db = nil

对于Swift 2和更早版本的Xcode,请参阅此答案的先前版本


1
对于那些在传递1时遇到问题的人,请考虑以下事项:在您的Xcode项目中创建一个桥接头(例如BridgingHeader.h);这个头文件可能只有几行代码导入用于桥接到Swift的Objective-C / C头文件(例如#include <sqlite3.h>);在“构建设置”上找到“ Objective-C桥接标头”(您可以使用搜索栏),然后键入“ BridgingHeader.h”(如果收到类似“无法导入Objective-C标头”的错误消息,请尝试“ name / BridgingHeader.h“); 转到“构建阶段”,“使用库链接二进制文件”,并在XCode 7
Jorg B Jorge中

最好嵌套if(... == SQLITE_OK),以使如果失败则不会执行以下操作。我纯粹是在问,因为我对此很陌生,只是好奇您是否出于教学目的这样做。
2016年

@quemeful-当然可以,但是如果您通过许多SQLite调用来完成此操作,则最终会得到真正深深嵌套的代码。如果您对此有所担心,我可能会改用guard语句。
罗布

@Jorg B Jorge我做了所有事情,您是否还需要导入桥接头文件?我正在测试课程中工作
-Async-

@Rob,您好,我在这里的Swift项目中使用您的sqlite包装器。真的很好,谢谢。但是,我不能使用它从表中进行选择count(*)。它一直崩溃。如果我从其中some_col = xxx的表名中进行选择计数(col_name),则它可以工作。你有什么建议?
gbenroscience

18

最好的办法是将动态库导入桥接头文件中:

  1. 将libsqlite3.dylib添加到“使用库链接二进制文件”构建阶段
  2. 创建一个“ Bridging-Header.h”并添加#import <sqlite3.h>到顶部
  3. 在“ Swift编译器-代码生成”下的“构建设置”中为“ Objective-C桥接头”设置设置“ Bridging-Header.h”

然后,您将能够访问所有c方法,例如sqlite3_open从您的swift代码。

但是,您可能只想使用FMDB并将其通过桥接头导入,因为这是sqlite的更面向对象的包装。在Swift中,处理C指针和结构会很麻烦。


我必须将其添加到“项目”构建设置下,而不是“目标”构建设置下,以便Xcode找到桥接头。
rob5408

3
大家也和他们的父亲现在已经创建了一个斯威夫特包装..见下文
quemeful

1
可悲的是,它们都不是非常成熟的,因此,如果您使用任何这些新包装材料,请当心。例如,在撰写本文时,我瞥了一眼其中的四个,而三个错误地处理了日期,而第四个根本不处理它们。
罗布2015年


@stephencelis嘿,这比大多数人好,因为至少你指定的时区,但我仍然有问题NSDateFormatter。但是,我的意图不是批评这些特定实现的特定方面,而是暗示它表明了一个更广泛的问题,即这些问题没有像FMDB这样的解决方案具有多年的完善性。我认为人们太快抛弃成熟的Objective-C解决方案,转而使用不太成熟的Swift实现(TFHpple与NDHpple是另一个很好的例子)。
罗布2015年

11

我也一直在寻找某种与SQLite交互的方法,就像我以前在Objective-C中所做的一样。诚然,由于C的兼容性,我只使用了直接的C API。

由于Swift中目前没有用于SQLite的包装器,并且上面提到的SQLiteDB代码具有更高的层次并具有一定的用法,因此我决定创建一个包装器,并对该过程进行一些熟悉。您可以在这里找到它:https : //github.com/chrismsimpson/SwiftSQLite

var db = SQLiteDatabase();
db.open("/path/to/database.sqlite");

var statement = SQLiteStatement(database: db);

if ( statement.prepare("SELECT * FROM tableName WHERE Id = ?") != .Ok )
{
    /* handle error */
}

statement.bindInt(1, value: 123);

if ( statement.step() == .Row )
{
    /* do something with statement */
    var id:Int = statement.getIntAt(0)
    var stringValue:String? = statement.getStringAt(1)
    var boolValue:Bool = statement.getBoolAt(2)
    var dateValue:NSDate? = statement.getDateAt(3)
}

statement.finalizeStatement(); /* not called finalize() due to destructor/language keyword */

5

我创建了一个优雅的完全用Swift编写的SQLite库,称为SwiftData

它的一些功能是:

  • 方便地将对象绑定到SQL字符串
  • 支持交易和保存点
  • 内联错误处理
  • 默认情况下完全线程安全

它提供了一种执行“更改”(例如INSERT,UPDATE,DELETE等)的简便方法:

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES ('Toronto', 2615060, 0, '1793-08-27')") {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

和“查询”(例如SELECT):

let (resultSet, err) = SD.executeQuery("SELECT * FROM Cities")
if err != nil {
    //there was an error during the query, handle it here
} else {
    for row in resultSet {
        if let name = row["Name"].asString() {
            println("The City name is: \(name)")
        }
        if let population = row["Population"].asInt() {
            println("The population is: \(population)")
        }
        if let isWarm = row["IsWarm"].asBool() {
            if isWarm {
                println("The city is warm")
            } else {
                println("The city is cold")
            }
        }
        if let foundedIn = row["FoundedIn"].asDate() {
            println("The city was founded in: \(foundedIn)")
        }
    }
}

随着更多的功能!

你可以在这里查看


不幸的是,您的lib仅适用于iOS!:-/
BadmintonCat

3

适用于Swift 2和Swift 3的另一个SQLite包装器:http//github.com/groue/GRDB.swift

特征:

  • ccgus / fmdb用户看似熟悉的API

  • 利用Swift标准库的低级SQLite API

  • 适用于SQL过敏开发人员的漂亮Swift查询界面

  • 支持SQLite WAL模式,并发数据库访问可提高性能

  • 一个Record类,它包装结果集,吃早餐时使用自定义SQL查询,并提供基本的CRUD操作

  • Swift类型自由度:选择适合您数据的正确Swift类型。必要时使用Int64,或坚持使用方便的Int。存储和读取NSDate或NSDateComponents。为离散数据类型声明Swift枚举。定义自己的数据库可转换类型。

  • 数据库迁移

  • 速度:https : //github.com/groue/GRDB.swift/wiki/Performance


GRDB是Github上最好的文档,受支持和维护的框架之一!
克拉斯

3

AppDelegate.swift

func createDatabase()
{
    var path:Array=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let directory:String=path[0]
    let DBpath=(directory as NSString).appendingPathComponent("Food.sqlite")

    print(DBpath)

    if (FileManager.default.fileExists(atPath: DBpath))
    {
        print("Successfull database create")
    }
    else
    {
        let pathfrom:String=(Bundle.main.resourcePath! as NSString).appendingPathComponent("Food.sqlite")

        var success:Bool
        do {
            try FileManager.default.copyItem(atPath: pathfrom, toPath: DBpath)
            success = true
        } catch _ {
            success = false
        }

        if !success
        {
            print("database not create ")
        }
        else
        {
            print("Successfull database new create")
        }
    }
}

数据库.swift

import UIKit

class database: NSObject
{
func databasePath() -> NSString
{
    var path:Array=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let directory:String=path[0] 
    let DBpath=(directory as NSString).appendingPathComponent("Food.sqlite")

    if (FileManager.default.fileExists(atPath: DBpath))
    {
        return DBpath as NSString
    }
    return DBpath as NSString
}

func ExecuteQuery(_ str:String) -> Bool
{
    var result:Bool=false
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if (sqlite3_open(DBpath, &db)==SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            if (sqlite3_step(stmt) == SQLITE_DONE)
            {
                result=true
            } 
        }
        sqlite3_finalize(stmt)
    }
    sqlite3_close(db)

    return result
}

func SelectQuery(_ str:String) -> Array<Dictionary<String,String>>
{
    var result:Array<Dictionary<String,String>>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                var i:Int32=0
                let icount:Int32=sqlite3_column_count(stmt)

                var dict=Dictionary<String, String>()

                while i < icount
                {
                    let strF=sqlite3_column_name(stmt, i)
                    let strV = sqlite3_column_text(stmt, i)

                    let rFiled:String=String(cString: strF!)
                    let rValue:String=String(cString: strV!)
                    //let rValue=String(cString: UnsafePointer<Int8>(strV!))

                    dict[rFiled] = rValue

                    i += 1
                }
                result.insert(dict, at: result.count)
            }
        sqlite3_finalize(stmt)
        }

    sqlite3_close(db)
    }
    return result
}

func AllSelectQuery(_ str:String) -> Array<Model>
{
    var result:Array<Model>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                let mod=Model()

                mod.id=String(cString: sqlite3_column_text(stmt, 0))
                mod.image=String(cString: sqlite3_column_text(stmt, 1))
                mod.name=String(cString: sqlite3_column_text(stmt, 2))
                mod.foodtype=String(cString: sqlite3_column_text(stmt, 3))
                mod.vegtype=String(cString: sqlite3_column_text(stmt, 4))
                mod.details=String(cString: sqlite3_column_text(stmt, 5))

                result.insert(mod, at: result.count)
            }
            sqlite3_finalize(stmt)
        }
        sqlite3_close(db)
    }
    return result
}

}

模型模型

import UIKit


class Model: NSObject
{
var uid:Int = 0
var id:String = ""
var image:String = ""
var name:String = ""
var foodtype:String = ""
var vegtype:String = ""
var details:String = ""
var mealtype:String = ""
var date:String = ""
}

访问数据库:

let DB=database()
var mod=Model()

数据库查询火:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")

这个qyery不起作用。为什么有==而不是一个=?
ArgaPK '18年

1

这是迄今为止我在Swift中使用过的最好的SQLite库:https : //github.com/stephencelis/SQLite.swift

查看代码示例。比C API干净得多:

import SQLite

let db = try Connection("path/to/db.sqlite3")

let users = Table("users")
let id = Expression<Int64>("id")
let name = Expression<String?>("name")
let email = Expression<String>("email")

try db.run(users.create { t in
    t.column(id, primaryKey: true)
    t.column(name)
    t.column(email, unique: true)
})
// CREATE TABLE "users" (
//     "id" INTEGER PRIMARY KEY NOT NULL,
//     "name" TEXT,
//     "email" TEXT NOT NULL UNIQUE
// )

let insert = users.insert(name <- "Alice", email <- "alice@mac.com")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice@mac.com')

for user in try db.prepare(users) {
    print("id: \(user[id]), name: \(user[name]), email: \(user[email])")
    // id: 1, name: Optional("Alice"), email: alice@mac.com
}
// SELECT * FROM "users"

let alice = users.filter(id == rowid)

try db.run(alice.update(email <- email.replace("mac.com", with: "me.com")))
// UPDATE "users" SET "email" = replace("email", 'mac.com', 'me.com')
// WHERE ("id" = 1)

try db.run(alice.delete())
// DELETE FROM "users" WHERE ("id" = 1)

try db.scalar(users.count) // 0
// SELECT count(*) FROM "users"

该文档还说:“ SQLite.swift还可以作为对C API的轻量级,对Swift友好的包装器”,并附带一些示例。


0

我已经编写了一个用Swift编写的SQLite3包装器库

这实际上是具有非常简单的API的非常高级的包装器,但是无论如何,它具有低级的C互操作代码,我在这里发布了它的(简化)部分以展示C互操作。

    struct C
    {
        static let  NULL        =   COpaquePointer.null()
    }

    func open(filename:String, flags:OpenFlag)
    {
        let name2   =   filename.cStringUsingEncoding(NSUTF8StringEncoding)!
        let r       =   sqlite3_open_v2(name2, &_rawptr, flags.value, UnsafePointer<Int8>.null())
        checkNoErrorWith(resultCode: r)
    }

    func close()
    {   
        let r   =   sqlite3_close(_rawptr)
        checkNoErrorWith(resultCode: r)
        _rawptr =   C.NULL
    }

    func prepare(SQL:String) -> (statements:[Core.Statement], tail:String)
    {
        func once(zSql:UnsafePointer<Int8>, len:Int32, inout zTail:UnsafePointer<Int8>) -> Core.Statement?
        {
            var pStmt   =   C.NULL
            let r       =   sqlite3_prepare_v2(_rawptr, zSql, len, &pStmt, &zTail)
            checkNoErrorWith(resultCode: r)

            if pStmt == C.NULL
            {
                return  nil
            }
            return  Core.Statement(database: self, pointerToRawCStatementObject: pStmt)
        }

        var stmts:[Core.Statement]  =   []
        let sql2    =   SQL as NSString
        var zSql    =   UnsafePointer<Int8>(sql2.UTF8String)
        var zTail   =   UnsafePointer<Int8>.null()
        var len1    =   sql2.lengthOfBytesUsingEncoding(NSUTF8StringEncoding);
        var maxlen2 =   Int32(len1)+1

        while let one = once(zSql, maxlen2, &zTail)
        {
            stmts.append(one)
            zSql    =   zTail
        }

        let rest1   =   String.fromCString(zTail)
        let rest2   =   rest1 == nil ? "" : rest1!

        return  (stmts, rest2)
    }

    func step() -> Bool
    {   
        let rc1 =   sqlite3_step(_rawptr)

        switch rc1
        {   
            case SQLITE_ROW:
                return  true

            case SQLITE_DONE:
                return  false

            default:
                database.checkNoErrorWith(resultCode: rc1)
        }
    }

    func columnText(at index:Int32) -> String
    {
        let bc  =   sqlite3_column_bytes(_rawptr, Int32(index))
        let cs  =   sqlite3_column_text(_rawptr, Int32(index))

        let s1  =   bc == 0 ? "" : String.fromCString(UnsafePointer<CChar>(cs))!
        return  s1
    }

    func finalize()
    {
        let r   =   sqlite3_finalize(_rawptr)
        database.checkNoErrorWith(resultCode: r)

        _rawptr =   C.NULL
    }

如果您需要此低级包装的完整源代码,请参阅这些文件。


0

配置您的Swift项目以处理SQLite C调用:

创建到项目的桥接头文件。请参阅将Swift与Cocoa和Objective-C一起使用,将Objective-C导入Swift。此桥接标头应导入sqlite3.h:

将libsqlite3.0.dylib添加到您的项目。有关将库/框架添加到自己的项目中的信息,请参阅Apple的文档。

并使用以下代码

    func executeQuery(query: NSString ) -> Int
    {
        if  sqlite3_open(databasePath! as String, &database) != SQLITE_OK
        {
            println("Databse is not open")
            return 0
        }
        else
        {
            query.stringByReplacingOccurrencesOfString("null", withString: "")
            var cStatement:COpaquePointer = nil
            var executeSql = query as NSString
            var lastId : Int?
            var sqlStatement = executeSql.cStringUsingEncoding(NSUTF8StringEncoding)
            sqlite3_prepare_v2(database, sqlStatement, -1, &cStatement, nil)
            var execute = sqlite3_step(cStatement)
            println("\(execute)")
            if execute == SQLITE_DONE
            {
                lastId = Int(sqlite3_last_insert_rowid(database))
            }
            else
            {
                println("Error in Run Statement :- \(sqlite3_errmsg16(database))")
            }
            sqlite3_finalize(cStatement)
            return lastId!
        }
    }
    func ViewAllData(query: NSString, error: NSError) -> NSArray
    {
        var cStatement = COpaquePointer()
        var result : AnyObject = NSNull()
        var thisArray : NSMutableArray = NSMutableArray(capacity: 4)
        cStatement = prepare(query)
        if cStatement != nil
        {
            while sqlite3_step(cStatement) == SQLITE_ROW
            {
                result = NSNull()
                var thisDict : NSMutableDictionary = NSMutableDictionary(capacity: 4)
                for var i = 0 ; i < Int(sqlite3_column_count(cStatement)) ; i++
                {
                    if sqlite3_column_type(cStatement, Int32(i)) == 0
                    {
                        continue
                    }
                    if sqlite3_column_decltype(cStatement, Int32(i)) != nil && strcasecmp(sqlite3_column_decltype(cStatement, Int32(i)), "Boolean") == 0
                    {
                        var temp = sqlite3_column_int(cStatement, Int32(i))
                        if temp == 0
                        {
                            result = NSNumber(bool : false)
                        }
                        else
                        {
                            result = NSNumber(bool : true)
                        }
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_INTEGER
                    {
                        var temp = sqlite3_column_int(cStatement,Int32(i))
                        result = NSNumber(int : temp)
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_FLOAT
                    {
                        var temp = sqlite3_column_double(cStatement,Int32(i))
                        result = NSNumber(double: temp)
                    }
                    else
                    {
                        if sqlite3_column_text(cStatement, Int32(i)) != nil
                        {
                            var temp = sqlite3_column_text(cStatement,Int32(i))
                            result = String.fromCString(UnsafePointer<CChar>(temp))!
                            
                            var keyString = sqlite3_column_name(cStatement,Int32(i))
                            thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                        }
                        result = NSNull()

                    }
                    if result as! NSObject != NSNull()
                    {
                        var keyString = sqlite3_column_name(cStatement,Int32(i))
                        thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                    }
                }
                thisArray.addObject(NSMutableDictionary(dictionary: thisDict))
            }
            sqlite3_finalize(cStatement)
        }
        return thisArray
    }
    func prepare(sql : NSString) -> COpaquePointer
    {
        var cStatement:COpaquePointer = nil
        sqlite3_open(databasePath! as String, &database)
        var utfSql = sql.UTF8String
        if sqlite3_prepare(database, utfSql, -1, &cStatement, nil) == 0
        {
            sqlite3_close(database)
            return cStatement
        }
        else
        {
            sqlite3_close(database)
            return nil
        }
    }
}

0

有时,在sqlite.org上显示的Swift版本的“ 5分钟或更短时间内的SQLite”方法就足够了。的“5分钟或更少”的方法的用途,其是一个方便的包装器,,,和sqlite3_exec()sqlite3_prepare()sqlite3_step()sqlite3_column()sqlite3_finalize()

Swift 2.2可以直接将sqlite3_exec() callback函数指针支持为全局,非实例过程func或非捕获的文字闭包{}

可读的 typealias

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

回调方法

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
}

封闭方法

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
}

要准备一个Xcode项目以调用诸如SQLite之类的C库,需要(1)添加一个Bridging-Header.h文件引用C标头,如#import "sqlite3.h"(2)将Bridging-Header.h添加到项目中的Objective-C Bridging Header设置,以及(3)添加libsqlite3.tbdLink Binary With Library目标设置。

sqlite.org‘的SQLite在5分钟或更少’例如是在斯威夫特Xcode7项目实施在这里


0

您也可以轻松使用单吨级类快速配置SQLite。

参考

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

创建数据库的方法

func methodToCreateDatabase() -> NSURL?{} 

插入,更新和删除数据的方法

func methodToInsertUpdateDeleteData(strQuery : String) -> Bool{}

选择数据的方法

func methodToSelectData(strQuery : String) -> NSMutableArray{}

0

您可以在Swift中为SQLite使用此库 https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

使用Swift和以Swift编写的SQLDataAccess类的SQLite演示

添加到您的项目

您只需要将三个文件添加到您的项目中* SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h必须在您的Xcode项目“ Swift Compiler-General”下的“ Objective-C Bridging Header”中设置Bridging-Header。

使用范例

只需按照ViewController.swift中的代码查看如何使用SQLDataAccess.swift编写简单的SQL首先,您需要打开要处理的SQLite数据库

    let db = SQLDataAccess.shared
    db.setDBName(name:"SQLite.db")
    let opened = db.openConnection(copyFile:true)

如果openConnection成功,现在您可以在Table AppInfo中进行简单插入

    //Insert into Table AppInfo
    let status = db.executeStatement("insert into AppInfo (name,value,descrip,date) values(?,?,?,?)",SQLiteDemo","1.0.2","unencrypted",Date())
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

看看那有多简单!

db.executeStatement中的第一个术语是您的SQL作为String,后面的所有术语都是Any类型的可变参数列表,并且是Array中的参数。所有这些术语在SQL参数列表中均以逗号分隔。您可以在续集语句后立即输入Strings,Integers,Date和Blob,因为所有这些术语均被视为续集的参数。可变参数数组仅使在一个executeStatement或getRecordsForQuery调用中输入所有续集变得很方便。如果没有任何参数,则不要在SQL之后输入任何内容。

结果数组是字典数组,其中“键”是表的列名,“值”是从SQLite获得的数据。您可以使用for循环轻松遍历此数组,也可以直接将其打印出来,也可以将这些Dictionary元素分配给在View Controller中用于模型使用的自定义数据对象类。

    for dic in results as! [[String:AnyObject]] {
       print(“result = \(dic))
    }

SQLDataAccess将存储,文本,双精度型,浮点型,斑点,日期,整数和长整型整数。对于Blob,您可以存储二进制,varbinary,blob。

对于“文本”,您可以存储char,字符,clob,国家字符,本机字符,nchar,nvarchar,varchar,变体,变量字符,文本。

对于日期,您可以存储日期时间,时间,时间戳记,日期。

对于整数,您可以存储bigint,bit,bool,boolean,int2,int8,integer,mediumint,smallint,tinyint,int。

对于Double,您可以存储十进制,double精度,float,数值,实数,double。Double的精度最高。

您甚至可以存储Null类型的Null。

在ViewController.swift中,完成了一个更复杂的示例,显示了如何将Dictionary作为“ Blob”插入。另外,SQLDataAccess理解本机Swift Date(),因此您可以不进行转换而插入这些对象,它将把它们转换为文本并存储,并在检索时将它们从文本转换回Date。

当然,SQLite的真正功能是它的事务处理能力。在这里,您可以从字面上排队使用参数的400条SQL语句,然后一次将它们全部插入,这非常强大,因为它是如此之快。ViewController.swift还向您显示了如何执行此操作的示例。您真正要做的就是创建一个名为“ sqlAndParams”的字典数组,在该数组中,您存储的字典具有两个键“ SQL”(用于字符串续集语句或查询),以及“ PARAMS”(这只是一组原生对象SQLite)了解该查询。然后,每个“ sqlParams”(即续集查询加参数的单独词典)都存储在“ sqlAndParams”数组中。创建此数组后,您只需调用。

    let status = db.executeTransaction(sqlAndParams)
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries for the above Transactions
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

此外,所有executeStatement和getRecordsForQuery方法都可以通过用于SQL查询的简单String和用于查询所需参数的Array完成。

    let sql : String = "insert into AppInfo (name,value,descrip) values(?,?,?)"
    let params : Array = ["SQLiteDemo","1.0.0","unencrypted"]
    let status = db.executeStatement(sql, withParameters: params)
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries for the above Transactions
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

还存在一个Objective-C版本,称为相同的SQLDataAccess,因此现在您可以选择用Objective-C或Swift编写续集。另外,SQLDataAccess也可以与SQLCipher一起使用,目前的代码尚未设置成可以使用,但是很容易做到,而如何做到这一点的示例实际上是在SQLDataAccess的Objective-C版本中。

SQLDataAccess是一个非常快速且高效的类,可以用来代替CoreData,后者实际上仅使用SQLite作为其基础数据存储,而没有CoreData附带的所有CoreData核心数据完整性故障崩溃。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.