Доступ к базе данных SQLite в Swift

103

Я ищу способ получить доступ к базе данных SQLite в моем приложении с помощью кода Swift.

Я знаю, что могу использовать SQLite Wrapper в Objective C и использовать заголовок моста, но я бы предпочел полностью реализовать этот проект на Swift. Есть ли способ сделать это, если да, может ли кто-нибудь указать мне ссылку, которая показывает, как отправить запрос, получить строки и т. Д.?

Jase
источник
1
github.com/stephencelis/SQLite.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.
Роб

Ответы:

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

Для Swift 2 и более старых версий Xcode см. Предыдущие версии этого ответа .

Роб
источник
1
Тем, у кого возникли проблемы на этапе 1, рассмотрим следующее: Создайте заголовок моста в своем проекте Xcode (например, BridgingHeader.h); Этот файл заголовка может содержать только строки, импортирующие заголовки Objective-C / C для подключения к Swift (например, #include <sqlite3.h>); В «Настройках сборки» найдите «Заголовок моста Objective-C» (вы можете использовать панель поиска) и введите «BridgingHeader.h» (если вы получите сообщение об ошибке типа «Не удалось импортировать заголовок Objective-C», попробуйте «project- имя / BridgingHeader.h "); Перейдите в «Фазы сборки», «Свяжите двоичный файл с библиотеками» и добавьте libsqlite3.0.dylib или libsqlite3.0.tbd в XCode 7
Jorg B Jorge
Было бы лучше вложить if (... == SQLITE_OK), чтобы следующее не выполнялось в случае сбоя. Я просто спрашиваю, потому что я новичок в этом, и мне было просто любопытно, сделали ли вы это в учебных целях.
quemeful
@quemeful - Конечно, но если вы сделаете это с большим количеством вызовов SQLite, вы получите код, который действительно глубоко вложен. Если вас это беспокоит, я бы, вероятно, использовал guardвместо этого инструкции.
Роб
@Jorg B Jorge Я все сделал, вам еще нужно как-то импортировать мостовой заголовок? Я работаю в тестовом классе
Async
Привет, @Rob, здесь я использую вашу оболочку sqlite в проекте 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» в настройках сборки в разделе «Компилятор Swift - Генерация кода»

После этого вы сможете получить доступ ко всем методам c, например, sqlite3_openиз вашего быстрого кода.

Однако вы можете просто использовать FMDB и импортировать его через заголовок моста, поскольку это более объектно-ориентированная оболочка sqlite. Работа с указателями и структурами C в 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 против NDHpple).
Роб
11

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

Поскольку в настоящее время оболочки для 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, полностью написанную на 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

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

  • Класс 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

Посмотрите примеры кода. Намного чище, чем 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 также работает как легкая, дружественная к Swift оболочка над C API», и следует несколько примеров этого.

Эндрю Костер
источник
0

Я написал библиотеку-оболочку SQLite3, написанную на Swift .

На самом деле это оболочка очень высокого уровня с очень простым API, но в любом случае у нее есть низкоуровневый межоперационный код C, и я публикую здесь (упрощенную) его часть, чтобы показать взаимодействие 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:

Создайте файл заголовка моста для проекта. См. Раздел «Импорт Objective-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) добавить заголовки C ссылки на файл Bridging-Header.h, например #import "sqlite3.h", (2) добавить Bridging-Header.h в заголовок моста Objective-C в проекте настройки и (3) добавить libsqlite3.tbdв Link Binary With Library настройки целевые параметры.

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

l --marc l
источник
0

Вы можете использовать эту библиотеку в Swift для SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Демонстрация SQLite с использованием 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 и являются вашими параметрами в массиве. Все эти термины разделены запятыми в вашем списке аргументов SQL. Вы можете ввести строки, целые числа, даты и большие двоичные объекты сразу после оператора продолжения, поскольку все эти термины считаются параметрами для продолжения. Массив вариативных аргументов просто упрощает ввод всего вашего продолжения в один вызов executeStatement или getRecordsForQuery. Если у вас нет параметров, не вводите ничего после вашего SQL.

Массив результатов - это массив словаря, где «ключ» - это имя столбца вашей таблицы, а «значение» - это ваши данные, полученные из SQLite. Вы можете легко выполнить итерацию по этому массиву с помощью цикла for, либо распечатать его напрямую, либо назначить эти элементы Dictionary для пользовательских классов объектов данных, которые вы используете в своих контроллерах представления для потребления модели.

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

SQLDataAccess будет хранить текстовые, двойные, float, blob, Date, целые и длинные длинные целые числа. Для BLOB-объектов вы можете хранить двоичные, varbinary, blob-файлы.

Для текста вы можете хранить char, character, clob, национальный переменный символ, собственный символ, nchar, nvarchar, varchar, вариант, изменяющийся символ, текст.

Для дат вы можете хранить дату и время, время, метку времени, дату.

Для целых чисел вы можете хранить bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Для чисел типа Double вы можете хранить десятичные числа, числа с двойной точностью, числа с плавающей запятой, числа, вещественные числа, числа с двойной точностью. Double имеет наибольшую точность.

Вы даже можете хранить значения Null типа Null.

В ViewController.swift сделан более сложный пример, показывающий, как вставить словарь как «Blob». Кроме того, SQLDataAccess понимает собственный Swift Date (), поэтому вы можете вставлять эти объекты без преобразования, он преобразует их в текст и сохраняет их, а при извлечении преобразует их обратно из текста в Date.

Конечно, настоящая сила SQLite - это возможность транзакций. Здесь вы можете буквально поставить в очередь 400 операторов SQL с параметрами и вставить их все сразу, что очень эффективно, так как это очень быстро. ViewController.swift также показывает вам пример того, как это сделать. Все, что вы на самом деле делаете, это создаете массив словарей под названием sqlAndParams, в этом массиве вы храните словари с двумя ключами «SQL» для оператора продолжения строки или запроса и «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 могут быть выполнены с помощью простой строки для запроса 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 с помощью swift, используя также однотонный класс.

Обратитесь

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Метод создания базы данных

func methodToCreateDatabase() -> NSURL?{} 

Метод вставки, обновления и удаления данных

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

Метод выбора данных

func methodToSelectData(strQuery : String) -> NSMutableArray{}
Хасья
источник