Accesso a un database SQLite in Swift


103

Sto cercando un modo per accedere a un database SQLite nella mia app con il codice Swift.

So di poter utilizzare un wrapper SQLite in Objective C e utilizzare l'intestazione di bridging, ma preferirei essere in grado di fare questo progetto interamente in Swift. C'è un modo per farlo, in caso affermativo, qualcuno può indicarmi un riferimento che mostra come inviare una query, recuperare righe, ecc.?



1
dove devo mettere il mio file di database?
C. Feliana

1
@ C.Feliana - La directory di supporto dell'applicazione è un ottimo posto, ad es let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Risposte:


143

Anche se probabilmente dovresti usare uno dei tanti wrapper SQLite, se volessi sapere come chiamare tu stesso la libreria SQLite, dovresti:

  1. Configura il tuo progetto Swift per gestire le chiamate SQLite C. Se usi Xcode 9 o successivo, puoi semplicemente fare:

    import SQLite3
  2. Crea / apri database.

    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
    }

    Nota, so che sembra strano chiudere il database in caso di mancata apertura, ma la sqlite3_open documentazione rende esplicito che dobbiamo farlo per evitare perdite di memoria:

    Indipendentemente dal fatto che si verifichi o meno un errore all'apertura, le risorse associate all'handle di connessione al database devono essere rilasciate passandolo a sqlite3_close()quando non è più necessario.

  3. Utilizzare sqlite3_execper eseguire SQL (ad esempio creare tabella).

    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. Utilizzare sqlite3_prepare_v2per preparare SQL con ?segnaposto a cui legheremo il valore.

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

    Nota che utilizza la SQLITE_TRANSIENTcostante che può essere implementata come segue:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Reimposta SQL per inserire un altro valore. In questo esempio, inserirò un NULLvalore:

    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. Finalizza l'istruzione preparata per recuperare la memoria associata a tale istruzione preparata:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Preparare una nuova istruzione per selezionare i valori dalla tabella e scorrere il recupero dei valori:

    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. Chiudi database:

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

Per Swift 2 e le versioni precedenti di Xcode, vedere le revisioni precedenti di questa risposta .


1
Per coloro che hanno riscontrato alcuni problemi al passaggio 1, considera questo: Crea un'intestazione di bridging nel tuo progetto Xcode (ad esempio BridgingHeader.h); Questo file di intestazione può contenere solo righe che importano intestazioni Objective-C / C per il bridging in Swift (ad esempio #include <sqlite3.h>); Su "Build Settings" trova "Objective-C Bridging Header" (Puoi usare la barra di ricerca) e digita "BridgingHeader.h" (se ricevi un messaggio di errore come "Impossibile importare Objective-C Header", prova "project- nome / BridgingHeader.h "); Vai a "Build Phases", "Link Binary With Libraries" e aggiungi libsqlite3.0.dylib o libsqlite3.0.tbd in XCode 7
Jorg B Jorge

Sarebbe meglio annidare if (... == SQLITE_OK) in modo che quanto segue non venga eseguito se fallisce. Lo chiedo semplicemente perché sono molto nuovo in questo ed ero solo curioso se lo facessi per scopi didattici.
quemeful

@quemeful - Certo, ma se lo fai con molte chiamate SQLite, ti ritroverai con un codice che è davvero profondamente nidificato. Se sei preoccupato per questo, probabilmente userò guardinvece le dichiarazioni.
Rob

@Jorg B Jorge Ho fatto tutto, devi anche importare in qualche modo l'intestazione a ponte? Sto lavorando nella classe di test
Async

Ciao @ Rob, sto usando il tuo wrapper sqlite in un progetto Swift qui. È davvero carino, grazie. Tuttavia, non posso eseguire un conteggio selezionato (*) dalla tabella con esso. Continua a bloccarsi. Se ho eseguito un conteggio selezionato (col_name) da tablename dove some_col = xxx, funziona. Cosa suggerisci?
gbenroscience

