การเข้าถึงฐานข้อมูล SQLite ใน Swift


103

ฉันกำลังมองหาวิธีเข้าถึงฐานข้อมูล SQLite ในแอพของฉันด้วย Swift code

ฉันรู้ว่าฉันสามารถใช้ SQLite Wrapper ใน Objective C และใช้ bridging header ได้ แต่ฉันอยากจะทำโปรเจ็กต์นี้ทั้งหมดใน Swift มีวิธีทำเช่นนี้หรือไม่หากมีใครสามารถชี้ให้ฉันดูข้อมูลอ้างอิงที่แสดงวิธีการส่งแบบสอบถามเรียกข้อมูลแถว ฯลฯ



1
ฉันควรวางไฟล์ฐานข้อมูลไว้ที่ไหน
C. Feliana

1
@ C.Feliana - let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").pathไดเรกทอรีสนับสนุนการประยุกต์ใช้เป็นสถานที่ที่ดีเช่น
Rob

คำตอบ:


145

ในขณะที่คุณควรใช้หนึ่งในตัวตัด 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
    

สำหรับสวิฟท์ที่ 2 และรุ่นเก่าของ Xcode ดูการแก้ไขก่อนหน้าของคำตอบนี้


1
สำหรับผู้ที่มีปัญหาในการผ่าน 1 ให้พิจารณาสิ่งนี้: สร้าง Bridging Header ในโปรเจ็กต์ Xcode ของคุณ (เช่น BridgingHeader.h); ไฟล์ส่วนหัวนี้อาจมีเฉพาะบรรทัดที่นำเข้าส่วนหัว Objective-C / C สำหรับเชื่อมต่อกับ Swift (เช่น #include <sqlite3.h>); ใน "Build Settings" ให้ค้นหา "Objective-C Bridging Header" (คุณอาจใช้แถบค้นหา) และพิมพ์ "BridgingHeader.h" (หากคุณได้รับข้อความแสดงข้อผิดพลาดเช่น "ไม่สามารถนำเข้า Objective-C Header" ให้ลอง "project- ชื่อ / BridgingHeader.h "); ไปที่ "Build Phases", "Link Binary With Libraries" และเพิ่ม libsqlite3.0.dylib หรือ libsqlite3.0.tbd ใน XCode 7
Jorg B Jorge

จะดีกว่าไหมหากซ้อน if (... == SQLITE_OK) เพื่อไม่ให้สิ่งต่อไปนี้ดำเนินการหากล้มเหลว ฉันถามอย่างหมดจดเพราะฉันยังใหม่มากและแค่อยากรู้ว่าคุณทำเพื่อจุดประสงค์ในการสอน
ดับ

@quemeful - แน่นอน แต่ถ้าคุณทำเช่นนั้นกับการเรียก SQLite จำนวนมากคุณจะจบลงด้วยรหัสที่ซ้อนกันอย่างลึกซึ้ง หากคุณกังวลเกี่ยวกับเรื่องนี้ฉันอาจใช้guardข้อความแทน
Rob

@Jorg B Jorge ฉันทำทุกอย่างแล้วคุณต้องนำเข้าส่วนหัวเชื่อมต่อด้วยหรือไม่? ฉันกำลังทำงานในชั้นเรียนทดสอบ
Async-

สวัสดี @Rob ฉันใช้ sqlite wrapper ของคุณในโครงการ Swift ที่นี่ ดีจริงๆขอบคุณ อย่างไรก็ตามฉันไม่สามารถนับจำนวนการเลือก (*) จากตารางได้ มันหยุดทำงาน ถ้าฉันเลือกจำนวนที่เลือก (col_name) จาก tablename โดยที่ some_col = xxx ก็ใช้ได้ คุณแนะนำอะไร?
gbenroscience

18

สิ่งที่ดีที่สุดที่คุณทำได้คือนำเข้าไลบรารีไดนามิกภายในส่วนหัวเชื่อมต่อ:

  1. เพิ่ม libsqlite3.dylib ลงในเฟสบิวด์ "ลิงก์ไบนารีกับไลบรารี"
  2. สร้าง "Bridging-Header.h" และเพิ่ม#import <sqlite3.h>ที่ด้านบน
  3. ตั้งค่า "Bridging-Header.h" สำหรับการตั้งค่า "Objective-C Bridging Header" ใน Build Settings ภายใต้ "Swift Compiler - Code Generation"

จากนั้นคุณจะสามารถเข้าถึงวิธี c ทั้งหมดเช่นsqlite3_openจากรหัสที่รวดเร็วของคุณ

อย่างไรก็ตามคุณอาจต้องการใช้FMDBและนำเข้าผ่านส่วนหัวเชื่อมต่อเนื่องจากเป็นกระดาษห่อหุ้มที่เน้นวัตถุมากกว่าของ sqlite การจัดการกับตัวชี้ C และโครงสร้างจะยุ่งยากใน Swift


ฉันต้องเพิ่มสิ่งนี้ภายใต้การตั้งค่าการสร้างโครงการไม่ใช่การตั้งค่าการสร้างเป้าหมายเพื่อให้ Xcode ค้นหาส่วนหัวเชื่อมต่อ
rob5408

3
ตอนนี้ทุกคนและพ่อของพวกเขาได้สร้าง Swift wrapper แล้ว .. ดูด้านล่าง
ดับ

1
น่าเสียดายที่ไม่มีใครที่โตเต็มที่ดังนั้นหากคุณใช้กระดาษห่อใหม่เหล่านี้โปรดระวัง ตัวอย่างเช่นในขณะที่เขียนฉันมองไปที่สี่ในนั้นและวันที่จัดการสามวันไม่ถูกต้องและวันที่สี่ไม่ได้จัดการเลย
Rob

@ ร็อบคุณดูgithub.com/stephencelis/SQLite.swift#readmeหรือยัง? ข้อมูลการกำหนดค่าเพื่อใช้กับ NSDate ที่นี่: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis

@stephencelis เฮ้ที่ดีกว่ามากที่สุดของพวกเขาเพราะอย่างน้อยคุณระบุเขตเวลา แต่ฉันยังคงมีปัญหาNSDateFormatterกับที่ แต่เจตนาของฉันน้อยที่จะวิจารณ์แง่มุมเฉพาะของการใช้งานเหล่านี้มากกว่าที่จะชี้ให้เห็นว่ามันบ่งบอกถึงปัญหาที่กว้างขึ้นซึ่งสิ่งเหล่านี้ไม่มีการปรับแต่งหลายปีที่โซลูชันเช่น FMDB มี ฉันคิดว่าผู้คนเร็วเกินไปที่จะทิ้งโซลูชัน Objective-C ที่ได้รับการพิสูจน์แล้วเพื่อสนับสนุนการใช้งาน Swift ที่เป็นผู้ใหญ่น้อยกว่า (TFHpple กับ NDHpple เป็นอีกตัวอย่างที่ดี)
Rob

11

ฉันก็กำลังมองหาวิธีโต้ตอบกับ SQLite แบบเดียวกับที่ฉันคุ้นเคยใน Objective-C ก่อนหน้านี้ เป็นที่ยอมรับเนื่องจากความเข้ากันได้ของ C ฉันจึงใช้ C API แบบตรง

เนื่องจากปัจจุบันไม่มีกระดาษห่อหุ้มสำหรับ SQLite ใน Swift และรหัส 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 */

5

ฉันได้สร้างห้องสมุด 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

ยังมี SQLite wrapper สำหรับ Swift 2 และ Swift 3 อีก: http://github.com/groue/GRDB.swift

คุณสมบัติ:

  • API ที่จะดูคุ้นเคยสำหรับผู้ใช้ccgus / fmdb

  • SQLite API ระดับต่ำที่ใช้ประโยชน์จากไลบรารีมาตรฐาน Swift

  • อินเทอร์เฟซการสืบค้น Swift ที่สวยงามสำหรับนักพัฒนาที่แพ้ SQL

  • รองรับโหมด SQLite WAL และการเข้าถึงฐานข้อมูลพร้อมกันเพื่อประสิทธิภาพพิเศษ

  • คลาสเรกคอร์ดที่รวมชุดผลลัพธ์กินคิวรี SQL ที่กำหนดเองของคุณเป็นอาหารเช้าและจัดเตรียมการดำเนินการ CRUD พื้นฐาน

  • อิสระในการพิมพ์อย่างรวดเร็ว: เลือกประเภท Swift ที่เหมาะสมกับข้อมูลของคุณ ใช้ Int64 เมื่อจำเป็นหรือติดกับ Int ที่สะดวก จัดเก็บและอ่าน NSDate หรือ NSDateComponents ประกาศ Swift enums สำหรับชนิดข้อมูลที่ไม่ต่อเนื่อง กำหนดประเภทฐานข้อมูลที่แปลงได้ของคุณเอง

  • การย้ายฐานข้อมูล

  • ความเร็ว: https://github.com/groue/GRDB.swift/wiki/Performance


GRDB เป็นหนึ่งในกรอบการทำงานที่ได้รับการสนับสนุนและดูแลรักษาที่ดีที่สุดบน 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 = ""
}

