Mengakses Database SQLite di Swift

103

Saya mencari cara untuk mengakses database SQLite di aplikasi saya dengan kode Swift.

Saya tahu bahwa saya dapat menggunakan SQLite Wrapper di Objective C dan menggunakan header bridging, tetapi saya lebih suka dapat melakukan proyek ini sepenuhnya di Swift. Apakah ada cara untuk melakukan ini, jika demikian, dapatkah seseorang mengarahkan saya ke referensi yang menunjukkan cara mengirimkan kueri, mengambil baris, dll?

Jase
sumber
1
di mana saya harus meletakkan file database saya?
C. Feliana
1
@ C.Feliana - Direktori dukungan aplikasi adalah tempat yang tepat, mis let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Jawaban:

143

Meskipun Anda mungkin harus menggunakan salah satu dari banyak pembungkus SQLite, jika Anda ingin mengetahui cara memanggil pustaka SQLite sendiri, Anda akan:

  1. Konfigurasikan proyek Swift Anda untuk menangani panggilan SQLite C. Jika menggunakan Xcode 9 atau lebih baru, Anda cukup melakukan:

    import SQLite3
  2. Buat / buka database.

    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
    }
    

    Catatan, saya tahu tampaknya aneh untuk menutup database saat gagal membuka, tetapi sqlite3_open dokumentasinya menjelaskan bahwa kita harus melakukannya untuk menghindari kebocoran memori:

    Apakah terjadi kesalahan saat dibuka atau tidak, sumber daya yang terkait dengan pegangan koneksi database harus dilepaskan dengan meneruskannya ke sqlite3_close()saat tidak lagi diperlukan.

  3. Digunakan sqlite3_execuntuk menjalankan SQL (misalnya membuat tabel).

    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. Gunakan sqlite3_prepare_v2untuk mempersiapkan SQL dengan ?placeholder yang akan kami ikat nilainya.

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

    Catatan, itu menggunakan SQLITE_TRANSIENTkonstanta yang dapat diimplementasikan sebagai berikut:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
    
  5. Setel ulang SQL untuk memasukkan nilai lain. Dalam contoh ini, saya akan memasukkan NULLnilai:

    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. Selesaikan pernyataan yang disiapkan untuk memulihkan memori yang terkait dengan pernyataan yang disiapkan itu:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Siapkan pernyataan baru untuk memilih nilai dari tabel dan perulangan melalui pengambilan nilai:

    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. Tutup database:

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

Untuk Swift 2 dan versi Xcode yang lebih lama, lihat revisi sebelumnya dari jawaban ini .

