programing tip

Swift에서 SQLite 데이터베이스에 액세스

itbloger 2020. 8. 18. 07:24
반응형

Swift에서 SQLite 데이터베이스에 액세스


Swift 코드로 내 앱에서 SQLite 데이터베이스에 액세스하는 방법을 찾고 있습니다.

Objective C에서 SQLite Wrapper를 사용하고 브리징 헤더를 사용할 수 있다는 것을 알고 있지만,이 프로젝트를 Swift에서 완전히 수행 할 수 있기를 바랍니다. 이 작업을 수행하는 방법이 있습니까? 누군가 쿼리를 제출하고 행을 검색하는 방법을 보여주는 참조를 알려줄 수 있습니까?


많은 SQLite 래퍼 중 하나를 사용해야하지만 (저는 FMDB를 선호 합니다) SQLite 라이브러리를 직접 호출하는 방법을 알고 싶다면 다음을 수행합니다.

  1. SQLite C 호출을 처리하도록 Swift 프로젝트를 구성하십시오. Xcode 9를 사용하는 경우 다음을 수행 할 수 있습니다.

    import SQLite3
    

    이전 버전의 Xcode에서는 다음을 수행 할 수 있습니다.

    • 프로젝트에 대한 브리징 헤더 파일을 만듭니다. Using Swift with Cocoa and Objective-CImporting Objective-C into Swift 섹션을 참조하십시오 . 이 브리징 헤더는 다음을 가져와야합니다 .sqlite3.h

      #import <sqlite3.h>
      
    • 추가 libsqlite3.tbd(또는 이전 버전의 경우 libsqlite3.dylib) 프로젝트에 :

      여기에 이미지 설명 입력

  2. 데이터베이스 생성 / 열기.

    let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent("test.sqlite")
    
    // open database
    
    var db: OpaquePointer?
    if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
        print("error opening database")
    }
    
  3. sqlite3_execSQL을 수행하는 데 사용 합니다 (예 : 테이블 생성).

    if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error creating table: \(errmsg)")
    }
    
  4. 값을 바인딩 할 자리 표시 자로 sqlite3_prepare_v2SQL을 준비하는 데 사용 ?합니다.

    var statement: OpaquePointer?
    
    if sqlite3_prepare_v2(db, "insert into test (name) values (?)", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing insert: \(errmsg)")
    }
    
    if sqlite3_bind_text(statement, 1, "foo", -1, SQLITE_TRANSIENT) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding foo: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting foo: \(errmsg)")
    }
    

    다음과 같이 구현할 수있는SQLITE_TRANSIENT 상수 를 사용합니다 .

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
    
  5. 다른 값을 삽입하려면 SQL을 재설정하십시오. 이 예에서는 NULL값을 삽입 합니다.

    if sqlite3_reset(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error resetting prepared statement: \(errmsg)")
    }
    
    if sqlite3_bind_null(statement, 1) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding null: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting null: \(errmsg)")
    }
    
  6. 준비된 명령문과 연관된 메모리를 복구하기 위해 준비된 명령문을 완료하십시오.

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
    
  7. 테이블에서 값을 선택하기위한 새 문을 준비하고 값 검색을 통해 반복합니다.

    if sqlite3_prepare_v2(db, "select id, name from test", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing select: \(errmsg)")
    }
    
    while sqlite3_step(statement) == SQLITE_ROW {
        let id = sqlite3_column_int64(statement, 0)
        print("id = \(id); ", terminator: "")
    
        if let cString = sqlite3_column_text(statement, 1) {
            let name = String(cString: cString)
            print("name = \(name)")
        } else {
            print("name not found")
        }
    }
    
    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
    
  8. 데이터베이스 닫기 :

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

Swift 2 의 경우이 답변의 이전 개정판을 참조하십시오 .


