Truy cập Cơ sở dữ liệu SQLite bằng Swift


103

Tôi đang tìm cách truy cập cơ sở dữ liệu SQLite trong ứng dụng của mình bằng mã Swift.

Tôi biết rằng tôi có thể sử dụng SQLite Wrapper trong Objective C và sử dụng tiêu đề bắc cầu, nhưng tôi muốn có thể thực hiện dự án này hoàn toàn bằng Swift. Có cách nào để thực hiện việc này không, nếu vậy, ai đó có thể chỉ cho tôi một tham chiếu chỉ ra cách gửi truy vấn, truy xuất hàng, v.v. không?



1
tôi nên đặt tệp cơ sở dữ liệu của mình ở đâu?
C. Feliana

1
@ C.Feliana - Thư mục hỗ trợ ứng dụng là một nơi tuyệt vời, ví dụ let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Câu trả lời:


143

Mặc dù bạn có thể nên sử dụng một trong nhiều trình bao bọc SQLite, nhưng nếu bạn muốn biết cách tự gọi thư viện SQLite, bạn sẽ:

  1. Định cấu hình dự án Swift của bạn để xử lý các cuộc gọi SQLite C. Nếu sử dụng Xcode 9 trở lên, bạn chỉ cần thực hiện:

    import SQLite3
  2. Tạo / mở cơ sở dữ liệu.

    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
    }

    Lưu ý, tôi biết việc đóng cơ sở dữ liệu khi không mở được có vẻ kỳ lạ, nhưng sqlite3_open tài liệu hướng dẫn rõ ràng rằng chúng ta phải làm như vậy để tránh rò rỉ bộ nhớ:

    Cho dù lỗi có xảy ra khi nó được mở hay không, các tài nguyên được liên kết với xử lý kết nối cơ sở dữ liệu phải được giải phóng bằng cách chuyển nó đến sqlite3_close()khi nó không còn được yêu cầu.

  3. Sử dụng sqlite3_execđể thực hiện SQL (ví dụ: tạo bảng).

    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. Sử dụng sqlite3_prepare_v2để chuẩn bị SQL với ?trình giữ chỗ mà chúng tôi sẽ liên kết giá trị.

    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)")
    }

    Lưu ý, điều đó sử dụng SQLITE_TRANSIENThằng số có thể được thực hiện như sau:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Đặt lại SQL để chèn một giá trị khác. Trong ví dụ này, tôi sẽ chèn một NULLgiá trị:

    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. Hoàn thiện câu lệnh đã chuẩn bị để khôi phục bộ nhớ liên quan đến câu lệnh đã chuẩn bị đó:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Chuẩn bị câu lệnh mới để chọn các giá trị từ bảng và lặp qua việc truy xuất các giá trị:

    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. Đóng cơ sở dữ liệu:

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

Đối với Swift 2 và các phiên bản Xcode cũ hơn, hãy xem các bản sửa đổi trước của câu trả lời này .