rampok
sumber
1
Bagi mereka yang mendapat beberapa masalah pada pass 1, pertimbangkan ini: Buat Header Bridging di proyek Xcode Anda (mis. BridgingHeader.h); File header ini mungkin hanya memiliki baris yang mengimpor header Objective-C / C untuk dihubungkan ke Swift (misalnya #include <sqlite3.h>); Pada "Build Settings", temukan "Objective-C Bridging Header" (Anda dapat menggunakan bilah pencarian) dan ketik "BridgingHeader.h" (jika Anda mendapatkan pesan kesalahan seperti "Tidak dapat mengimpor Objective-C Header", coba "project- name / BridgingHeader.h "); Pergi ke "Build Phases", "Link Binary With Libraries", dan tambahkan libsqlite3.0.dylib atau libsqlite3.0.tbd di XCode 7
Jorg B Jorge
Apakah lebih baik jika menumpuk if (... == SQLITE_OK) sehingga hal berikut ini tidak akan dijalankan jika gagal. Saya murni bertanya karena saya sangat baru dalam hal ini dan hanya ingin tahu apakah Anda melakukan ini untuk tujuan pengajaran.
quemeful
@quemeful - Tentu, tetapi jika Anda melakukannya dengan banyak panggilan SQLite, Anda akan mendapatkan kode yang sangat bertingkat. Jika Anda khawatir tentang ini, saya mungkin akan menggunakan guardpernyataan sebagai gantinya.
Rob
@Jorg B Jorge Saya melakukan segalanya, apakah Anda juga perlu mengimpor header yang menjembatani? Saya bekerja di kelas tes
Async-
Hai @Rob, saya menggunakan pembungkus sqlite Anda dalam proyek Swift di sini. Ini sangat bagus, terima kasih. Namun, saya tidak bisa melakukan hitungan pilih (*) dari tabel dengannya. Itu terus mogok. Jika saya melakukan hitungan pilih (col_name) dari tablename di mana some_col = xxx, itu berhasil. Apa yang Anda sarankan?
gbenroscience
18

Hal terbaik yang dapat Anda lakukan adalah mengimpor pustaka dinamis di dalam header penghubung:

  1. Tambahkan libsqlite3.dylib ke fase pembuatan "Link Binary With Libraries"
  2. Buat "Bridging-Header.h" dan tambahkan #import <sqlite3.h>ke atas
  3. setel "Bridging-Header.h" untuk setelan "Objective-C Bridging Header" di Build Settings di bawah "Swift Compiler - Code Generation"

Anda kemudian akan dapat mengakses semua metode c seperti sqlite3_opendari kode swift Anda.

Namun, Anda mungkin hanya ingin menggunakan FMDB dan mengimpornya melalui header bridging karena itu adalah pembungkus sqlite yang lebih berorientasi objek. Berurusan dengan pointer C dan struct akan menjadi rumit di Swift.

menggambar
sumber
Saya harus menambahkan ini di bawah pengaturan Proyek membangun dan bukan pengaturan membangun Target agar Xcode dapat menemukan header penghubung.
rob5408
3
juga semua orang dan ayah mereka kini telah membuat pembungkus Swift .. lihat di bawah
Quemeful
1
Sayangnya, tidak ada yang terlalu matang, jadi jika Anda menggunakan salah satu bungkus baru ini, berhati-hatilah. Misalnya, pada saat penulisan, saya melirik empat di antaranya, dan tiga menangani tanggal secara tidak benar dan yang keempat tidak menangani semuanya sama sekali.
Rob
@Rob Pernahkah Anda melihat github.com/stephencelis/SQLite.swift#readme ? Informasi tentang konfigurasi untuk digunakan dengan NSDate di sini: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis
@stephencelis Hei, itu lebih baik daripada kebanyakan dari mereka, karena setidaknya Anda menentukan zona waktunya, tapi saya masih punya masalah dengan itu NSDateFormatter. Tetapi niat saya bukan untuk mengkritik aspek khusus ini dari implementasi khusus ini daripada menyarankan bahwa ini menunjukkan masalah yang lebih luas, bahwa ini tidak memiliki tahun penyempurnaan yang dimiliki solusi seperti FMDB. Saya pikir orang terlalu cepat untuk membuang solusi Objective-C yang sudah terbukti demi penerapan Swift yang kurang matang (TFHpple vs NDHpple adalah contoh bagus lainnya).
Rob
11

Saya juga sedang mencari cara untuk berinteraksi dengan SQLite dengan cara yang sama seperti yang biasa saya lakukan sebelumnya di Objective-C. Memang, karena kompatibilitas C, saya baru saja menggunakan C API langsung.

Karena saat ini tidak ada pembungkus untuk SQLite di Swift dan kode SQLiteDB yang disebutkan di atas berjalan sedikit lebih tinggi dan mengasumsikan penggunaan tertentu, saya memutuskan untuk membuat pembungkus dan sedikit terbiasa dengan Swift dalam prosesnya. Anda dapat menemukannya di sini: 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 */
Chris Simpson
sumber
5

Saya telah membuat pustaka SQLite elegan yang ditulis sepenuhnya dalam Swift yang disebut SwiftData .

Beberapa fiturnya adalah:

  • Ikat objek dengan nyaman ke string SQL
  • Dukungan untuk transaksi dan savepoint
  • Penanganan error sebaris
  • Amankan utas sepenuhnya secara default

Ini menyediakan cara mudah untuk menjalankan 'perubahan' (mis. INSERT, UPDATE, DELETE, dll.):

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
}

dan 'query' (misalnya PILIH):

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

Seiring dengan lebih banyak fitur!

Anda bisa memeriksanya di sini

ryanfowler.dll
sumber
Sayangnya lib Anda hanya untuk iOS! : - /
BadmintonCat
3

Pembungkus SQLite lain untuk Swift 2 dan Swift 3: http://github.com/groue/GRDB.swift

Fitur:

  • API yang akan terlihat familier bagi pengguna ccgus / fmdb

  • API SQLite tingkat rendah yang memanfaatkan pustaka standar Swift

  • Antarmuka kueri Swift yang cantik untuk pengembang yang alergi terhadap SQL

  • Dukungan untuk mode SQLite WAL, dan akses database bersamaan untuk kinerja ekstra

  • Kelas Record yang membungkus kumpulan hasil, memakan kueri SQL kustom Anda untuk sarapan, dan menyediakan operasi CRUD dasar

  • Kebebasan tipe Swift: pilih tipe Swift yang tepat yang sesuai dengan data Anda. Gunakan Int64 bila perlu, atau tetap gunakan Int. Simpan dan baca NSDate atau NSDateComponents. Deklarasikan enum Swift untuk tipe data diskrit. Tentukan tipe konvertibel database Anda sendiri.

  • Migrasi Database

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

