在Swift中访问SQLite数据库

Jas*_*ase 101 sqlite swift

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

我知道我可以在Objective C中使用SQLite Wrapper并使用桥接头,但我宁愿能够完全在Swift中完成这个项目.有没有办法做到这一点,如果是这样,有人可以指向我的参考,显示如何提交查询,检索行等?

Rob*_*Rob 135

虽然你应该使用众多SQLite包装器中的一个(我更喜欢FMDB,我自己),如果你想知道如何自己调用SQLite库,你会:

  1. 配置Swift项目以处理SQLite C调用.如果使用Xcode 9,您只需执行以下操作:

    import SQLite3
    
    Run Code Online (Sandbox Code Playgroud)

    在早期版本的Xcode中,您可以:

  2. 创建/打开数据库.

    let fileURL = try! FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("test.sqlite")
    
    // open database
    
    var db: OpaquePointer?
    if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
        print("error opening database")
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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)")
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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)")
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
    
    Run Code Online (Sandbox Code Playgroud)
  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)")
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 完成准备好的语句以恢复与该预准备语句相关的内存:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)
  8. 关闭数据库:

    if sqlite3_close(db) != SQLITE_OK {
        print("error closing database")
    }
    
    db = nil
    
    Run Code Online (Sandbox Code Playgroud)

对于Swift 2,请参阅此答案的先前版本.


dre*_*wag 18

您可以做的最好的事情是在桥接头中导入动态库:

  1. 将libsqlite3.dylib添加到"Link Binary With Libraries"构建阶段
  2. 创建一个"Bridging-Header.h"并添加#import <sqlite3.h>到顶部
  3. 在"Swift Compiler - Code Generation"下的Build Settings中为"Objective-C Bridging Header"设置设置"Bridging-Header.h"

然后,您就可以sqlite3_open从swift代码中访问所有c方法.

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

  • 也是每个人和他们的父亲现在都创建了一个Swift包装器..见下文 (3认同)

Chr*_*son 11

我也在寻找一种与SQLite交互的方式,就像我之前在Objective-C中所做的那样.不可否认,由于C兼容性,我只使用了直接的C API.

由于Swift中的SQLite当前不存在包装器,并且上面提到的SQLiteDB代码更高级并且假定某些用法,我决定创建一个包装器并在此过程中熟悉Swift.你可以在这里找到它: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 */
Run Code Online (Sandbox Code Playgroud)


小智 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
}
Run Code Online (Sandbox Code Playgroud)

和“查询”(例如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)")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

随着更多的功能!

你可以在这里查看


Gwe*_*oué 5

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