가장 좋은 방법은 브리징 헤더 안에 동적 라이브러리를 가져 오는 것입니다.

  1. "Link Binary With Libraries"빌드 단계에 libsqlite3.dylib를 추가합니다.
  2. "Bridging-Header.h"를 만들고 #import <sqlite3.h>상단에 추가
  3. "Swift 컴파일러-코드 생성"아래의 빌드 설정에서 "Objective-C 브리징 헤더"설정에 대해 "Bridging-Header.h"를 설정합니다.

그러면 sqlite3_open신속한 코드에서 와 같이 모든 c 메서드에 액세스 할 수 있습니다.

그러나 FMDB 를 사용 하고 sqlite의 객체 지향 래퍼 인 브리징 헤더를 통해 가져올 수 있습니다. C 포인터와 구조체를 다루는 것은 Swift에서 번거로울 것입니다.


저도 이전에 Objective-C에서했던 것과 같은 방식으로 SQLite와 상호 작용할 수있는 방법을 찾고있었습니다. C 호환성 때문에 저는 그냥 C API를 사용했습니다.

현재 Swift에 SQLite에 대한 래퍼가없고 위에서 언급 한 SQLiteDB 코드가 약간 더 높은 수준으로 이동하고 특정 사용을 가정하므로 래퍼를 만들고 그 과정에서 Swift에 약간 익숙해지기로 결정했습니다. https://github.com/chrismsimpson/SwiftSQLite에서 찾을 수 있습니다 .

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

var statement = SQLiteStatement(database: db);

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

statement.bindInt(1, value: 123);

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

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

SwiftData 라는 Swift로 완전히 작성된 우아한 SQLite 라이브러리를 만들었습니다 .

그 기능 중 일부는 다음과 같습니다.

  • 객체를 SQL 문자열에 편리하게 바인딩
  • 트랜잭션 및 저장 점 지원
  • 인라인 오류 처리
  • 기본적으로 완전히 스레드 안전

'변경'(예 : INSERT, UPDATE, DELETE 등)을 실행하는 쉬운 방법을 제공합니다.

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES ('Toronto', 2615060, 0, '1793-08-27')") {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

및 '쿼리'(예 : SELECT) :

let (resultSet, err) = SD.executeQuery("SELECT * FROM Cities")
if err != nil {
    //there was an error during the query, handle it here
} else {
    for row in resultSet {
        if let name = row["Name"].asString() {
            println("The City name is: \(name)")
        }
        if let population = row["Population"].asInt() {
            println("The population is: \(population)")
        }
        if let isWarm = row["IsWarm"].asBool() {
            if isWarm {
                println("The city is warm")
            } else {
                println("The city is cold")
            }
        }
        if let foundedIn = row["FoundedIn"].asDate() {
            println("The city was founded in: \(foundedIn)")
        }
    }
}

더 많은 기능과 함께!

여기에서 확인할 수 있습니다.


AppDelegate.swift

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

    print(DBpath)

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

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

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

Database.swift

import UIKit

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

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

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

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

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

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

    return result
}

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

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

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

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

                var dict=Dictionary<String, String>()

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

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

                    dict[rFiled] = rValue

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

    sqlite3_close(db)
    }
    return result
}

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

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

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

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

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

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

}

Model.swift

import UIKit


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

액세스 데이터베이스 :

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

데이터베이스 쿼리 실행 :

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

Swift 2 및 Swift 3을위한 또 다른 SQLite 래퍼 : http://github.com/groue/GRDB.swift

