Acessando um banco de dados SQLite em Swift

103

Estou procurando uma maneira de acessar um banco de dados SQLite em meu aplicativo com o código Swift.

Eu sei que posso usar um Wrapper SQLite em Objective C e usar o cabeçalho de ponte, mas prefiro poder fazer esse projeto inteiramente em Swift. Existe uma maneira de fazer isso, em caso afirmativo, alguém pode me indicar uma referência que mostra como enviar uma consulta, recuperar linhas, etc?

Jase
fonte
1
onde devo colocar meu arquivo de banco de dados?
C. Feliana
1
@ C.Feliana - O diretório de suporte do aplicativo é um ótimo lugar, por exemplo let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Respostas:

143

Embora você provavelmente deva usar um dos muitos wrappers SQLite, se quiser saber como chamar a biblioteca SQLite por conta própria, você deve:

  1. Configure seu projeto Swift para lidar com chamadas SQLite C. Se estiver usando o Xcode 9 ou posterior, você pode simplesmente fazer:

    import SQLite3
  2. Criar / abrir banco de dados.

    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
    }

    Note, eu sei que parece estranho fechar o banco de dados em caso de falha ao abrir, mas a sqlite3_open documentação deixa explícito que devemos fazer isso para evitar vazamento de memória:

    Independentemente de ocorrer um erro ao ser aberto ou não, os recursos associados ao identificador de conexão do banco de dados devem ser liberados, passando-o para sqlite3_close()quando não for mais necessário.

  3. Use sqlite3_execpara executar SQL (por exemplo, criar tabela).

    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. Use sqlite3_prepare_v2para preparar SQL com ?espaço reservado ao qual vincularemos o 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)")
    }

    Observe que isso usa a SQLITE_TRANSIENTconstante que pode ser implementada da seguinte forma:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Redefina o SQL para inserir outro valor. Neste exemplo, irei inserir um 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. Finalize a declaração preparada para recuperar a memória associada a essa declaração preparada:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Prepare uma nova instrução para selecionar valores da tabela e percorra a recuperação dos 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. Fechar banco de dados:

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

Para Swift 2 e versões anteriores do Xcode, consulte as revisões anteriores desta resposta .