เข้าถึงฐานข้อมูล:

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

1

นี่เป็นไลบรารี SQLite ที่ดีที่สุดที่ฉันเคยใช้ใน Swift: 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" และตามด้วยตัวอย่างบางส่วน


0

ฉันได้เขียนห้องสมุด SQLite3 เสื้อคลุมเขียนในสวิฟท์

นี่เป็นกระดาษห่อหุ้มระดับสูงที่มี API ที่เรียบง่ายมาก แต่อย่างไรก็ตามมันมีโค้ด inter-op C ระดับต่ำและฉันโพสต์ส่วน (แบบง่าย) ไว้ที่นี่เพื่อแสดง C inter-op

    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
    }

หากคุณต้องการซอร์สโค้ดแบบเต็มของ Wrapper ระดับต่ำนี้โปรดดูไฟล์เหล่านี้


0

กำหนดค่าโครงการ Swift ของคุณเพื่อจัดการการโทร SQLite C:

สร้างไฟล์ส่วนหัวเชื่อมต่อกับโปรเจ็กต์ ดูส่วนการนำเข้า Objective-C ไปยัง Swift ของการใช้ Swift กับ Cocoa และ Objective-C ส่วนหัวเชื่อมต่อนี้ควรนำเข้า 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 ใน 5 นาทีหรือน้อยกว่า"เวอร์ชัน Swift ที่แสดงบนsqlite.orgก็เพียงพอแล้ว "การ 5 นาทีหรือน้อยกว่า" ใช้วิธีการsqlite3_exec()ที่เป็นเสื้อคลุมความสะดวกสบายสำหรับsqlite3_prepare(), sqlite3_step(), และsqlite3_column()sqlite3_finalize()