1
Đối với những người gặp một số vấn đề trên vượt qua 1, hãy xem xét điều này: Tạo một tiêu đề bắc cầu trong dự án Xcode của bạn (ví dụ: BridgingHeader.h); Tệp tiêu đề này có thể chỉ có các dòng nhập tiêu đề Objective-C / C để bắc cầu sang Swift (ví dụ: #include <sqlite3.h>); Trên "Cài đặt bản dựng", tìm "Tiêu đề cầu nối Objective-C" (Bạn có thể sử dụng thanh tìm kiếm) và nhập "BridgingHeader.h" (nếu bạn nhận được thông báo lỗi như "Không thể nhập tiêu đề Objective-C", hãy thử "project- tên / BridgingHeader.h "); Đi tới "Build Phases", "Link Binary With Libraries" và thêm libsqlite3.0.dylib hoặc libsqlite3.0.tbd trong XCode 7
Jorg B Jorge

Sẽ tốt hơn nếu lồng if (... == SQLITE_OK) để phần sau sẽ không thực thi nếu nó không thành công. Tôi hoàn toàn hỏi vì tôi còn rất mới với điều này và chỉ tò mò nếu bạn làm điều này cho mục đích giảng dạy.
quemeful 14/09/2016

@quemeful - Chắc chắn rồi, nhưng nếu bạn làm điều đó với nhiều lệnh gọi SQLite, bạn sẽ có mã thực sự được lồng sâu vào nhau. Nếu bạn lo lắng về điều này, tôi có thể sử dụng guardcâu lệnh thay thế.
Rob

@Jorg B Jorge Tôi đã làm mọi thứ, bạn cũng cần nhập tiêu đề bắc cầu bằng cách nào đó? Tôi đang làm việc trong lớp thử nghiệm
Async- Ngày

Xin chào @Rob, tôi đang sử dụng trình bao bọc sqlite của bạn trong một dự án Swift tại đây. Nó thực sự tốt đẹp, cảm ơn. Tuy nhiên, tôi không thể thực hiện đếm chọn (*) từ bảng với nó. Nó liên tục bị trục trặc. Nếu tôi đã thực hiện một số lượng được chọn (col_name) từ tên bảng trong đó some_col = xxx, nó hoạt động. Bạn có đề nghị gì?
gbenroscience,

18

Điều tốt nhất bạn có thể làm là nhập thư viện động bên trong tiêu đề bắc cầu:

  1. Thêm libsqlite3.dylib vào giai đoạn xây dựng "Liên kết nhị phân với thư viện" của bạn
  2. Tạo một "Bridging-Header.h" và thêm #import <sqlite3.h>vào đầu
  3. đặt "Bridging-Header.h" cho cài đặt "Objective-C Bridging Header" trong Cài đặt bản dựng trong "Trình biên dịch Swift - Tạo mã"

Sau đó, bạn sẽ có thể truy cập tất cả các phương thức c như sqlite3_opentừ mã nhanh của bạn.

Tuy nhiên, bạn có thể chỉ muốn sử dụng FMDB và nhập nó thông qua tiêu đề bắc cầu vì đó là trình bao bọc hướng đối tượng hơn của sqlite. Xử lý con trỏ và cấu trúc C sẽ rất phức tạp trong Swift.


Tôi phải thêm điều này trong cài đặt xây dựng Dự án chứ không phải cài đặt xây dựng mục tiêu để Xcode tìm thấy tiêu đề bắc cầu.
rob5408

3
tất cả mọi người và cha của họ hiện đã tạo một trình bao bọc Swift .. xem bên dưới
quemeful

1
Đáng buồn thay, không có cái nào trong số chúng đã trưởng thành quá mức, vì vậy nếu bạn sử dụng bất kỳ loại giấy gói mới nào trong số này, hãy cẩn thận. Ví dụ, tại thời điểm viết bài, tôi đã xem qua bốn trong số chúng, và ba ngày xử lý không chính xác và thứ tư không xử lý chúng chút nào.
Rob

@Rob Bạn đã xem github.com/stephencelis/SQLite.swift#readme chưa? Thông tin về cách cấu hình để sử dụng với NSDate tại đây: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis

@stephencelis Này, điều đó tốt hơn hầu hết chúng, vì ít nhất bạn chỉ định múi giờ, nhưng tôi vẫn gặp vấn đề với điều đó NSDateFormatter. Nhưng mục đích của tôi không phải là chỉ trích khía cạnh cụ thể này của những triển khai cụ thể này mà là gợi ý rằng nó là dấu hiệu của một vấn đề rộng lớn hơn, rằng những giải pháp này không có nhiều năm sàng lọc như các giải pháp như FMDB. Tôi nghĩ rằng mọi người quá nhanh chóng để loại bỏ các giải pháp Objective-C đã được chứng minh để ủng hộ các triển khai Swift ít trưởng thành hơn (TFHpple và NDHpple là một ví dụ điển hình khác).
Rob

11

Tôi cũng đang tìm kiếm một số cách để tương tác với SQLite giống như cách tôi đã làm trước đây trong Objective-C. Phải thừa nhận rằng vì khả năng tương thích của C, tôi chỉ sử dụng API C thẳng.

Vì hiện tại không có trình bao bọc nào tồn tại cho SQLite trong Swift và mã SQLiteDB được đề cập ở trên cao hơn một chút và có cách sử dụng nhất định, tôi quyết định tạo trình bao bọc và làm quen một chút với Swift trong quá trình này. Bạn có thể tìm thấy nó ở đây: 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

Tôi đã tạo một thư viện SQLite trang nhã được viết hoàn toàn bằng Swift có tên SwiftData .

Một số tính năng của nó là:

  • Liên kết các đối tượng thuận tiện với chuỗi SQL
  • Hỗ trợ giao dịch và điểm lưu
  • Xử lý lỗi nội tuyến
  • Hoàn toàn an toàn theo mặc định

Nó cung cấp một cách dễ dàng để thực hiện các 'thay đổi' (ví dụ: CHÈN, CẬP NHẬT, XÓA, v.v.):

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
}

