Swift 코드로 내 앱에서 SQLite 데이터베이스에 액세스하는 방법을 찾고 있습니다.
Objective C에서 SQLite Wrapper를 사용하고 브리징 헤더를 사용할 수 있다는 것을 알고 있지만,이 프로젝트를 Swift에서 완전히 수행 할 수 있기를 바랍니다. 이 작업을 수행하는 방법이 있습니까? 누군가 쿼리를 제출하고 행을 검색하는 방법을 보여주는 참조를 알려줄 수 있습니까?
답변
많은 SQLite 래퍼 중 하나를 사용해야하지만 SQLite 라이브러리를 직접 호출하는 방법을 알고 싶다면 다음을 수행합니다.
-
SQLite C 호출을 처리하도록 Swift 프로젝트를 구성하십시오. Xcode 9 이상을 사용하는 경우 다음을 수행 할 수 있습니다.
import SQLite3
-
데이터베이스 생성 / 열기.
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 }
참고로, 데이터베이스를 열지 못했을 때 데이터베이스를 닫는 것이 이상하게 보이지만
sqlite3_open
문서 에는 메모리 누수를 피하기 위해 그렇게해야한다고 명시되어 있습니다.열릴 때 오류가 발생하는지 여부에 관계없이 데이터베이스 연결 핸들 과 관련된 자원
sqlite3_close()
은 더 이상 필요하지 않을 때 전달하여 해제해야 합니다. -
sqlite3_exec
SQL을 수행하는 데 사용 합니다 (예 : 테이블 생성).if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("error creating table: \(errmsg)") }
-
값을 바인딩 할 자리 표시 자로
sqlite3_prepare_v2
SQL을 준비하는 데 사용?
합니다.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)
-
다른 값을 삽입하려면 SQL을 재설정하십시오. 이 예에서는
NULL
값을 삽입 합니다.if sqlite3_reset(statement) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("error resetting prepared statement: \(errmsg)") } if sqlite3_bind_null(statement, 1) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("failure binding null: \(errmsg)") } if sqlite3_step(statement) != SQLITE_DONE { let errmsg = String(cString: sqlite3_errmsg(db)!) print("failure inserting null: \(errmsg)") }
-
준비된 명령문과 연관된 메모리를 복구하기 위해 준비된 명령문을 완료하십시오.
if sqlite3_finalize(statement) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("error finalizing prepared statement: \(errmsg)") } statement = nil
-
테이블에서 값을 선택하기위한 새 문을 준비하고 값 검색을 통해 반복합니다.
if sqlite3_prepare_v2(db, "select id, name from test", -1, &statement, nil) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("error preparing select: \(errmsg)") } while sqlite3_step(statement) == SQLITE_ROW { let id = sqlite3_column_int64(statement, 0) print("id = \(id); ", terminator: "") if let cString = sqlite3_column_text(statement, 1) { let name = String(cString: cString) print("name = \(name)") } else { print("name not found") } } if sqlite3_finalize(statement) != SQLITE_OK { let errmsg = String(cString: sqlite3_errmsg(db)!) print("error finalizing prepared statement: \(errmsg)") } statement = nil
-
데이터베이스 닫기 :
if sqlite3_close(db) != SQLITE_OK { print("error closing database") } db = nil
Swift 2 및 이전 버전의 Xcode에 대해서는 이 답변의 이전 개정판을 참조하십시오 .
답변
가장 좋은 방법은 브리징 헤더 안에 동적 라이브러리를 가져 오는 것입니다.
- “Link Binary With Libraries”빌드 단계에 libsqlite3.dylib를 추가합니다.
- “Bridging-Header.h”를 만들고
#import <sqlite3.h>
상단에 추가 - “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)")
}
}
}
더 많은 기능과 함께!
여기에서 확인할 수 있습니다.
답변
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 열거 형을 선언합니다. 고유 한 데이터베이스 변환 가능 유형을 정의하십시오.
-
데이터베이스 마이그레이션
답변
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에서 사용한 최고의 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 친화적 인 래퍼로도 작동합니다.”라고 나와 있으며 그에 대한 몇 가지 예가 뒤 따릅니다.