สวิฟท์ 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 เพื่อเรียกไลบรารี C เช่น SQLite เราต้อง (1) เพิ่มการอ้างอิงไฟล์ Bridging-Header.h ส่วนหัว C เช่น#import "sqlite3.h"(2) เพิ่ม Bridging-Header.h ให้กับObjective-C Bridging Headerในโปรเจ็กต์ การตั้งค่าและ (3) เพิ่มlibsqlite3.tbdในการตั้งค่าเป้าหมายของลิงก์ไบนารีด้วยไลบรารี

sqlite.org 's 'SQLite ใน 5 นาทีหรือน้อยกว่า'ตัวอย่างที่จะดำเนินการในโครงการสวิฟท์ Xcode7 ที่นี่


0

คุณสามารถใช้ไลบรารีนี้ใน Swift สำหรับ SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

SQLite Demo โดยใช้ Swift กับคลาส SQLDataAccess ที่เขียนด้วย Swift

การเพิ่มในโครงการของคุณ

คุณต้องการเพียงสามไฟล์เพื่อเพิ่มลงในโปรเจ็กต์ของคุณ * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header ต้องตั้งค่าในโครงการ Xcode ของคุณ 'Objective-C Bridging Header' ภายใต้ 'Swift Compiler - General'

ตัวอย่างการใช้งาน

