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

84

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

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

  • 1
    github.com/stephencelis/SQLite.swift
  • 0
    где я должен положить файл моей базы данных?
Теги:

11 ответов

109
Лучший ответ

В то время как вы, вероятно, должны использовать одну из многих оболочек SQLite (я предпочитаю FMDB, если вы хотите знать, как вызовите библиотеку SQLite самостоятельно, вы должны:

  • Настройте свой проект Swift для обработки вызовов SQLite C. Если вы используете Xcode 9, вы можете просто:

    import SQLite3
    

    В более ранних версиях Xcode вы можете:

    • Создайте в проект заголовок заголовочного файла. См. Раздел Импорт Objective-C в Swift раздела Использование Swift с Cocoa и Objective-C. Этот заголовок моста должен импортировать sqlite3.h:

      #import <sqlite3.h>
      
    • Добавьте libsqlite3.tbd (или для более старых версий, libsqlite3.dylib) в ваш проект.

  • Создать/открыть базу данных.

    let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent("test.sqlite")
    
    // open database
    
    var db: OpaquePointer?
    if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
        print("error opening database")
    }
    
  • Используйте sqlite3_exec для выполнения SQL (например, create table).

    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)")
    }
    
  • Используйте sqlite3_prepare_v2 для подготовки SQL с помощью ? placeholder, с которым мы свяжем значение.

    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)
    
  • Reset 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)")
    }
    
  • Завершить подготовленный оператор для восстановления памяти, связанной с этим подготовленным оператором:

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

    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
    
  • Закрыть базу данных:

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

Для Swift 2, см. предыдущую версию этого ответа.

  • 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
  • 4
    Да благословит вас Господь, человек ...
Показать ещё 3 комментария
15

Лучшее, что вы можете сделать, это импортировать динамическую библиотеку внутри заголовка моста:

  • Добавьте libsqlite3.dylib в фазу сборки Link Binary With Libraries.
  • Создайте "Bridging-Header.h" и добавьте #import <sqlite3.h> в начало
  • установите "Bridging-Header.h" для параметра "Objective-C Bridging Header" в настройках сборки в разделе "Swift Compiler - Code Generation"

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

Однако вы можете просто использовать FMDB и импортировать его через заголовок моста, поскольку это более объектно-ориентированная оболочка sqlite. Работа с C-указателями и структурами будет громоздкой в ​​Swift.

  • 0
    Мне пришлось добавить это в настройках сборки Project, а не в настройках сборки Target, чтобы Xcode мог найти заголовок моста.
  • 3
    также каждый и их отец теперь создали оболочку Swift .. см. ниже
Показать ещё 4 комментария
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 */
4

Я создал элегантную библиотеку 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)")
        }
    }
}

Наряду со многими другими функциями!

Вы можете проверить это здесь

  • 0
    К сожалению, ваша библиотека доступна только для iOS! : - /
2

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")
  • 0
    этот вопрос не работает. почему == вместо одного =?
2

Еще одна оболочка SQLite для Swift 2 и Swift 3: http://github.com/groue/GRDB.swift

Особенности:

  • API, который будет выглядеть знакомым пользователям ccgus/fmdb

  • Низкоуровневый SQLite API, который использует стандартную библиотеку Swift

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

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

  • Класс записи, который обертывает результирующие наборы, ест ваши пользовательские SQL-запросы на завтрак и предоставляет основные операции CRUD

  • Свободная свобода: выберите правильный тип Swift, соответствующий вашим данным. Используйте Int64, если необходимо, или придерживайтесь удобного Int. Храните и читайте NSDate или NSDateComponents. Объявить Swift перечисления для дискретных типов данных. Определите свои собственные типы конвертируемых баз данных.

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

  • Скорость: https://github.com/groue/GRDB.swift/wiki/Performance

  • 0
    GRDB - одна из лучших документированных, поддерживаемых и поддерживаемых сред на Github!
0

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

SQLiteDemo

SQLite Demo с использованием Swift с классом SQLDataAccess, написанным в Swift

Добавление в проект

Вам нужно всего три файла для добавления в ваш проект * SQLDataAccess.swift * DataConstants.swift * Мосты-Header.h Bridging-Header должен быть установлен в вашем проекте Xcode 'Objective-C Bridging Header' в разделе 'Swift Compiler - General'

Примеры использования

Просто следуйте коду в ViewController.swift, чтобы узнать, как писать простой SQL с помощью SQLDataAccess.swift Сначала вам нужно открыть базу данных SQLite, которая имеет дело с

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

Если openConnection удалось, теперь вы можете сделать простую вставку в таблицу AppInfo

```swift
//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 или распечатать его напрямую или присвоить этим элементам словаря пользовательским классам данных, которые вы используете в контроллерах View для потребления модели.

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


```

SQLDataAccess будет хранить, текст, double, float, blob, Date, integer и long long integers. Для Blobs вы можете хранить двоичные, varbinary, blob.

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

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

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

Для парного разряда вы можете хранить десятичную, двойную точность, float, numeric, real, double. Двойник имеет наибольшую точность.

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

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

Конечно, реальная сила SQLite - это способность транзакции. Здесь вы можете буквально поставить в очередь 400 операторов SQL с параметрами и вставлять их все сразу, что очень эффективно, так как это так быстро. ViewController.swift также показывает вам пример того, как это сделать. Все, что вы действительно делаете, это создание массива словарей под названием "sqlAndParams", в этом массиве хранятся словари с двумя ключами "SQL" для оператора или запроса синтаксиса String и "PARAMS", который представляет собой просто массив собственных объектов, которые SQLite понимает для этого запроса. Каждый "sqlParams", который является отдельным Словарем сиквельных запросов плюс параметры, затем сохраняется в массиве "sqlAndParams". Когда вы создали этот массив, вы просто вызываете.

```swift
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 и массива для параметров, необходимых для запроса.

```swift
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, настоящий код еще не настроен для работы с ним, но это довольно легко сделать, и пример того, как это сделать, на самом деле находится в версии Objective-C SQLDataAccess.

SQLDataAccess является очень быстрым и эффективным классом и может использоваться вместо CoreData, который на самом деле просто использует SQLite в качестве базовых данных хранить без всех сбоев в сбое целостности ядра CoreData, которые поставляются с CoreData.

0

Вы можете легко настроить SQLite с помощью swift с использованием одного тонного класса.

См

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Способ создания базы данных

func methodToCreateDatabase() -> NSURL?{} 

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

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

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

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

Иногда быстрая версия "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
}

Подход Closure

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 в Link Binary With Library.

sqlite.org "SQLite за 5 минут или меньше" реализован в проекте Swift Xcode7 здесь.

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

Я написал библиотеку обертки 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
    }

Если вы хотите получить полный исходный код этой низкоуровневой оболочки, см. эти файлы.

  • 1
    Ссылки битые.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню