Acceder a una base de datos SQLite en Swift

103

Estoy buscando una forma de acceder a una base de datos SQLite en mi aplicación con código Swift.

Sé que puedo usar un SQLite Wrapper en Objective C y usar el encabezado de puente, pero prefiero poder hacer este proyecto por completo en Swift. ¿Hay alguna manera de hacer esto? Si es así, ¿alguien puede señalarme una referencia que muestre cómo enviar una consulta, recuperar filas, etc.?

Jase
fuente
1
¿Dónde debo poner mi archivo de base de datos?
C.Feliana
1
@ C.Feliana - El directorio de soporte de la aplicación es un gran lugar, por ejemplo let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Respuestas:

143

Si bien probablemente debería usar uno de los muchos contenedores de SQLite, si quisiera saber cómo llamar a la biblioteca de SQLite usted mismo, debería:

  1. Configure su proyecto Swift para manejar llamadas SQLite C. Si usa Xcode 9 o posterior, simplemente puede hacer:

    import SQLite3
  2. Crear / abrir base de datos.

    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
    }

    Tenga en cuenta que sé que parece extraño cerrar la base de datos cuando no se abre, pero la sqlite3_open documentación deja explícito que debemos hacerlo para evitar fugas de memoria:

    Tanto si se produce un error al abrirlo como si no, los recursos asociados con el identificador de conexión de la base de datos deben liberarse pasándolo a sqlite3_close()cuando ya no sea necesario.

  3. Úselo sqlite3_execpara realizar SQL (por ejemplo, crear una tabla).

    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. Úselo sqlite3_prepare_v2para preparar SQL con un ?marcador de posición al que vincularemos el valor.

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

    Tenga en cuenta que utiliza la SQLITE_TRANSIENTconstante que se puede implementar de la siguiente manera:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Restablezca SQL para insertar otro valor. En este ejemplo, insertaré un NULLvalor:

    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. Finalice la declaración preparada para recuperar la memoria asociada con esa declaración preparada:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Prepare una nueva declaración para seleccionar valores de la tabla y recorra la recuperación de los valores:

    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. Cerrar base de datos:

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

Para Swift 2 y versiones anteriores de Xcode, consulte las revisiones anteriores de esta respuesta .