Gwendal Roué
sumber
GRDB adalah salah satu framework terbaik yang didokumentasikan, didukung, dan dipelihara di 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 = ""
}

Access database:

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

database Query fire:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
sumber
qyery ini tidak berfungsi. mengapa ada == bukan hanya satu =?
ArgaPK
1

Sejauh ini, ini adalah pustaka SQLite terbaik yang pernah saya gunakan di Swift: https://github.com/stephencelis/SQLite.swift

Lihat contoh kode. Jauh lebih bersih dari 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 <- "[email protected]")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', '[email protected]')

for user in try db.prepare(users) {
    print("id: \(user[id]), name: \(user[name]), email: \(user[email])")
    // id: 1, name: Optional("Alice"), email: [email protected]
}
// 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"

Dokumentasi juga mengatakan bahwa "SQLite.swift juga berfungsi sebagai pembungkus ringan dan ramah Swift melalui C API," dan diikuti dengan beberapa contohnya.

Andrew Koster
sumber
0

Saya telah menulis pustaka pembungkus SQLite3 yang ditulis dalam Swift .

Ini sebenarnya adalah pembungkus tingkat sangat tinggi dengan API yang sangat sederhana, tetapi bagaimanapun, ia memiliki kode antar-operasi C tingkat rendah, dan saya memposting di sini bagian (yang disederhanakan) untuk menunjukkan antar-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
    }

Jika Anda menginginkan kode sumber lengkap dari pembungkus tingkat rendah ini, lihat file ini.

eonil
sumber
0

Konfigurasikan proyek Swift Anda untuk menangani panggilan SQLite C:

Buat file header penghubung ke proyek. Lihat bagian Mengimpor Objective-C ke Swift dari Menggunakan Swift dengan Cocoa dan Objective-C. Header penghubung ini harus mengimpor sqlite3.h:

Tambahkan libsqlite3.0.dylib ke proyek Anda. Lihat dokumentasi Apple tentang menambahkan perpustakaan / kerangka kerja ke proyek seseorang.

dan menggunakan kode berikut

    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
        }
    }
}
chirag shah
sumber
0

Terkadang, versi Swift dari pendekatan "SQLite dalam 5 menit atau kurang" yang ditampilkan di sqlite.org sudah cukup. The "5 menit atau kurang" pendekatan kegunaan sqlite3_exec()yang merupakan pembungkus kemudahan bagi sqlite3_prepare(), sqlite3_step(), sqlite3_column(), dan sqlite3_finalize().

Swift 2.2 dapat secara langsung mendukung sqlite3_exec() callbackpenunjuk fungsi baik sebagai prosedur non-instans global funcatau penutupan literal non-capturing {}.

Dapat dibaca typealias

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

Pendekatan Callback

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
}

Pendekatan Penutupan

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
}

Untuk mempersiapkan proyek Xcode untuk memanggil pustaka C seperti SQLite, seseorang perlu (1) menambahkan header C referensi file Bridging-Header.h seperti #import "sqlite3.h", (2) menambahkan Bridging-Header.h ke Objective-C Bridging Header dalam proyek pengaturan, dan (3) tambahkan libsqlite3.tbdke Link Binary With Library pengaturan target.

The sqlite.org 's 'SQLite dalam 5 menit atau kurang' Misalnya diimplementasikan dalam proyek Swift Xcode7 sini .

l --marc l
sumber
0

Anda dapat menggunakan pustaka ini di Swift untuk SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Demo SQLite menggunakan Swift dengan kelas SQLDataAccess yang ditulis di Swift

Menambahkan ke Proyek Anda

Anda hanya perlu tiga file untuk ditambahkan ke proyek Anda * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header harus disetel di proyek Xcode Anda 'Objective-C Bridging Header' di bawah 'Swift Compiler - General'

Contoh Penggunaan

Cukup ikuti kode di ViewController.swift untuk melihat cara menulis SQL sederhana dengan SQLDataAccess.swift Pertama, Anda perlu membuka Database SQLite yang berhubungan dengan Anda.

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

Jika openConnection berhasil, sekarang Anda dapat melakukan penyisipan sederhana ke Tabel 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)")
    }

Lihat betapa sederhananya itu!

