Доступ до бази даних SQLite в Swift


103

Я шукаю спосіб отримати доступ до бази даних SQLite у своєму додатку з кодом Swift.

Я знаю, що я можу використовувати програму SQLite Wrapper в Objective C і використовувати мостиковий заголовок, але я скоріше зможу зробити цей проект цілком у Swift. Чи є спосіб це зробити, якщо так, чи може хтось вказати мені на посилання, яке показує, як подати запит, отримати рядки тощо?



1
куди я повинен помістити файл бази даних?
C. Феліана

1
@ C.Feliana - Каталог підтримки додатків - це чудове місце, наприклад let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Роб

Відповіді:


143

Хоча, ймовірно, ви повинні використовувати один із багатьох обгортків SQLite, якщо ви хочете знати, як самостійно викликати бібліотеку SQLite, ви:

  1. Налаштуйте проект Swift для обробки дзвінків SQLite C. Якщо ви використовуєте Xcode 9 або новішої версії, ви можете просто зробити:

    import SQLite3
  2. Створення / відкриття бази даних.

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

    Зауважте, я знаю, що здається дивним закривати базу даних після невміння відкрити, але в sqlite3_open документації чітко зазначено, що ми повинні робити це, щоб уникнути витоку пам'яті:

    Незалежно від того, чи виникає помилка під час її відкриття, ресурси, пов’язані з ручкою з'єднання з базою даних, слід звільнити, передавши її, sqlite3_close()коли вона більше не потрібна.

  3. Використовувати sqlite3_execдля виконання SQL (наприклад, створити таблицю).

    if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error creating table: \(errmsg)")
    }
    
  4. Використовуйте sqlite3_prepare_v2для підготовки SQL із ?заповнювачем, до якого ми прив’яжемо значення.

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

    Зауважте, що використовується SQLITE_TRANSIENTконстанта, яка може бути реалізована наступним чином:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Скиньте SQL, щоб вставити інше значення. У цьому прикладі я вставлю NULLзначення:

    if sqlite3_reset(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error resetting prepared statement: \(errmsg)")
    }
    
    if sqlite3_bind_null(statement, 1) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding null: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting null: \(errmsg)")
    }
  6. Доопрацюйте підготовлений оператор, щоб відновити пам'ять, пов'язану з цим підготовленим оператором:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Підготуйте новий оператор для вибору значень із таблиці та циклу шляхом отримання значень:

    if sqlite3_prepare_v2(db, "select id, name from test", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing select: \(errmsg)")
    }
    
    while sqlite3_step(statement) == SQLITE_ROW {
        let id = sqlite3_column_int64(statement, 0)
        print("id = \(id); ", terminator: "")
    
        if let cString = sqlite3_column_text(statement, 1) {
            let name = String(cString: cString)
            print("name = \(name)")
        } else {
            print("name not found")
        }
    }
    
    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  8. Закрити базу даних:

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

Для Swift 2 та старіших версій Xcode див. Попередні редакції цієї відповіді .


1
Для тих, хто отримав деякі проблеми за пропуск 1, врахуйте це: Створіть мостиковий заголовок у своєму проекті Xcode (наприклад, BridgingHeader.h); Цей файл заголовка може мати лише рядки, що імпортують заголовки Objective-C / C для з'єднання з Swift (наприклад, #include <sqlite3.h>); У розділі "Налаштування побудови" знайдіть "Об'єктивний заголовок мосту C" (Ви можете використовувати панель пошуку) та введіть "BridgingHeader.h" (якщо ви отримаєте повідомлення про помилку типу "Не вдалося імпортувати заголовок Objective-C", спробуйте "project- ім’я / BridgingHeader.h "); Перейдіть до «Фаз збірки», «Пов’язати бінарне з бібліотеками» та додайте libsqlite3.0.dylib або libsqlite3.0.tbd у XCode 7
Jorg B Jorge

Було б краще вкласти "if" (... == SQLITE_OK), щоб наступне не виконувалося, якщо воно не вдалося. Я чисто запитую, тому що я дуже новачок у цьому і мені було просто цікаво, якщо ви робили це для навчальних цілей.
пам’ятний

@quemeful - Звичайно, але якщо ви робите це з багатьма дзвінками SQLite, ви отримуєте код, який дійсно глибоко вкладений. Якщо вас це турбує, я, мабуть, guardзамість цього використовую заяви.
Роб

@ Джордж Б Хорхе Я все зробив, чи потрібно також імпортувати якось мостовий заголовок? Я працюю в тестовому класі
Async-

Привіт @Rob, тут я використовую вашу обгортку sqlite в проекті Swift. Це дійсно приємно, дякую. Однак я не можу зробити з ним таблицю вибору (*). Це продовжує збої. Якщо я зробив підрахунок (col_name) з імені таблиці, де some_col = xxx, це працює. Що ти пропонуєш?
gbenroscience