Robar
fuente
1
Para aquellos que tuvieron algunos problemas en el paso 1, considere esto: Cree un encabezado de puente en su proyecto Xcode (por ejemplo, BridgingHeader.h); Este archivo de encabezado puede tener solo líneas que importan encabezados de Objective-C / C para hacer un puente a Swift (por ejemplo, #include <sqlite3.h>); En "Configuración de compilación", busque "Encabezado puente Objective-C" (puede usar la barra de búsqueda) y escriba "BridgingHeader.h" (si recibe un mensaje de error como "No se pudo importar el encabezado Objective-C", intente "proyecto- nombre / BridgingHeader.h "); Vaya a "Build Phases", "Link Binary With Libraries" y agregue libsqlite3.0.dylib o libsqlite3.0.tbd en XCode 7
Jorg B Jorge
¿Sería mejor anidar el if (... == SQLITE_OK) para que lo siguiente no se ejecute si falla? Solo pregunto porque soy muy nuevo en esto y solo tenía curiosidad por saber si lo hizo con fines de enseñanza.
quemeful
@quemeful - Claro, pero si haces eso con muchas llamadas SQLite, terminas con un código que está profundamente anidado. Si le preocupa esto, probablemente usaría guarddeclaraciones en su lugar.
Rob
@Jorg B Jorge Hice todo, ¿también necesitas importar el encabezado puente de alguna manera? Estoy trabajando en la clase de prueba
Async-
Hola @Rob, estoy usando su contenedor sqlite en un proyecto Swift aquí. Es muy lindo, gracias. Sin embargo, no puedo hacer un recuento seleccionado (*) de la tabla con él. Sigue chocando. Si hice un recuento de selección (col_name) de tablename donde some_col = xxx, funciona. ¿Que sugieres?
gbenroscience
18

Lo mejor que puede hacer es importar la biblioteca dinámica dentro de un encabezado puente:

  1. Agregue libsqlite3.dylib a su fase de compilación "Vincular binarios con bibliotecas"
  2. Cree un "Bridging-Header.h" y agréguelo #import <sqlite3.h>al principio
  3. establezca "Bridging-Header.h" para la configuración "Objective-C Bridging Header" en Build Settings en "Swift Compiler - Code Generation"

A continuación, podrá acceder a todos los métodos c como sqlite3_opendesde su código swift.

Sin embargo, es posible que desee utilizar FMDB e importarlo a través del encabezado de puente, ya que es un contenedor más orientado a objetos de sqlite. Tratar con punteros y estructuras de C será engorroso en Swift.

dibujar
fuente
Tuve que agregar esto en la configuración de compilación del proyecto y no en la configuración de compilación de Target para que Xcode encuentre el encabezado de puente.
rob5408
3
también todos y su padre ahora han creado un contenedor Swift .. ver más abajo
quemeful
1
Lamentablemente, ninguno de ellos es demasiado maduro, por lo que si usa alguno de estos nuevos envoltorios, tenga cuidado. Por ejemplo, en el momento de escribir este artículo, eché un vistazo a cuatro de ellos, y tres manejaron las fechas incorrectamente y el cuarto no los manejó en absoluto.
Rob
@Rob ¿Ha mirado github.com/stephencelis/SQLite.swift#readme ? Información sobre la configuración para usar con NSDate aquí: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis
@stephencelis Oye, eso es mejor que la mayoría de ellos, porque al menos especificas la zona horaria, pero todavía tengo problemas con eso NSDateFormatter. Pero mi intención fue menos criticar este aspecto particular de estas implementaciones particulares que sugerir que es indicativo de un problema más amplio, que estas no tienen los años de refinamiento que tienen soluciones como FMDB. Creo que la gente se apresura a descartar las soluciones probadas de Objective-C en favor de implementaciones Swift menos maduras (TFHpple vs NDHpple son otro buen ejemplo).
Rob
11

Yo también estaba buscando alguna forma de interactuar con SQLite de la misma manera que estaba acostumbrado a hacerlo anteriormente en Objective-C. Es cierto que, debido a la compatibilidad con C, acabo de usar la API C directa.

Como actualmente no existe un contenedor para SQLite en Swift y el código SQLiteDB mencionado anteriormente va un poco más alto y asume cierto uso, decidí crear un contenedor y familiarizarme un poco con Swift en el proceso. Puede encontrarlo aquí: https://github.com/chrismsimpson/SwiftSQLite .

var db = SQLiteDatabase();
db.open("/path/to/database.sqlite");

var statement = SQLiteStatement(database: db);

if ( statement.prepare("SELECT * FROM tableName WHERE Id = ?") != .Ok )
{
    /* handle error */
}

statement.bindInt(1, value: 123);

if ( statement.step() == .Row )
{
    /* do something with statement */
    var id:Int = statement.getIntAt(0)
    var stringValue:String? = statement.getStringAt(1)
    var boolValue:Bool = statement.getBoolAt(2)
    var dateValue:NSDate? = statement.getDateAt(3)
}

statement.finalizeStatement(); /* not called finalize() due to destructor/language keyword */
Chris Simpson
fuente
5

He creado una elegante biblioteca SQLite escrita completamente en Swift llamada SwiftData .

Algunas de sus características son:

  • Enlazar objetos convenientemente a la cadena de SQL
  • Soporte para transacciones y puntos de rescate
  • Manejo de errores en línea
  • Totalmente seguro para subprocesos por defecto

Proporciona una manera fácil de ejecutar 'cambios' (por ejemplo, INSERT, UPDATE, DELETE, etc.):

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
}