và 'truy vấn' (ví dụ: CHỌN):

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)")
        }
    }
}

Cùng với nhiều tính năng khác!

Bạn có thể kiểm tra nó tại đây


Thật không may, lib của bạn chỉ dành cho iOS! : - /
Cầu

3

Tuy nhiên, một trình bao bọc SQLite khác cho Swift 2 và Swift 3: http://github.com/groue/GRDB.swift

Đặc trưng:

  • Một API trông quen thuộc với người dùng ccgus / fmdb

  • API SQLite cấp thấp sử dụng thư viện chuẩn Swift

  • Một giao diện truy vấn Swift đẹp cho các nhà phát triển dị ứng với SQL

  • Hỗ trợ chế độ SQLite WAL và truy cập cơ sở dữ liệu đồng thời để tăng hiệu suất

  • Một lớp Bản ghi kết thúc các tập kết quả, sử dụng các truy vấn SQL tùy chỉnh của bạn cho bữa sáng và cung cấp các hoạt động CRUD cơ bản

  • Tự do kiểu Swift: chọn đúng kiểu Swift phù hợp với dữ liệu của bạn. Sử dụng Int64 khi cần thiết hoặc gắn bó với Int. Lưu trữ và đọc NSDate hoặc NSDateComponents. Khai báo các enums trong Swift cho các kiểu dữ liệu rời rạc. Xác định các kiểu chuyển đổi cơ sở dữ liệu của riêng bạn.

  • Di chuyển cơ sở dữ liệu

  • Tốc độ: https://github.com/groue/GRDB.swift/wiki/Performance


GRDB là một trong những framework được tài liệu hóa, hỗ trợ và duy trì tốt nhất trên Github!
Klaas

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")
        }
    }
}

Database.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
}

}

Model.swift

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 = ""
}

Truy cập cơ sở dữ liệu:

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

cơ sở dữ liệu Truy vấn cháy:

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

qyery này không hoạt động. tại sao lại có == thay vì chỉ một =?
ArgaPK

1

Đây là thư viện SQLite tốt nhất mà tôi đã sử dụng trong Swift: https://github.com/stephencelis/SQLite.swift

Nhìn vào các ví dụ mã. Gọn gàng hơn nhiều so với API C:

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"

Tài liệu cũng nói rằng "SQLite.swift cũng hoạt động như một trình bao bọc nhẹ, thân thiện với Swift trên C API" và sau đây là một số ví dụ về điều đó.


0

Tôi đã viết một thư viện trình bao bọc SQLite3 được viết bằng Swift .

Đây thực sự là một trình bao bọc cấp rất cao với API rất đơn giản, nhưng dù sao, nó có mã liên động C cấp thấp và tôi đăng ở đây một phần (đơn giản hóa) của nó để hiển thị liên thông 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
    }

Nếu bạn muốn có mã nguồn đầy đủ của trình bao bọc cấp thấp này, hãy xem các tệp này.


0