18

Il meglio che puoi fare è importare la libreria dinamica all'interno di un'intestazione di bridging:

  1. Aggiungi libsqlite3.dylib alla fase di compilazione "Collega binario con librerie"
  2. Crea un "Bridging-Header.h" e aggiungilo #import <sqlite3.h>all'inizio
  3. imposta "Bridging-Header.h" per l'impostazione "Objective-C Bridging Header" in Impostazioni build in "Swift Compiler - Generazione codice"

Sarai quindi in grado di accedere a tutti i metodi c come sqlite3_open dal tuo codice swift.

Tuttavia, potresti semplicemente voler usare FMDB e importarlo attraverso l'intestazione di bridging in quanto è un wrapper più orientato agli oggetti di sqlite. Gestire i puntatori e le strutture in C sarà complicato in Swift.


Ho dovuto aggiungerlo nelle impostazioni di build del progetto e non nelle impostazioni di build di destinazione in modo che Xcode trovasse l'intestazione del ponte.
rob5408

3
anche tutti e il loro padre hanno ora creato un involucro Swift .. vedi sotto
simpatico

1
Purtroppo, nessuno di loro è terribilmente maturo, quindi se usi uno di questi nuovi involucri, fai attenzione. Ad esempio, nel momento in cui scrivo, ho dato un'occhiata a quattro di loro e tre hanno gestito le date in modo errato e il quarto non le ha gestite affatto.
Rob il

@Rob Hai guardato github.com/stephencelis/SQLite.swift#readme ? Informazioni sulla configurazione da utilizzare con NSDate qui: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis

@stephencelis Ehi, è meglio della maggior parte di loro, perché almeno specifichi il fuso orario, ma ho ancora problemi con quello NSDateFormatter. Ma il mio intento era meno quello di criticare questo particolare aspetto di queste particolari implementazioni che di suggerire che è indicativo di una questione più ampia, che queste non hanno gli anni di perfezionamento che hanno soluzioni come FMDB. Penso che le persone siano troppo veloci nello scartare soluzioni Objective-C comprovate a favore di implementazioni Swift meno mature (TFHpple vs NDHpple sono un altro buon esempio).
Rob il

11

Anch'io stavo cercando un modo per interagire con SQLite nello stesso modo in cui ero abituato a fare in precedenza in Objective-C. Certo, a causa della compatibilità C, ho usato solo l'API C diretta.

Poiché attualmente non esiste alcun wrapper per SQLite in Swift e il codice SQLiteDB menzionato sopra è di livello leggermente superiore e presuppone un determinato utilizzo, ho deciso di creare un wrapper e di acquisire familiarità con Swift nel processo. Puoi trovarlo qui: 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

Ho creato un'elegante libreria SQLite scritta completamente in Swift chiamata SwiftData .

Alcune delle sue caratteristiche sono:

  • Associa comodamente gli oggetti alla stringa di SQL
  • Supporto per transazioni e punti di salvataggio
  • Gestione degli errori in linea
  • Completamente thread-safe per impostazione predefinita

Fornisce un modo semplice per eseguire 'modifiche' (ad esempio INSERT, UPDATE, DELETE, ecc.):

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
}

e "query" (ad es. SELEZIONA):

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

Insieme a molte altre funzionalità!

Puoi verificarlo qui


Sfortunatamente la tua libreria è solo iOS! : - /
Badminton

3

Ancora un altro wrapper SQLite per Swift 2 e Swift 3: http://github.com/groue/GRDB.swift