Istilah pertama dalam db.executeStatement adalah SQL Anda sebagai String, semua istilah berikut adalah daftar argumen variadic tipe Any, dan merupakan parameter Anda dalam Array. Semua istilah ini dipisahkan dengan koma di daftar argumen SQL Anda. Anda dapat memasukkan Strings, Integers, Date's, dan Blob tepat setelah pernyataan sekuel karena semua istilah ini dianggap sebagai parameter untuk sekuelnya. Array argumen variadic membuatnya nyaman untuk memasukkan semua sekuel Anda hanya dalam satu panggilan executeStatement atau getRecordsForQuery. Jika Anda tidak memiliki parameter apa pun, jangan masukkan apa pun setelah SQL Anda.

Larik hasil adalah Array Kamus dengan 'kunci' adalah nama kolom tabel Anda, dan 'nilai' adalah data yang diperoleh dari SQLite. Anda dapat dengan mudah melakukan iterasi melalui larik ini dengan loop for atau mencetaknya secara langsung atau menetapkan elemen Dictionary ini ke objek data kustom Kelas yang Anda gunakan di View Controllers untuk konsumsi model.

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

SQLDataAccess akan menyimpan teks, double, float, blob, Date, integer dan long long integers. Untuk Blob Anda dapat menyimpan biner, varbinary, blob.

Untuk Teks Anda dapat menyimpan karakter, karakter, gumpalan, karakter bervariasi nasional, karakter asli, nchar, nvarchar, varchar, varian, karakter bervariasi, teks.

Untuk Tanggal Anda dapat menyimpan tanggal waktu, waktu, cap waktu, tanggal.

Untuk Integer Anda dapat menyimpan bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Untuk Ganda Anda dapat menyimpan desimal, presisi ganda, float, numeric, real, double. Ganda memiliki presisi paling tinggi.

Anda bahkan dapat menyimpan Null dari tipe Null.

Dalam ViewController.swift, contoh yang lebih kompleks telah diselesaikan dengan menunjukkan cara menyisipkan Kamus sebagai 'Blob'. Selain itu, SQLDataAccess memahami Swift Date () asli sehingga Anda dapat menyisipkan objek ini tanpa mengonversi, dan itu akan mengonversinya menjadi teks dan menyimpannya, dan ketika diambil, konversikan kembali dari teks ke Tanggal.

Tentu saja kekuatan sebenarnya dari SQLite adalah kemampuan Transaksinya. Di sini Anda benar-benar dapat mengantri 400 pernyataan SQL dengan parameter dan memasukkan semuanya sekaligus yang sangat kuat karena sangat cepat. ViewController.swift juga menunjukkan contoh bagaimana melakukan ini. Yang benar-benar Anda lakukan adalah membuat Array of Dictionaries yang disebut 'sqlAndParams', di Array ini Kamus penyimpanan Anda dengan dua kunci 'SQL' untuk pernyataan atau kueri sekuel String, dan 'PARAMS' yang hanya Array objek asli SQLite memahami kueri itu. Setiap 'sqlParams' yang merupakan Dictionary individual dari kueri sekuel plus parameter kemudian disimpan dalam Array 'sqlAndParams'. Setelah Anda membuat array ini, panggil saja.

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

Selain itu, semua metode executeStatement dan getRecordsForQuery dapat dilakukan dengan String sederhana untuk kueri SQL dan Array untuk parameter yang dibutuhkan oleh kueri.

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

Versi Objective-C juga ada dan disebut SQLDataAccess yang sama, jadi sekarang Anda dapat memilih untuk menulis sekuel Anda di Objective-C atau Swift. Selain itu SQLDataAccess juga akan bekerja dengan SQLCipher, kode saat ini belum disiapkan untuk bekerja dengannya, tetapi cukup mudah untuk dilakukan, dan contoh bagaimana melakukannya sebenarnya dalam versi Objective-C dari SQLDataAccess.

SQLDataAccess adalah kelas yang sangat cepat dan efisien, dan dapat digunakan sebagai pengganti CoreData yang benar-benar hanya menggunakan SQLite sebagai penyimpanan data yang mendasarinya tanpa semua gangguan integritas data inti CoreData yang datang dengan CoreData.

Embun beku
sumber
-1

Anda dapat dengan mudah mengkonfigurasi SQLite dengan swift menggunakan kelas ton tunggal juga.

Lihat

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Metode untuk membuat database

func methodToCreateDatabase() -> NSURL?{} 

Metode untuk memasukkan, memperbarui dan menghapus data

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

Metode untuk memilih data

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