เพียงทำตามรหัสใน ViewController.swift เพื่อดูวิธีการเขียน SQL อย่างง่ายด้วย SQLDataAccess.swift ก่อนอื่นคุณต้องเปิดฐานข้อมูล 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 และ Blobs ได้ทันทีหลังจากคำสั่ง sequel เนื่องจากคำศัพท์เหล่านี้ทั้งหมดถือเป็นพารามิเตอร์สำหรับผลสืบเนื่อง อาร์เรย์อาร์กิวเมนต์ตัวแปรทำให้สะดวกในการป้อนภาคต่อทั้งหมดของคุณในการเรียก executeStatement หรือ getRecordsForQuery เพียงครั้งเดียว หากคุณไม่มีพารามิเตอร์อย่าป้อนอะไรเลยหลัง SQL ของคุณ

อาร์เรย์ผลลัพธ์คืออาร์เรย์ของพจนานุกรมโดยที่ 'คีย์' คือชื่อคอลัมน์ตารางของคุณและ 'ค่า' คือข้อมูลของคุณที่ได้รับจาก SQLite คุณสามารถวนซ้ำผ่านอาร์เรย์นี้ได้อย่างง่ายดายด้วย for loop หรือพิมพ์ออกมาโดยตรงหรือกำหนดองค์ประกอบ Dictionary เหล่านี้ให้กับคลาสอ็อบเจ็กต์ข้อมูลแบบกำหนดเองที่คุณใช้ใน View Controllers สำหรับการใช้โมเดล

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

SQLDataAccess จะจัดเก็บข้อความคู่ลอยหยดวันที่จำนวนเต็มและจำนวนเต็มยาว สำหรับ Blobs คุณสามารถจัดเก็บ binary, varbinary, blob

สำหรับข้อความคุณสามารถจัดเก็บถ่านอักขระก้อนอักขระที่แตกต่างกันของประเทศอักขระพื้นเมือง nchar nvarchar varchar ตัวแปรอักขระที่แตกต่างกันข้อความ

สำหรับ Dates คุณสามารถจัดเก็บ datetime, time, timestamp, date ได้

สำหรับจำนวนเต็มคุณสามารถจัดเก็บ bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int

สำหรับคู่คุณสามารถจัดเก็บทศนิยม, ความแม่นยำสองเท่า, ลอย, ตัวเลข, จริง, สองเท่า Double มีความแม่นยำมากที่สุด

คุณยังสามารถจัดเก็บ Nulls ประเภท Null

ใน ViewController.swift มีตัวอย่างที่ซับซ้อนมากขึ้นซึ่งแสดงวิธีการแทรกพจนานุกรมเป็น 'Blob' นอกจากนี้ SQLDataAccess ยังเข้าใจ Swift Date () ดั้งเดิมเพื่อให้คุณสามารถแทรกวัตถุเหล่านี้ได้โดยไม่ต้องแปลงและจะแปลงเป็นข้อความและจัดเก็บและเมื่อดึงข้อมูลแล้วจะแปลงกลับจากข้อความเป็นวันที่

แน่นอนว่าพลังที่แท้จริงของ SQLite คือความสามารถในการทำธุรกรรม ที่นี่คุณสามารถจัดคิวคำสั่ง SQL 400 รายการพร้อมพารามิเตอร์และแทรกทั้งหมดพร้อมกันซึ่งมีประสิทธิภาพมากเนื่องจากมันเร็วมาก ViewController.swift ยังแสดงตัวอย่างวิธีการทำเช่นนี้ สิ่งที่คุณทำจริงๆคือการสร้าง Array of Dictionaries ที่เรียกว่า 'sqlAndParams' ใน Array นี้จัดเก็บ Dictionaries ของคุณด้วยสองปุ่ม 'SQL' สำหรับคำสั่ง String sequel หรือ query และ 'PARAMS' ซึ่งเป็นเพียง Array ของวัตถุพื้นเมือง 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 และ 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


-1

คุณสามารถกำหนดค่า SQLite ได้อย่างรวดเร็วโดยใช้คลาสตันเดียวเช่นกัน

อ้างถึง

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

วิธีการสร้างฐานข้อมูล

func methodToCreateDatabase() -> NSURL?{} 

วิธีการแทรกอัปเดตและลบข้อมูล

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

วิธีการเลือกข้อมูล

func methodToSelectData(strQuery : String) -> NSMutableArray{}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.