Roubar
fonte
1
Para aqueles que tiveram alguns problemas na passagem 1, considere o seguinte: Crie um cabeçalho de ponte em seu projeto Xcode (por exemplo, BridgingHeader.h); Este arquivo de cabeçalho pode ter apenas linhas importando cabeçalhos Objective-C / C para fazer a ponte para Swift (por exemplo, #include <sqlite3.h>); Em "Build Settings", encontre "Objective-C Bridging Header" (você pode usar a barra de pesquisa) e digite "BridgingHeader.h" (se receber uma mensagem de erro como "Não foi possível importar Objective-C Header", tente "project- name / BridgingHeader.h "); Vá para "Build Phases", "Link Binary With Libraries" e adicione libsqlite3.0.dylib ou libsqlite3.0.tbd no XCode 7
Jorg B Jorge
Seria melhor aninhar o if (... == SQLITE_OK) para que o seguinte não fosse executado se falhasse. Estou apenas perguntando porque sou muito novo nisso e só estava curioso para saber se você fez isso para fins de ensino.
quemeful
@quemeful - Claro, mas se você fizer isso com muitas chamadas SQLite, você terminará com um código profundamente aninhado. Se você está preocupado com isso, provavelmente eu usaria guarddeclarações.
Rob
@Jorg B Jorge Eu fiz tudo, você também precisa importar algum cabeçalho de ponte? Estou trabalhando na aula de teste
Assíncrono-
Olá @Rob, estou usando seu wrapper sqlite em um projeto Swift aqui. É muito bom, obrigado. No entanto, não posso fazer uma contagem de seleção (*) da tabela com ele. Ele continua travando. Se eu fiz uma contagem de seleção (col_name) de tablename onde some_col = xxx, funcionou. O que você sugere?
gbenroscience
18

O melhor que você pode fazer é importar a biblioteca dinâmica dentro de um cabeçalho de ponte:

  1. Adicione libsqlite3.dylib à fase de construção "Vincular binário às bibliotecas"
  2. Crie um "Bridging-Header.h" e adicione #import <sqlite3.h>ao topo
  3. defina "Bridging-Header.h" para a configuração "Objective-C Bridging Header" em Build Settings em "Swift Compiler - Code Generation"

Você poderá então acessar todos os métodos c, como a sqlite3_openpartir do seu código swift.

No entanto, você pode apenas querer usar FMDB e importá-lo por meio do cabeçalho de ponte, pois é um wrapper mais orientado a objetos do sqlite. Lidar com ponteiros e estruturas C será complicado no Swift.

Drewag
fonte
Eu tive que adicionar isso nas configurações de compilação do projeto e não nas configurações de compilação de destino para que o Xcode encontrasse o cabeçalho de ponte.
rob5408
3
também todos e seus pais criaram um invólucro do Swift .. veja abaixo
quemeful
1
Infelizmente, nenhum deles é terrivelmente maduro, então se você usar qualquer um desses novos invólucros, tome cuidado. Por exemplo, no momento em que escrevi este artigo, eu dei uma olhada em quatro deles, e três lidaram com datas incorretamente e o quarto não lidou com nada.
Rob
@Rob Você já consultou github.com/stephencelis/SQLite.swift#readme ? Informações sobre como configurar para usar com NSDate aqui: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis
@stephencelis Ei, isso é melhor do que a maioria deles, porque pelo menos você especifica o fuso horário, mas ainda tenho problemas com isso NSDateFormatter. Mas minha intenção era menos criticar esse aspecto particular dessas implementações particulares do que sugerir que isso é indicativo de um problema mais amplo, que eles não têm os anos de refinamento que soluções como o FMDB têm. Acho que as pessoas são muito rápidas em descartar soluções comprovadas de Objective-C em favor de implementações menos maduras do Swift (TFHpple vs NDHpple são outro bom exemplo).
Rob
11

Eu também estava procurando alguma maneira de interagir com o SQLite da mesma forma que estava acostumado a fazer anteriormente em Objective-C. É certo que, por causa da compatibilidade com C, acabei de usar a API C simples.

Como nenhum wrapper existe atualmente para SQLite em Swift e o código SQLiteDB mencionado acima vai um pouco mais alto e assume certo uso, decidi criar um wrapper e me familiarizar um pouco com Swift no processo. Você pode encontrá-lo aqui: 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
fonte
5

Eu criei uma elegante biblioteca SQLite escrita completamente em Swift chamada SwiftData .

Alguns de seus recursos são:

  • Vincule objetos convenientemente à string de SQL
  • Suporte para transações e pontos de salvamento
  • Tratamento de erros inline
  • Completamente thread-safe por padrão

Ele fornece uma maneira fácil de executar 'alterações' (por exemplo, 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
}

e 'consultas' (por exemplo, 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 com muitos outros recursos!

Você pode conferir aqui

Ryanfowler
fonte
Infelizmente, sua biblioteca é apenas para iOS! : - /
BadmintonCat
3

Mais um wrapper SQLite para Swift 2 e Swift 3: http://github.com/groue/GRDB.swift

Recursos:

  • Uma API que parecerá familiar para usuários de ccgus / fmdb

  • Uma API SQLite de baixo nível que aproveita a biblioteca padrão Swift

  • Uma interface de consulta bem Swift para desenvolvedores alérgicos a SQL

  • Suporte para o modo SQLite WAL e acesso simultâneo ao banco de dados para desempenho extra

  • Uma classe Record que agrupa conjuntos de resultados, come suas consultas SQL personalizadas no café da manhã e fornece operações CRUD básicas

  • Liberdade de tipo Swift: escolha o tipo Swift certo que se adapta aos seus dados. Use Int64 quando necessário ou use o conveniente Int. Armazene e leia NSDate ou NSDateComponents. Declare enums do Swift para tipos de dados discretos. Defina seus próprios tipos conversíveis de banco de dados.

  • Migrações de banco de dados

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

Gwendal Roué
fonte
GRDB é um dos frameworks mais bem documentados, suportados e mantidos no Github!
Klaas de
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 = ""
}

Banco de dados de acesso:

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

Incêndio de consulta de banco de dados:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
fonte
este qyery não está funcionando. por que existe == em vez de apenas um =?
ArgaPK
1

Esta é de longe a melhor biblioteca SQLite que usei em Swift: https://github.com/stephencelis/SQLite.swift

Veja os exemplos de código. Muito mais limpo do que a 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 <- "[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"

A documentação também diz que "SQLite.swift também funciona como um wrapper leve e amigável ao Swift sobre a API C" e segue com alguns exemplos disso.

Andrew Koster
fonte
0

Eu escrevi uma biblioteca de invólucro SQLite3 escrita em Swift .

Na verdade, este é um wrapper de nível muito alto com API muito simples, mas de qualquer maneira, ele tem código de interoperabilidade C de baixo nível, e eu posto aqui uma parte (simplificada) dele para mostrar a interoperabilidade 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 você quiser um código-fonte completo deste wrapper de baixo nível, consulte estes arquivos.

eonil
fonte
0

Configure seu projeto Swift para lidar com chamadas SQLite C:

Crie um arquivo de cabeçalho de ponte para o projeto. Veja a seção Importando Objective-C para o Swift do Usando Swift com Cocoa e Objective-C. Este cabeçalho de ponte deve importar sqlite3.h:

Adicione o libsqlite3.0.dylib ao seu projeto. Consulte a documentação da Apple sobre como adicionar biblioteca / estrutura a um projeto.

e usado o seguinte 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
fonte
0

Às vezes, uma versão Swift da abordagem "SQLite em 5 minutos ou menos" mostrada em sqlite.org é suficiente. Os "5 minutos ou menos" usos abordagem sqlite3_exec()que é um invólucro de conveniência para sqlite3_prepare(), sqlite3_step(), sqlite3_column()e sqlite3_finalize().

O Swift 2.2 pode suportar diretamente o sqlite3_exec() callbackponteiro de função como um procedimento global, não de instância funcou um fechamento literal de não captura {}.

Legível typealias

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

Abordagem de retorno de chamada

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
}