y 'consultas' (por ejemplo, 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)")
        }
    }
}

¡Junto con muchas más funciones!

Puedes verlo aquí

Ryanfowler
fuente
Desafortunadamente, su lib es solo para iOS. : - /
BadmintonCat
3

Otro contenedor de SQLite para Swift 2 y Swift 3: http://github.com/groue/GRDB.swift

caracteristicas:

  • Una API que resultará familiar a los usuarios de ccgus / fmdb

  • Una API de SQLite de bajo nivel que aprovecha la biblioteca estándar de Swift

  • Una interfaz de consulta bastante rápida para desarrolladores alérgicos a SQL

  • Soporte para el modo SQLite WAL y acceso concurrente a la base de datos para un rendimiento adicional

  • Una clase de registro que envuelve conjuntos de resultados, se come sus consultas SQL personalizadas para el desayuno y proporciona operaciones CRUD básicas

  • Libertad de tipo Swift: elija el tipo Swift correcto que se adapte a sus datos. Use Int64 cuando sea necesario, o quédese con el conveniente Int. Almacene y lea NSDate o NSDateComponents. Declare enumeraciones Swift para tipos de datos discretos. Defina sus propios tipos de bases de datos convertibles.

  • Migraciones de bases de datos

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

Gwendal Roué
fuente
¡GRDB es uno de los frameworks mejor documentados, soportados y mantenidos en Github!
Klaas
3

AppDelegate.swift

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

    print(DBpath)

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

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

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

Database.swift

import UIKit

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

    if (FileManager.default.fileExists(atPath: DBpath))
    {
        return DBpath as NSString
    }
    return DBpath as NSString
}

func ExecuteQuery(_ str:String) -> Bool
{
    var result:Bool=false
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if (sqlite3_open(DBpath, &db)==SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            if (sqlite3_step(stmt) == SQLITE_DONE)
            {
                result=true
            } 
        }
        sqlite3_finalize(stmt)
    }
    sqlite3_close(db)

    return result
}

func SelectQuery(_ str:String) -> Array<Dictionary<String,String>>
{
    var result:Array<Dictionary<String,String>>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                var i:Int32=0
                let icount:Int32=sqlite3_column_count(stmt)

                var dict=Dictionary<String, String>()

                while i < icount
                {
                    let strF=sqlite3_column_name(stmt, i)
                    let strV = sqlite3_column_text(stmt, i)

                    let rFiled:String=String(cString: strF!)
                    let rValue:String=String(cString: strV!)
                    //let rValue=String(cString: UnsafePointer<Int8>(strV!))

                    dict[rFiled] = rValue

                    i += 1
                }
                result.insert(dict, at: result.count)
            }
        sqlite3_finalize(stmt)
        }

    sqlite3_close(db)
    }
    return result
}

func AllSelectQuery(_ str:String) -> Array<Model>
{
    var result:Array<Model>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                let mod=Model()

                mod.id=String(cString: sqlite3_column_text(stmt, 0))
                mod.image=String(cString: sqlite3_column_text(stmt, 1))
                mod.name=String(cString: sqlite3_column_text(stmt, 2))
                mod.foodtype=String(cString: sqlite3_column_text(stmt, 3))
                mod.vegtype=String(cString: sqlite3_column_text(stmt, 4))
                mod.details=String(cString: sqlite3_column_text(stmt, 5))

                result.insert(mod, at: result.count)
            }
            sqlite3_finalize(stmt)
        }
        sqlite3_close(db)
    }
    return result
}

}

Model.swift

import UIKit


class Model: NSObject
{
var uid:Int = 0
var id:String = ""
var image:String = ""
var name:String = ""
var foodtype:String = ""
var vegtype:String = ""
var details:String = ""
var mealtype:String = ""
var date:String = ""
}

Base de datos de acceso:

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

Fuego de consulta de base de datos:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
fuente
este qyery no funciona. ¿Por qué hay == en lugar de solo uno =?
ArgaPK
1