풍모:

  • ccgus / fmdb 사용자에게 친숙한 API

  • Swift 표준 라이브러리를 활용하는 저수준 SQLite API

  • SQL 알레르기 개발자를위한 매우 신속한 쿼리 인터페이스

  • SQLite WAL 모드 지원 및 추가 성능을위한 동시 데이터베이스 액세스

  • 결과 집합을 래핑하고, 아침 식사를 위해 사용자 지정 SQL 쿼리를 먹고, 기본 CRUD 작업을 제공하는 레코드 클래스

  • 신속한 유형 자유 : 데이터에 맞는 올바른 Swift 유형을 선택하십시오. 필요할 때 Int64를 사용하거나 편리한 Int를 고수하십시오. NSDate 또는 NSDateComponents를 저장하고 읽습니다. 이산 데이터 유형에 대한 Swift 열거 형을 선언합니다. 고유 한 데이터베이스 변환 가능 유형을 정의하십시오.

  • 데이터베이스 마이그레이션

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


내가 쓴 스위프트로 작성된 sqlite3를 래퍼 라이브러리를 .

이것은 실제로 매우 간단한 API를 사용하는 매우 높은 수준의 래퍼이지만 어쨌든 저수준 C inter-op 코드가 있으며 여기에 C inter-op을 보여주기 위해 여기에 (간단한) 부분을 게시합니다.

    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
    }

이 저수준 래퍼의 전체 소스 코드를 원한다면 다음 파일을 참조하십시오.


SQLite C 호출을 처리하도록 Swift 프로젝트를 구성하십시오.

프로젝트에 대한 브리징 헤더 파일을 만듭니다. Using Swift with Cocoa and Objective-C의 Importing Objective-C into Swift 섹션을 참조하십시오. 이 브리징 헤더는 sqlite3.h를 가져와야합니다.

프로젝트에 libsqlite3.0.dylib를 추가하십시오. 프로젝트에 라이브러리 / 프레임 워크 추가에 관한 Apple의 문서를 참조하십시오.

다음 코드를 사용했습니다.

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

                            var keyString = sqlite3_column_name(cStatement,Int32(i))
                            thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                        }
                        result = NSNull()

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

때로는 sqlite.org에 나와있는 "5 분 이내에 SQLite" 접근 방식 의 Swift 버전으로 충분합니다. 은 "5 분 이하"접근 방식에서 사용 하는 편의 랩퍼이고 , , , 및 .sqlite3_exec()sqlite3_prepare()sqlite3_step()sqlite3_column()sqlite3_finalize()

Swift 2.2는 sqlite3_exec() callback함수 포인터를 전역 비 인스턴스 프로 시저 func또는 비 캡처 리터럴 클로저 직접 지원할 수 있습니다 {}.

읽기 가능 typealias

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

콜백 접근법

func callback(
    resultVoidPointer: CVoidPointer, // void *NotUsed 
    columnCount: CInt,               // int argc
    values: CCharHandle,             // char **argv     
    columns: CCharHandle             // char **azColName
    ) -> CInt {
    for  i in 0 ..< Int(columnCount) {
        guard let value = String.fromCString(values[i]) 
        else { continue }
        guard let column = String.fromCString(columns[i]) 
        else { continue }
        print("\(column) = \(value)")
    }
    return 0 // status ok
}

func sqlQueryCallbackBasic(argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0 // result code

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(db, argv[2], callback, nil, &zErrMsg)
    if rc != SQLITE_OK {
        print("ERROR: sqlite3_exec " + String.fromCString(zErrMsg)! ?? "")
        sqlite3_free(zErrMsg)
    }

    sqlite3_close(db)
    return 0
}

폐쇄 접근법

func sqlQueryClosureBasic(argc argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(
        db,      // database 
        argv[2], // statement
        {        // callback: non-capturing closure
            resultVoidPointer, columnCount, values, columns in

            for i in 0 ..< Int(columnCount) {
                guard let value = String.fromCString(values[i]) 
                else { continue }
                guard let column = String.fromCString(columns[i]) 
                else { continue }
                print("\(column) = \(value)")
            }
            return 0
        }, 
        nil, 
        &zErrMsg
    )

    if rc != SQLITE_OK {
        let errorMsg = String.fromCString(zErrMsg)! ?? ""
        print("ERROR: sqlite3_exec \(errorMsg)")
        sqlite3_free(zErrMsg)
    }
    sqlite3_close(db)
    return 0
}