Caratteristiche:

  • Un'API che sembrerà familiare agli utenti di ccgus / fmdb

  • Un'API SQLite di basso livello che sfrutta la libreria standard di Swift

  • Un'interfaccia di query piuttosto rapida per sviluppatori allergici a SQL

  • Supporto per la modalità SQLite WAL e accesso simultaneo al database per prestazioni extra

  • Una classe Record che racchiude i set di risultati, mangia le query SQL personalizzate per colazione e fornisce operazioni CRUD di base

  • Libertà di tipo Swift: scegli il tipo di Swift giusto che si adatta ai tuoi dati. Usa Int64 quando necessario o mantieni il comodo Int. Archivia e leggi NSDate o NSDateComponents. Dichiara enumerazioni Swift per tipi di dati discreti. Definisci i tuoi tipi di database convertibili.

  • Migrazioni di database

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


GRDB è uno dei framework meglio documentati, supportati e mantenuti su 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 = ""
}

Database di accesso:

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

database Query fire:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")

questo qyery non funziona. perché c'è == invece di un solo =?
ArgaPK

1

Questa è di gran lunga la migliore libreria SQLite che ho usato in Swift: https://github.com/stephencelis/SQLite.swift

Guarda gli esempi di codice. Molto più pulito dell'API 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 <- "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"

La documentazione dice anche che "SQLite.swift funziona anche come wrapper leggero e intuitivo per l'API C" e segue con alcuni esempi di questo.


0

Ho scritto una libreria wrapper SQLite3 scritta in Swift .

Questo è in realtà un wrapper di livello molto alto con API molto semplice, ma comunque ha un codice interoperativo C di basso livello, e ne posto qui una parte (semplificata) per mostrare l'interoperabilità 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
    }

Se vuoi un codice sorgente completo di questo wrapper di basso livello, guarda questi file.


0

Configura il tuo progetto Swift per gestire le chiamate SQLite C:

Crea un file di intestazione di collegamento al progetto. Vedi la sezione Importazione di Objective-C in Swift di Utilizzo di Swift con Cocoa e Objective-C. Questa intestazione di bridging dovrebbe importare sqlite3.h:

Aggiungi libsqlite3.0.dylib al tuo progetto. Vedere la documentazione di Apple relativa all'aggiunta di libreria / framework al proprio progetto.

e utilizzato il codice seguente

    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

A volte, una versione Swift dell'approccio "SQLite in 5 minuti o meno" mostrato su sqlite.org è sufficiente. I "5 minuti o meno" usi approccio sqlite3_exec(), che è un wrapper convenienza per sqlite3_prepare(), sqlite3_step(), sqlite3_column(), e sqlite3_finalize().

Swift 2.2 può supportare direttamente il sqlite3_exec() callbackpuntatore a funzione come una procedura globale non di istanza funco una chiusura letterale non di acquisizione {}.

Leggibile typealias

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

Approccio alla richiamata

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
}

Approccio alla chiusura

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
}

Per preparare un progetto Xcode per chiamare una libreria C come SQLite, è necessario (1) aggiungere un'intestazione C di riferimento al file Bridging-Header.h come #import "sqlite3.h", (2) aggiungere Bridging-Header.h a Objective-C Bridging Header nel progetto impostazioni e (3) aggiungere libsqlite3.tbda Collega file binario con libreria impostazioni di destinazione .

Lo sqlite.org s' 'SQLite in 5 minuti o meno' esempio è implementata in un progetto di Swift Xcode7 qui .


0

Puoi utilizzare questa libreria in Swift per SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

SQLite Demo utilizzando Swift con la classe SQLDataAccess scritta in Swift

Aggiunta al tuo progetto

Hai solo bisogno di tre file da aggiungere al tuo progetto * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header deve essere impostato nel tuo progetto Xcode 'Objective-C Bridging Header' sotto 'Swift Compiler - General'

Esempi di utilizzo

Segui semplicemente il codice in ViewController.swift per vedere come scrivere un semplice SQL con SQLDataAccess.swift Per prima cosa devi aprire il database SQLite con cui hai a che fare

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

Se openConnection ha avuto successo, ora puoi eseguire un semplice inserimento in 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)")
    }

Guarda com'è stato semplice!