Esta es, con mucho, la mejor biblioteca SQLite que he usado en Swift: https://github.com/stephencelis/SQLite.swift

Mira los ejemplos de código. Mucho más limpio que la API de C:

import SQLite

let db = try Connection("path/to/db.sqlite3")

let users = Table("users")
let id = Expression<Int64>("id")
let name = Expression<String?>("name")
let email = Expression<String>("email")

try db.run(users.create { t in
    t.column(id, primaryKey: true)
    t.column(name)
    t.column(email, unique: true)
})
// CREATE TABLE "users" (
//     "id" INTEGER PRIMARY KEY NOT NULL,
//     "name" TEXT,
//     "email" TEXT NOT NULL UNIQUE
// )

let insert = users.insert(name <- "Alice", email <- "[email protected]")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', '[email protected]')

for user in try db.prepare(users) {
    print("id: \(user[id]), name: \(user[name]), email: \(user[email])")
    // id: 1, name: Optional("Alice"), email: [email protected]
}
// SELECT * FROM "users"

let alice = users.filter(id == rowid)

try db.run(alice.update(email <- email.replace("mac.com", with: "me.com")))
// UPDATE "users" SET "email" = replace("email", 'mac.com', 'me.com')
// WHERE ("id" = 1)

try db.run(alice.delete())
// DELETE FROM "users" WHERE ("id" = 1)

try db.scalar(users.count) // 0
// SELECT count(*) FROM "users"

La documentación también dice que "SQLite.swift también funciona como un contenedor ligero y compatible con Swift sobre la API C", y sigue con algunos ejemplos de eso.

Andrew Koster
fuente
0

He escrito una biblioteca contenedora SQLite3 escrita en Swift .

Este es en realidad un contenedor de muy alto nivel con una API muy simple, pero de todos modos, tiene un código inter-op de C de bajo nivel, y publico aquí una parte (simplificada) para mostrar el inter-op de 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
    }

Si desea un código fuente completo de este contenedor de bajo nivel, consulte estos archivos.

eonil
fuente
0

Configure su proyecto Swift para manejar llamadas SQLite C:

Cree un archivo de encabezado puente para el proyecto. Consulte la sección Importación de Objective-C en Swift de Uso de Swift con Cocoa y Objective-C. Este encabezado puente debe importar sqlite3.h:

Agregue libsqlite3.0.dylib a su proyecto. Consulte la documentación de Apple sobre cómo agregar una biblioteca / marco a su proyecto.