SQLite와 같은 C 라이브러리를 호출하는 Xcode 프로젝트를 준비하려면 (1) Bridging-Header.h 파일 참조 C 헤더를 추가하고 #import "sqlite3.h", (2) Bridging-Header.h를 프로젝트의 Objective-C Bridging Header 에 추가해야합니다. 설정 및 (3) 라이브러리와 바이너리 연결 대상 설정에 추가 libsqlite3.tbd합니다 .

sqlite.org"SQLite는 5 분 이내에" 예는 스위프트 Xcode7 프로젝트에서 구현됩니다 여기에 .


단일 톤 클래스를 사용하여 신속하게 SQLite를 쉽게 구성 할 수 있습니다.

보내다

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

데이터베이스 생성 방법

func methodToCreateDatabase() -> NSURL?{} 

데이터 삽입, 업데이트 및 삭제 방법

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

데이터 선택 방법

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

이 라이브러리는 Swift for SQLite https://github.com/pmurphyjam/SQLiteDemo 에서 사용할 수 있습니다.

SQLiteDemo

Swift로 작성된 SQLDataAccess 클래스와 Swift를 사용하는 SQLite 데모

프로젝트에 추가

프로젝트에 추가하려면 3 개의 파일 만 필요합니다. * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header는 'Swift Compiler-General'의 Xcode 프로젝트 'Objective-C Bridging Header'에서 설정해야합니다.

사용 예

ViewController.swift의 코드를 따라 SQLDataAccess.swift로 간단한 SQL을 작성하는 방법을 확인하세요. 먼저 처리하는 SQLite 데이터베이스를 열어야합니다.

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

openConnection이 성공하면 이제 Table AppInfo에 간단한 삽입을 수행 할 수 있습니다.

```swift
//Insert into Table AppInfo
let status = db.executeStatement("insert into AppInfo (name,value,descrip,date) values(?,?,?,?)",
”SQLiteDemo","1.0.2","unencrypted",Date())
if(status)
{
    //Read Table AppInfo into an Array of Dictionaries
    let results = db.getRecordsForQuery("select * from AppInfo ")
    NSLog("Results = \(results)")
}
```

얼마나 간단한 지보십시오!

db.executeStatement의 첫 번째 용어는 SQL as String이고 다음의 모든 용어는 Any 유형의 가변 인수 목록이며 배열의 매개 변수입니다. 이러한 모든 용어는 SQL 인수 목록에서 쉼표로 구분됩니다. 이러한 모든 용어가 속편의 매개 변수로 간주되기 때문에 속편 문 바로 뒤에 문자열, 정수, 날짜 및 Blob을 입력 할 수 있습니다. 가변 인수 배열을 사용하면 한 번의 executeStatement 또는 getRecordsForQuery 호출로 모든 속편을 편리하게 입력 할 수 있습니다. 매개 변수가없는 경우 SQL 뒤에 아무것도 입력하지 마십시오.

결과 배열은 '키'가 테이블 열 이름이고 '값'이 SQLite에서 얻은 데이터 인 사전 배열입니다. for 루프를 사용하여이 배열을 쉽게 반복하거나 직접 인쇄하거나 이러한 Dictionary 요소를 모델 소비를 위해 View Controller에서 사용하는 사용자 지정 데이터 개체 클래스에 할당 할 수 있습니다.

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