Định cấu hình dự án Swift của bạn để xử lý các cuộc gọi SQLite C:

Tạo tập tin tiêu đề bắc cầu đến dự án. Xem phần Nhập Objective-C vào Swift trong phần Sử dụng Swift với Cocoa và Objective-C. Tiêu đề bắc cầu này sẽ nhập sqlite3.h:

Thêm libsqlite3.0.dylib vào dự án của bạn. Xem tài liệu của Apple về việc thêm thư viện / khuôn khổ vào dự án của một người.

và sử dụng mã sau

    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

Đôi khi, phiên bản Swift của phương pháp tiếp cận "SQLite trong 5 phút trở xuống" được hiển thị trên sqlite.org là đủ. "5 phút hoặc ít hơn" sử dụng cách tiếp cận sqlite3_exec()mà là một wrapper thuận tiện cho sqlite3_prepare(), sqlite3_step(), sqlite3_column(), vàsqlite3_finalize() .

Swift 2.2 có thể hỗ trợ trực tiếp sqlite3_exec() callbackcon trỏ hàm dưới dạng một thủ tục toàn cục, không phải phiên bản funchoặc một bao đóng theo nghĩa đen không bắt giữ {}.

Có thể đọc được typealias

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

Phương pháp gọi lại

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
}

Phương pháp đóng cửa

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
}

Để chuẩn bị một dự án Xcode để gọi một thư viện C như SQLite, người ta cần (1) thêm tiêu đề C tham chiếu tệp Bridging-Header.h như #import "sqlite3.h", (2) thêm Bridging-Header.h vào Objective-C Bridging Header trong dự án cài đặt và (3) thêm libsqlite3.tbdvào Liên kết nhị phân với thư viện cài đặt đích .

Các sqlite.org 's 'SQLite trong 5 phút hoặc ít hơn' dụ được thực hiện trong một dự án Swift Xcode7 đây .


0

Bạn cũng có thể dễ dàng cấu hình SQLite bằng cách sử dụng lớp tấn đơn.

Tham khảo

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Phương pháp tạo cơ sở dữ liệu

func methodToCreateDatabase() -> NSURL?{} 

Phương pháp chèn, cập nhật và xóa dữ liệu

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

Phương pháp chọn dữ liệu

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

0

Bạn có thể sử dụng thư viện này trong Swift cho SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

SQLite Demo sử dụng Swift với lớp SQLDataAccess được viết bằng Swift

Thêm vào dự án của bạn

Bạn chỉ cần ba tệp để thêm vào dự án của mình * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header phải được đặt trong dự án Xcode của bạn 'Objective-C Bridging Header' trong 'Swift Compiler - General'

Ví dụ để sử dụng

Chỉ cần làm theo mã trong ViewController.swift để xem cách viết SQL đơn giản với SQLDataAccess.swift Trước tiên, bạn cần mở Cơ sở dữ liệu SQLite mà bạn đang xử lý

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

Nếu openConnection thành công, bây giờ bạn có thể thực hiện một thao tác chèn đơn giản vào 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)")
    }

Hãy xem điều đó thật đơn giản!

Thuật ngữ đầu tiên trong db.executeStatement là SQL của bạn dưới dạng Chuỗi, tất cả các thuật ngữ theo sau là danh sách đối số đa dạng kiểu Bất kỳ và là các tham số của bạn trong Mảng. Tất cả các thuật ngữ này được phân tách bằng dấu phẩy trong danh sách các đối số SQL của bạn. Bạn có thể nhập Chuỗi, Số nguyên, Ngày tháng và Khối ngay sau câu lệnh phần tiếp theo vì tất cả các thuật ngữ này được coi là tham số cho phần tiếp theo. Mảng đối số đa dạng chỉ giúp bạn nhập tất cả phần tiếp theo của bạn một cách thuận tiện chỉ trong một lệnh gọi executeStatement hoặc getRecordsForQuery. Nếu bạn không có bất kỳ tham số nào, đừng nhập bất kỳ thứ gì sau SQL của bạn.