y usó el siguiente código

    func executeQuery(query: NSString ) -> Int
    {
        if  sqlite3_open(databasePath! as String, &database) != SQLITE_OK
        {
            println("Databse is not open")
            return 0
        }
        else
        {
            query.stringByReplacingOccurrencesOfString("null", withString: "")
            var cStatement:COpaquePointer = nil
            var executeSql = query as NSString
            var lastId : Int?
            var sqlStatement = executeSql.cStringUsingEncoding(NSUTF8StringEncoding)
            sqlite3_prepare_v2(database, sqlStatement, -1, &cStatement, nil)
            var execute = sqlite3_step(cStatement)
            println("\(execute)")
            if execute == SQLITE_DONE
            {
                lastId = Int(sqlite3_last_insert_rowid(database))
            }
            else
            {
                println("Error in Run Statement :- \(sqlite3_errmsg16(database))")
            }
            sqlite3_finalize(cStatement)
            return lastId!
        }
    }
    func ViewAllData(query: NSString, error: NSError) -> NSArray
    {
        var cStatement = COpaquePointer()
        var result : AnyObject = NSNull()
        var thisArray : NSMutableArray = NSMutableArray(capacity: 4)
        cStatement = prepare(query)
        if cStatement != nil
        {
            while sqlite3_step(cStatement) == SQLITE_ROW
            {
                result = NSNull()
                var thisDict : NSMutableDictionary = NSMutableDictionary(capacity: 4)
                for var i = 0 ; i < Int(sqlite3_column_count(cStatement)) ; i++
                {
                    if sqlite3_column_type(cStatement, Int32(i)) == 0
                    {
                        continue
                    }
                    if sqlite3_column_decltype(cStatement, Int32(i)) != nil && strcasecmp(sqlite3_column_decltype(cStatement, Int32(i)), "Boolean") == 0
                    {
                        var temp = sqlite3_column_int(cStatement, Int32(i))
                        if temp == 0
                        {
                            result = NSNumber(bool : false)
                        }
                        else
                        {
                            result = NSNumber(bool : true)
                        }
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_INTEGER
                    {
                        var temp = sqlite3_column_int(cStatement,Int32(i))
                        result = NSNumber(int : temp)
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_FLOAT
                    {
                        var temp = sqlite3_column_double(cStatement,Int32(i))
                        result = NSNumber(double: temp)
                    }
                    else
                    {
                        if sqlite3_column_text(cStatement, Int32(i)) != nil
                        {
                            var temp = sqlite3_column_text(cStatement,Int32(i))
                            result = String.fromCString(UnsafePointer<CChar>(temp))!
                            
                            var keyString = sqlite3_column_name(cStatement,Int32(i))
                            thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                        }
                        result = NSNull()

                    }
                    if result as! NSObject != NSNull()
                    {
                        var keyString = sqlite3_column_name(cStatement,Int32(i))
                        thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                    }
                }
                thisArray.addObject(NSMutableDictionary(dictionary: thisDict))
            }
            sqlite3_finalize(cStatement)
        }
        return thisArray
    }
    func prepare(sql : NSString) -> COpaquePointer
    {
        var cStatement:COpaquePointer = nil
        sqlite3_open(databasePath! as String, &database)
        var utfSql = sql.UTF8String
        if sqlite3_prepare(database, utfSql, -1, &cStatement, nil) == 0
        {
            sqlite3_close(database)
            return cStatement
        }
        else
        {
            sqlite3_close(database)
            return nil
        }
    }
}
chirag shah
fuente
0

A veces, es suficiente una versión rápida del enfoque "SQLite en 5 minutos o menos" que se muestra en sqlite.org . Los "5 minutos o menos" usos de aproximación sqlite3_exec(), que es un envoltorio de comodidad para sqlite3_prepare(), sqlite3_step(), sqlite3_column(), y sqlite3_finalize().

Swift 2.2 puede admitir directamente el sqlite3_exec() callbackpuntero de función como un procedimiento global que no es de instancia funco un cierre literal sin captura {}.

Legible typealias

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

Enfoque de devolución de llamada

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
}

Enfoque de cierre

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
}

Para preparar un proyecto Xcode para llamar a una biblioteca C como SQLite, es necesario (1) agregar un archivo Bridging-Header.h con encabezados C de referencia como #import "sqlite3.h", (2) agregar Bridging-Header.h a Objective-C Bridging Header en el proyecto y (3) agregar libsqlite3.tbda la configuración de destino de Link Binary With Library .

El sqlite.org 's 'SQLite en 5 minutos o menos' ejemplo se implementa en un proyecto Swift Xcode7 aquí .

l --marc l
fuente
0

Puede usar esta biblioteca en Swift para SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Demostración de SQLite usando Swift con la clase SQLDataAccess escrita en Swift

Agregar a su proyecto

Solo necesita tres archivos para agregar a su proyecto * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header debe establecerse en su proyecto de Xcode 'Objective-C Bridging Header' en 'Swift Compiler - General'

Ejemplos de uso

Simplemente siga el código en ViewController.swift para ver cómo escribir SQL simple con SQLDataAccess.swift Primero debe abrir la base de datos SQLite con la que está tratando

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

Si openConnection tuvo éxito, ahora puede hacer una inserción simple en 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)")
    }