```

SQLDataAccess는 텍스트, double, float, blob, 날짜, 정수 및 long long 정수를 저장합니다. Blob의 경우 바이너리, varbinary, blob을 저장할 수 있습니다.

텍스트의 경우 char, character, clob, 국가 별 다양한 문자, 네이티브 문자, nchar, nvarchar, varchar, variant, 다양한 문자, 텍스트를 저장할 수 있습니다.

날짜의 경우 datetime, time, timestamp, date를 저장할 수 있습니다.

정수의 경우 bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int를 저장할 수 있습니다.

Doubles의 경우 소수, 배정 밀도, 부동 소수점, 숫자, 실수, 배정 밀도를 저장할 수 있습니다. Double이 가장 정확합니다.

Null 유형의 Null을 저장할 수도 있습니다.

ViewController.swift에서 Dictionary를 'Blob'으로 삽입하는 방법을 보여주는 더 복잡한 예제가 수행됩니다. 또한 SQLDataAccess는 네이티브 Swift Date ()를 이해하므로 변환없이 이러한 개체를 삽입 할 수 있으며 텍스트로 변환하여 저장하고 검색되면 다시 텍스트에서 날짜로 변환합니다.

물론 SQLite의 진정한 힘은 트랜잭션 기능입니다. 여기에서 말 그대로 매개 변수가있는 400 개의 SQL 문을 대기열에 넣고 한 번에 모두 삽입 할 수 있습니다. 이는 매우 빠르기 때문에 정말 강력합니다. ViewController.swift는이를 수행하는 방법의 예도 보여줍니다. 당신이 정말로하고있는 것은 'sqlAndParams'라는 사전 배열을 만드는 것입니다. 해당 쿼리를 이해합니다. 후속 쿼리와 매개 변수의 개별 사전 인 각 'sqlParams'는 'sqlAndParams'배열에 저장됩니다. 이 배열을 만든 후에는 호출하기 만하면됩니다.

```swift
let status = db.executeTransaction(sqlAndParams)
if(status)
{
    //Read Table AppInfo into an Array of Dictionaries for the above Transactions
    let results = db.getRecordsForQuery("select * from AppInfo ")
    NSLog("Results = \(results)")
}
```

또한 모든 executeStatement 및 getRecordsForQuery 메서드는 SQL 쿼리에 대한 간단한 문자열과 쿼리에 필요한 매개 변수에 대한 배열을 사용하여 수행 할 수 있습니다.

```swift
let sql : String = "insert into AppInfo (name,value,descrip) values(?,?,?)"
let params : Array = ["SQLiteDemo","1.0.0","unencrypted"]
let status = db.executeStatement(sql, withParameters: params)
if(status)
{
    //Read Table AppInfo into an Array of Dictionaries for the above Transactions
    let results = db.getRecordsForQuery("select * from AppInfo ")
    NSLog("Results = \(results)")
}
```

Objective-C 버전도 존재하며 동일한 SQLDataAccess라고하므로 이제 Objective-C 또는 Swift로 속편을 작성하도록 선택할 수 있습니다. 또한 SQLDataAccess는 SQLCipher와 함께 작동합니다. 현재 코드는 아직 작동하도록 설정되어 있지 않지만 수행하기가 매우 쉽습니다.이를 수행하는 방법의 예는 실제로 SQLDataAccess의 Objective-C 버전에 있습니다.

SQLDataAccess는 매우 빠르고 효율적인 클래스이며 CoreData와 함께 제공되는 모든 CoreData 핵심 데이터 무결성 오류 충돌없이 SQLite를 기본 데이터 저장소로 사용하는 CoreData 대신 사용할 수 있습니다.


이것은 내가 Swift에서 사용한 최고의 SQLite 라이브러리입니다 : https://github.com/stephencelis/SQLite.swift

코드 예제를보십시오. C API보다 훨씬 깔끔합니다.

import SQLite

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

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

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

let insert = users.insert(name <- "Alice", email <- "alice@mac.com")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice@mac.com')

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

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

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

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

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

문서에는 또한 "SQLite.swift는 C API를 통해 가볍고 Swift 친화적 인 래퍼로도 작동합니다."라고 나와 있으며 그에 대한 몇 가지 예가 뒤 따릅니다.

참고 URL : https://stackoverflow.com/questions/24102775/accessing-an-sqlite-database-in-swift

반응형