Mảng kết quả là một Mảng Từ điển trong đó 'khóa' là tên cột bảng của bạn và 'giá trị' là dữ liệu của bạn lấy từ SQLite. Bạn có thể dễ dàng lặp lại mảng này bằng vòng lặp for hoặc in ra trực tiếp hoặc gán các phần tử Từ điển này cho các Lớp đối tượng dữ liệu tùy chỉnh mà bạn sử dụng trong Bộ điều khiển Chế độ xem để sử dụng mô hình.

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

SQLDataAccess sẽ lưu trữ, text, double, float, blob, Date, integer và các số nguyên dài. Đối với Blob bạn có thể lưu trữ binary, varbinary, blob.

Đối với Văn bản, bạn có thể lưu trữ ký tự char, ký tự, dấu gạch ngang, ký tự thay đổi quốc gia, ký tự bản địa, nchar, nvarchar, varchar, biến thể, ký tự khác nhau, văn bản.

Đối với Ngày, bạn có thể lưu trữ datetime, time, timestamp, date.

Đối với Số nguyên, bạn có thể lưu trữ bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Đối với Double, bạn có thể lưu trữ thập phân, chính xác kép, float, số, thực, gấp đôi. Đôi có độ chính xác nhất.

Bạn thậm chí có thể lưu trữ Null kiểu Null.

Trong ViewController.swift, một ví dụ phức tạp hơn được thực hiện cho thấy cách chèn Từ điển dưới dạng 'Blob'. Ngoài ra, SQLDataAccess hiểu Swift Date () nguyên gốc để bạn có thể chèn các đối tượng này mà không cần chuyển đổi, và nó sẽ chuyển đổi chúng thành văn bản và lưu trữ chúng, và khi được truy xuất, hãy chuyển đổi chúng từ văn bản thành Ngày.

Tất nhiên sức mạnh thực sự của SQLite là khả năng Giao dịch. Ở đây, bạn có thể xếp hàng 400 câu lệnh SQL với các tham số và chèn tất cả chúng cùng một lúc, điều này thực sự mạnh mẽ vì nó quá nhanh. ViewController.swift cũng cho bạn thấy một ví dụ về cách thực hiện việc này. Tất cả những gì bạn thực sự đang làm là tạo một Mảng Từ điển có tên là 'sqlAndParams', trong Mảng này Từ điển lưu trữ của bạn có hai khóa 'SQL' cho câu lệnh hoặc truy vấn phần tiếp theo của Chuỗi và 'PARAMS' chỉ là Mảng các đối tượng gốc SQLite hiểu cho truy vấn đó. Mỗi 'sqlParams' là một Từ điển riêng lẻ của truy vấn phần tiếp theo cộng với các tham số sau đó được lưu trữ trong Mảng 'sqlAndParams'. Khi bạn đã tạo mảng này, bạn chỉ cần gọi.

    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)")
    }

Ngoài ra, tất cả các phương thức executeStatement và getRecordsForQuery có thể được thực hiện với truy vấn Chuỗi cho SQL đơn giản và một Mảng cho các tham số cần thiết của truy vấn.

    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)")
    }

Một phiên bản Objective-C cũng tồn tại và được gọi là SQLDataAccess tương tự, vì vậy bây giờ bạn có thể chọn viết phần tiếp theo của mình bằng Objective-C hoặc Swift. Ngoài ra SQLDataAccess cũng sẽ hoạt động với SQLCipher, mã hiện tại chưa được thiết lập để hoạt động với nó, nhưng nó khá dễ thực hiện và một ví dụ về cách thực hiện điều này thực sự có trong phiên bản Objective-C của SQLDataAccess.

SQLDataAccess là một lớp rất nhanh và hiệu quả, và có thể được sử dụng thay cho CoreData thực sự chỉ sử dụng SQLite vì nó là nơi lưu trữ dữ liệu cơ bản mà không có tất cả các sự cố về tính toàn vẹn dữ liệu lõi CoreData đi kèm với CoreData.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.