¡Mira lo simple que fue!

El primer término en db.executeStatement es su SQL como String, todos los términos que siguen son una lista de argumentos variadic de tipo Any, y son sus parámetros en un Array. Todos estos términos están separados por comas en su lista de argumentos SQL. Puede ingresar cadenas, enteros, fechas y blobs justo después de la declaración de la secuela, ya que todos estos términos se consideran parámetros de la secuela. La matriz de argumentos variadic simplemente hace que sea conveniente ingresar toda su secuela en una sola llamada executeStatement o getRecordsForQuery. Si no tiene ningún parámetro, no ingrese nada después de su SQL.

La matriz de resultados es una matriz de diccionarios donde la 'clave' es el nombre de la columna de sus tablas y el 'valor' son sus datos obtenidos de SQLite. Puede iterar fácilmente a través de esta matriz con un bucle for o imprimirlo directamente o asignar estos elementos de diccionario a clases de objetos de datos personalizados que usa en sus controladores de vista para el consumo del modelo.

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

SQLDataAccess almacenará texto, doble, flotante, blob, fecha, entero y enteros largos largos. Para Blobs, puede almacenar binary, varbinary, blob.

Para Texto, puede almacenar char, character, clob, carácter nacional variable, carácter nativo, nchar, nvarchar, varchar, variant, carácter variable, texto.

Para las fechas, puede almacenar fecha y hora, hora, marca de tiempo, fecha.

Para los enteros, puede almacenar bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Para dobles, puede almacenar decimal, precisión doble, flotante, numérico, real, doble. Double tiene la mayor precisión.

Incluso puede almacenar Nulls de tipo Null.

En ViewController.swift se hace un ejemplo más complejo que muestra cómo insertar un diccionario como un 'Blob'. Además, SQLDataAccess comprende Swift Date () nativo, por lo que puede insertar estos objetos sin convertir, y los convertirá a texto y los almacenará, y cuando los recupere, los convertirá de texto a Date.

Por supuesto, el verdadero poder de SQLite es su capacidad de transacción. Aquí puede literalmente poner en cola 400 sentencias SQL con parámetros e insertarlas todas a la vez, lo cual es realmente poderoso ya que es muy rápido. ViewController.swift también le muestra un ejemplo de cómo hacer esto. Todo lo que está haciendo realmente es crear una matriz de diccionarios llamada 'sqlAndParams', en esta matriz sus diccionarios de almacenamiento con dos claves 'SQL' para la instrucción o consulta de secuencia de cadenas, y 'PARAMS', que es solo una matriz de objetos nativos SQLite entiende para esa consulta. Cada 'sqlParams', que es un diccionario individual de consultas posteriores más parámetros, se almacena en la matriz 'sqlAndParams'. Una vez que haya creado esta matriz, simplemente llame.

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

Además, todos los métodos executeStatement y getRecordsForQuery se pueden realizar con una cadena simple para la consulta SQL y una matriz para los parámetros que necesita la consulta.

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

También existe una versión de Objective-C y se llama el mismo SQLDataAccess, por lo que ahora puede elegir escribir su secuela en Objective-C o Swift. Además, SQLDataAccess también funcionará con SQLCipher, el código actual aún no está configurado para funcionar con él, pero es bastante fácil de hacer, y un ejemplo de cómo hacerlo se encuentra en la versión Objective-C de SQLDataAccess.

SQLDataAccess es una clase muy rápida y eficiente, y se puede usar en lugar de CoreData, que realmente solo usa SQLite como su almacén de datos subyacente sin todas las fallas de integridad de datos centrales de CoreData que vienen con CoreData.

escarcha
fuente
-1

También puede configurar SQLite fácilmente con rapidez usando la clase de tonelada única.

Referir

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Método para crear base de datos

func methodToCreateDatabase() -> NSURL?{} 

Método para insertar, actualizar y eliminar datos

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

Método para seleccionar datos

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