18

Найкраще, що ви можете зробити, це імпортувати динамічну бібліотеку всередині мостового заголовка:

  1. Додайте libsqlite3.dylib до фази збірки "Бінарне посилання з бібліотеками"
  2. Створіть "Bridging-Header.h" та додайте #import <sqlite3.h>до початку
  3. встановіть "Bridging-Header.h" для налаштування "Об'єктивний-C Bridging Header" у налаштуваннях збірки в розділі "Swift Compiler - Generation Code"

Тоді ви зможете отримати доступ до всіх методів c, наприклад, sqlite3_openзі свого швидкого коду.

Однак, ви можете просто використовувати FMDB та імпортувати це через мостиковий заголовок, оскільки це більш об'єктно-орієнтована обгортка sqlite. Робота з покажчиками та структурами С буде громіздкою в Swift.


Мені довелося додати це в налаштуваннях збірки проекту, а не в налаштуваннях цільової збірки для того, щоб Xcode знайшов мостовий заголовок.
rob5408

3
також усі та їх батько створили обгортку Swift. Дивіться нижче
quemeful

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 vs NDHpple - ще один хороший приклад).
Роб

11

Я теж шукав якийсь спосіб взаємодіяти з SQLite так само, як я звик раніше в Objective-C. Правда, через сумісність з C я просто використав прямий C API.

Оскільки для Swift в 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, повністю написану в Swift під назвою 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)")
        }
    }
}

Поряд із ще багатьма функціями!

Ви можете перевірити це тут


На жаль, ваша лайба лише для iOS! : - /
BadmintonCat

3

Ще одна обгортка SQLite для Swift 2 та Swift 3: http://github.com/groue/GRDB.swift

Особливості:

  • API, який буде виглядати звичним користувачам ccgus / fmdb

  • API низького рівня SQLite, який використовує стандартну бібліотеку Swift

  • Досить інтерфейс запитів Swift для розробників SQL-алергіків

  • Підтримка режиму WAL SQLite та паралельний доступ до бази даних для отримання додаткової продуктивності

  • Клас Record, який обгортає набори результатів, їсть ваші власні SQL-запити на сніданок і забезпечує основні CRUD-операції

  • Свобода типу Swift: виберіть потрібний тип Swift, який відповідає вашим даним. Використовуйте Int64 за потреби або дотримуйтесь зручного Int. Зберігайте та читайте NSDate або NSDateComponents. Заявити Swift перераховує для дискретних типів даних. Визначте власні типи конвертованих баз даних.

  • Міграція бази даних

  • Швидкість: https://github.com/groue/GRDB.swift/wiki/Performance


GRDB - одна з найкращих документально підтверджених, підтримуваних та підтримуваних фреймворків на Github!
Клаас

3

AppDelegate.swift

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

    print(DBpath)

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

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

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

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

Подивіться приклади коду. Настільки чистіший за API 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 також працює як легкий, зручний для Swift обгортки по API C", і випливає з деяких прикладів цього.


0

Я написав бібліотеку обгортки SQLite3, написану на Swift .

Це насправді обгортка дуже високого рівня з дуже простим API, але в будь-якому випадку, він має інтерпресійний код низького рівня, і я розміщую тут (спрощену) його частину, щоб показувати взаємодію C.

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

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

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

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

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

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

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

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

        return  (stmts, rest2)
    }

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

        switch rc1
        {   
            case SQLITE_ROW:
                return  true

            case SQLITE_DONE:
                return  false

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

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

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

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

        _rawptr =   C.NULL
    }

Якщо ви хочете отримати повний вихідний код цієї обгортки низького рівня, перегляньте ці файли.


0

Налаштуйте свій проект Swift для обробки дзвінків SQLite C:

Створіть для проекту проектний заголовок. Див. Розділ Імпорт об'єктива-C у Swift у розділі Використання Swift з какао та 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

Іноді, Swift версії підходу "SQLite за 5 хвилин або менше", показаного на sqlite.org , достатньо. «5 хвилин або менше» підхід використання sqlite3_exec()яких є зручність обгортка для sqlite3_prepare(), sqlite3_step(), sqlite3_column(), і sqlite3_finalize().

Swift 2.2 може безпосередньо підтримувати sqlite3_exec() callbackпокажчик функції як глобальна процедура, яка не є екземпляром, funcабо як буквальне закриття {}.

Читабельна typealias

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

Підхід зворотного дзвінка

func callback(
    resultVoidPointer: CVoidPointer, // void *NotUsed 
    columnCount: CInt,               // int argc
    values: CCharHandle,             // char **argv     
    columns: CCharHandle             // char **azColName
    ) -> CInt {
    for  i in 0 ..< Int(columnCount) {
        guard let value = String.fromCString(values[i]) 
        else { continue }
        guard let column = String.fromCString(columns[i]) 
        else { continue }
        print("\(column) = \(value)")
    }
    return 0 // status ok
}