Abordagem de Fechamento

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 um projeto Xcode para chamar uma biblioteca C, como SQLite, é necessário (1) adicionar cabeçalhos C de referência de arquivo Bridging-Header.h como #import "sqlite3.h", (2) adicionar Bridging-Header.h ao cabeçalho de ponte Objective-C no projeto e (3) adicionar libsqlite3.tbdàs configurações de destino Vincular binário à biblioteca .

O sqlite.org 's 'SQLite em 5 minutos ou menos' exemplo é implementado em um projeto Swift Xcode7 aqui .

l --marc l
fonte
0

Você pode usar esta biblioteca em Swift para SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

SQLite Demo usando Swift com classe SQLDataAccess escrita em Swift

Adicionando ao seu projeto

Você só precisa de três arquivos para adicionar ao seu projeto * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header deve ser definido no projeto do Xcode 'Objective-C Bridging Header' em 'Compilador Swift - Geral'

Exemplos de uso

Basta seguir o código em ViewController.swift para ver como escrever SQL simples com SQLDataAccess.swift Primeiro você precisa abrir o banco de dados SQLite com o qual está lidando

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

Se openConnection for bem-sucedido, agora você pode fazer uma inserção simples na Tabela 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)")
    }

Veja como foi simples!

O primeiro termo em db.executeStatement é seu SQL como String, todos os termos que se seguem são uma lista de argumentos variadic do tipo Any e são seus parâmetros em um Array. Todos esses termos são separados por vírgulas em sua lista de argumentos SQL. Você pode inserir Strings, Integers, Date's e Blobs logo após a instrução de sequência, pois todos esses termos são considerados parâmetros para a sequência. A matriz de argumento variadic apenas torna conveniente inserir toda a sua sequência em apenas uma chamada executeStatement ou getRecordsForQuery. Se você não tiver nenhum parâmetro, não insira nada após seu SQL.