Il primo termine in db.executeStatement è il tuo SQL come String, tutti i termini che seguono sono un elenco di argomenti variadici di tipo Any e sono i tuoi parametri in un Array. Tutti questi termini sono separati da virgole nell'elenco degli argomenti SQL. È possibile immettere stringhe, numeri interi, date e BLOB subito dopo l'istruzione sequel poiché tutti questi termini sono considerati parametri per il sequel. L'array di argomenti variadic semplifica l'inserimento di tutto il sequel in una sola chiamata executeStatement o getRecordsForQuery. Se non hai parametri, non inserire nulla dopo il tuo SQL.

L'array dei risultati è un array di Dictionary dove la "chiave" è il nome della colonna delle tabelle e il "valore" è i dati ottenuti da SQLite. È possibile iterare facilmente attraverso questo array con un ciclo for o stamparlo direttamente o assegnare questi elementi del dizionario alle classi di oggetti dati personalizzati che si utilizzano nei controller di visualizzazione per l'utilizzo del modello.

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

SQLDataAccess memorizzerà, text, double, float, blob, date, integer e long long interi. Per i BLOB è possibile archiviare binary, varbinary, blob.

Per il testo è possibile memorizzare char, carattere, clob, carattere variabile nazionale, carattere nativo, nchar, nvarchar, varchar, variante, carattere variabile, testo.

Per le date è possibile memorizzare datetime, time, timestamp, date.

Per i numeri interi è possibile memorizzare bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Per i doppi puoi memorizzare decimale, doppia precisione, float, numerico, reale, doppio. Double ha la massima precisione.

Puoi anche memorizzare Null di tipo Null.

In ViewController.swift viene eseguito un esempio più complesso che mostra come inserire un dizionario come "Blob". Inoltre SQLDataAccess comprende Swift Date () nativo, quindi puoi inserire questi oggetti senza convertirli e li convertirà in testo e li memorizzerà e, una volta recuperati, li convertirà di nuovo da testo a Date.

Ovviamente il vero potere di SQLite è la sua capacità di transazione. Qui puoi letteralmente mettere in coda 400 istruzioni SQL con parametri e inserirle tutte in una volta, il che è davvero potente poiché è così veloce. ViewController.swift mostra anche un esempio di come farlo. Tutto quello che stai facendo è creare un array di dizionari chiamato 'sqlAndParams', in questo array i tuoi dizionari di archiviazione con due chiavi 'SQL' per l'istruzione o query sequel String e 'PARAMS' che è solo un array di oggetti nativi SQLite capisce per quella query. Ogni 'sqlParams', che è un dizionario individuale di query sequel più parametri, viene quindi memorizzato nell'array 'sqlAndParams'. Dopo aver creato questo array, devi solo chiamare.

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

Inoltre, tutti i metodi executeStatement e getRecordsForQuery possono essere eseguiti con una semplice query String per SQL e un array per i parametri richiesti dalla query.

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

Esiste anche una versione Objective-C e si chiama lo stesso SQLDataAccess, quindi ora puoi scegliere di scrivere il tuo sequel in Objective-C o Swift. Inoltre SQLDataAccess funzionerà anche con SQLCipher, il codice attuale non è ancora configurato per funzionare con esso, ma è abbastanza facile da fare, e un esempio di come farlo è in realtà nella versione Objective-C di SQLDataAccess.

SQLDataAccess è una classe molto veloce ed efficiente e può essere utilizzata al posto di CoreData che in realtà utilizza solo SQLite come archivio dati sottostante senza tutti gli arresti anomali di integrità dei dati core CoreData che vengono con CoreData.


-1

Puoi facilmente configurare SQLite con swift usando anche la classe a tonnellata.

Fare riferimento

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Metodo per creare database

func methodToCreateDatabase() -> NSURL?{} 

Metodo per inserire, aggiornare e cancellare i dati

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

Metodo per selezionare i dati

func methodToSelectData(strQuery : String) -> NSMutableArray{}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.