func sqlQueryCallbackBasic(argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0 // result code

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(db, argv[2], callback, nil, &zErrMsg)
    if rc != SQLITE_OK {
        print("ERROR: sqlite3_exec " + String.fromCString(zErrMsg)! ?? "")
        sqlite3_free(zErrMsg)
    }

    sqlite3_close(db)
    return 0
}

Підхід до закриття

func sqlQueryClosureBasic(argc argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(
        db,      // database 
        argv[2], // statement
        {        // callback: non-capturing closure
            resultVoidPointer, columnCount, values, columns in

            for i in 0 ..< Int(columnCount) {
                guard let value = String.fromCString(values[i]) 
                else { continue }
                guard let column = String.fromCString(columns[i]) 
                else { continue }
                print("\(column) = \(value)")
            }
            return 0
        }, 
        nil, 
        &zErrMsg
    )

    if rc != SQLITE_OK {
        let errorMsg = String.fromCString(zErrMsg)! ?? ""
        print("ERROR: sqlite3_exec \(errorMsg)")
        sqlite3_free(zErrMsg)
    }
    sqlite3_close(db)
    return 0
}

Щоб підготувати проект Xcode для виклику бібліотеки C, такої як SQLite, потрібно (1) додати посилання на файл Bridging-Header.h, наприклад #import "sqlite3.h", C заголовки, як (2) додати Bridging-Header.h до об'єктивного заголовка мосту Objective-C у проекті налаштування та (3) додати libsqlite3.tbdдо цільових налаштувань Link Binary With Library .

Sqlite.org «s " SQLite за 5 хвилин або менше " , наприклад реалізується в рамках проекту Swift Xcode7 тут .


0

Ви можете використовувати цю бібліотеку в Swift для SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Демонстрація SQLite за допомогою Swift із класом SQLDataAccess, написаним у Swift

Додавання до вашого проекту

Вам потрібно лише три файли, щоб додати до свого проекту * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header повинен бути встановлений у проекті Xkod '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, і це ваші параметри в масиві. Усі ці терміни розділені комами у вашому списку аргументів SQL. Ви можете вводити рядки, цілі числа, дату та краплі відразу після заяви продовження, оскільки всі ці умови вважаються параметрами для продовження. Різноманітний масив аргументів просто дозволяє зручно вводити все ваше продовження лише в одному виклику ExecuteStatement або getRecordsForQuery. Якщо у вас немає параметрів, не вводите нічого після свого SQL.

Масив результатів - це масив словника, де "ключ" - це ім'я стовпця таблиці, а "значення" - ваші дані, отримані від SQLite. Ви можете легко повторити цей масив за допомогою циклу for або передрукувати його безпосередньо або призначити ці елементи словника власним класам об'єктів даних, які ви використовуєте у своїх контролерах перегляду для споживання моделі.

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

SQLDataAccess буде зберігати, текст, подвійний, плаваючий, краплинний, дата, ціле число та довгі довгі цілі числа. Для Blobs ви можете зберігати двійкові, варбінарні, blob.

У тексті ви можете зберігати char, персонаж, clob, національний мінливий персонаж, нативний персонаж, nchar, nvarchar, varchar, варіант, змінюючий символ, текст.

Для дат ви можете зберігати дату, час, позначку часу, дату.

Для Integers ви можете зберігати bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Для парних пар можна зберігати десяткову, подвійну точність, поплавок, числовий, реальний, подвійний. Подвійна має найбільшу точність.

Можна навіть зберігати Nulls типу Null.

У ViewController.swift робиться більш складний приклад, що показує, як вставити словник як "Blob". Крім того, SQLDataAccess розуміє нативну дату Swift (), тому ви можете вставити ці об'єкти з конвертуванням, і вони перетворять їх у текст і зберігатимуть їх, а при отриманні перетворюватимуть їх назад із тексту в дату.

Звичайно, реальна сила SQLite - це можливість транзакцій. Тут ви можете буквально встановити чергу до 400 операторів SQL з параметрами та вставити їх усі відразу, що є дуже потужним, оскільки це так швидко. ViewController.swift також показує вам приклад того, як це зробити. Все, що ви насправді робите, - це створити масив словників під назвою 'sqlAndParams', в цьому масиві зберігати словники з двома ключами 'SQL' для подальшого оператора або запиту String і 'PARAMS' - це лише масив нативних об'єктів SQLite розуміє для цього запиту. Кожен 'sqlParams', який є індивідуальним словником подальшого запиту плюс параметри, зберігається у масиві 'sqlAndParams'. Після створення цього масиву ви просто дзвоните.

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

Крім того, всі методи ExecuteStatement і getRecordsForQuery можна виконати за допомогою простого String для запиту SQL та масиву для параметрів, необхідних для запиту.

    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{}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.