A matriz de resultados é uma Matriz de Dicionário, onde a 'chave' é o nome da coluna da sua tabela, e o 'valor' são os seus dados obtidos do SQLite. Você pode iterar facilmente por meio desse array com um loop for ou imprimi-lo diretamente ou atribuir esses elementos Dictionary a classes de objetos de dados personalizados que você usa em seus controladores de visualização para consumo de modelo.

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

SQLDataAccess armazenará texto, duplo, flutuante, blob, data, inteiro e inteiros longos longos. Para Blobs, você pode armazenar binary, varbinary, blob.

Para Texto, você pode armazenar char, caractere, clob, caractere variável nacional, caractere nativo, nchar, nvarchar, varchar, variante, caractere variável, texto.

Para datas, você pode armazenar datetime, time, timestamp, date.

Para inteiros, você pode armazenar bigint, bit, bool, boolean, int2, int8, inteiro, mediumint, smallint, tinyint, int.

Para Doubles, você pode armazenar decimal, precisão dupla, float, numérico, real, duplo. Double tem mais precisão.

Você pode até armazenar Nulls do tipo Null.

Em ViewController.swift, um exemplo mais complexo é feito mostrando como inserir um Dicionário como um 'Blob'. Além disso, SQLDataAccess entende Swift Date () nativo para que você possa inserir esses objetos sem conversão e irá convertê-los em texto e armazená-los e, quando recuperado, convertê-los de volta de texto em Date.

Claro que o verdadeiro poder do SQLite é sua capacidade de transação. Aqui você pode literalmente enfileirar 400 instruções SQL com parâmetros e inseri-los todos de uma vez, o que é realmente poderoso, pois é muito rápido. ViewController.swift também mostra um exemplo de como fazer isso. Tudo o que você realmente está fazendo é criar um Array de Dicionários chamado 'sqlAndParams', neste Array seus Dicionários de armazenamento com duas chaves 'SQL' para a instrução ou consulta de sequência String e 'PARAMS' que é apenas um Array de objetos nativos SQLite entende por essa consulta. Cada 'sqlParams', que é um dicionário individual de consulta sequencial mais parâmetros, é então armazenado no array 'sqlAndParams'. Depois de criar esse array, basta chamar.

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

Além disso, todos os métodos executeStatement e getRecordsForQuery podem ser feitos com String simples para consulta SQL e um Array para os parâmetros necessários à 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)")
    }

Uma versão Objective-C também existe e é chamada de mesmo SQLDataAccess, então agora você pode escolher escrever sua sequência em Objective-C ou Swift. Além disso, SQLDataAccess também funcionará com SQLCipher, o código presente ainda não foi configurado para funcionar com ele, mas é muito fácil de fazer, e um exemplo de como fazer isso está na versão Objective-C de SQLDataAccess.

SQLDataAccess é uma classe muito rápida e eficiente, e pode ser usada no lugar de CoreData, que realmente usa SQLite como seu armazenamento de dados subjacente, sem todas as falhas de integridade de dados do núcleo CoreData que vêm com CoreData.

geada
fonte
-1

Você pode configurar facilmente o SQLite com o swift usando a classe de tonelada única também.

Referir

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Método para criar banco de dados

func methodToCreateDatabase() -> NSURL?{} 

Método para inserir, atualizar e excluir dados

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

Método para selecionar dados

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