데이터를 저장하는 SQLite 를 Swift 상에서 어떻게 사용하는지 간단하게 알아봤다.
다음 Swift SQLite 포스팅을 실습해봤다. 단순히 C 코드를 Swift 으로 바꾸는 작업이다.
XCode PlayGround 에서 Markdown 표시하기
예제코드를 띄웠을 때, XCode 상에서 Markdown 으로 나왔다.
주석 상에서 //, /* */ 대신, //:, /*: */ 으로 Markdown 으로 명시를 해줄 수가 있다.
자세한 것은 다음 문서에 나와있다.
SQLite3 임포트하기
1 | import SQLite3 | cs |
데이터베이스 연결하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | func openDatabase() -> OpaquePointer? { var db: OpaquePointer? = nil if sqlite3_open(part1DbPath, &db) == SQLITE_OK { print("Successfully opened connection to database at \(part1DbPath)") return db } else { print("Unable to open database. Verify that you created the directory described " + "in the Getting Started section.") PlaygroundPage.current.finishExecution() } } let db = openDatabase() | cs |
OpaquePointer 구조체는 Swift 로는 표현 불가능한 자료형의, C 포인터를 표현할 때 쓴다.
< 헤더파일 상의 주석 >
테이블 생성하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | let createTableString = """ CREATE TABLE Contact( Id INT PRIMARY KEY NOT NULL, Name CHAR(255)); """ func createTable() { // 1 var createTableStatement: OpaquePointer? = nil // 2 if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK { // 3 if sqlite3_step(createTableStatement) == SQLITE_DONE { print("Contact table created.") } else { print("Contact table could not be created.") } } else { print("CREATE TABLE statement could not be prepared.") } // 4 sqlite3_finalize(createTableStatement) } createTable() | cs |
sqlite3_prepare_v2 함수의 sqlite3_prepare 함수의 업데이트된 버전으로 보인다. 헤더파일의 주석에서도 v2 함수 사용을 권장한다. 문자열 쿼리문을 쿼리 스테이트 객체로 바꿔준다.
세 번째 매개변수는 nByte 이다. nByte 매개변수가 음수면 \0 문자가 나올 때 까지 읽는다는 의미다.
음수가 아닌 양수면, 그 바이트 수만큼 읽는다는 의미다.
sqlite3_step 함수는 쿼리 스테이트 객체의 결과를 리턴한다. SQLITE_DONE 은 성공적으로 쿼리문을 실행했다는 뜻이다. SQLITE_ROW 은 어떤 데이터를 반환할 때, 그의 결과값으로 함수에서 반환된다. 데이터가 여러 개 일때, 이 함수를 계속 사용한다.
sqlite3_finalize 함수는 스테이트 객체를 안전하게 종료시킨다. 스테이트 객체 사용에 아무 에러가 없거나, 스테이트 객체를 사용한 적이 없다면 정상적으로 종료된다. 이 떄, SQLITE_OK 값이 반환된다.
데이터 삽입하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | let insertStatementString = "INSERT INTO Contact (Id, Name) VALUES (?, ?);" func insert() { var insertStatement: OpaquePointer? = nil // 1 if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { let id: Int32 = 1 let name: NSString = "Ray" // 2 sqlite3_bind_int(insertStatement, 1, id) // 3 sqlite3_bind_text(insertStatement, 2, name.utf8String, -1, nil) // 4 if sqlite3_step(insertStatement) == SQLITE_DONE { print("Successfully inserted row.") } else { print("Could not insert row.") } } else { print("INSERT statement could not be prepared.") } // 5 sqlite3_finalize(insertStatement) } insert() | cs |
쿼리문에 '?' 자리에 들어갈 데이터를 bind 해주는 소스다.
sqlite3_bind_text 함수의 4번째 인자 역시, 3번째 인자의 Value를 읽을 바이트 수를 뜻한다. 음수면은 첫 \0 이 나올 때 까지 읽는다.
5번째 인자는 BLOB(Binary Large OBject), 으로 데이터베이스에서 바이너리 파일, 에 관한 속성이다. 사용하지 않으므로 nil 값을 보낸다.
쿼리(질의) 보내기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | let queryStatementString = "SELECT * FROM Contact;" func query() { var queryStatement: OpaquePointer? = nil // 1 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { // 2 if sqlite3_step(queryStatement) == SQLITE_ROW { // 3 let id = sqlite3_column_int(queryStatement, 0) // 4 let queryResultCol1 = sqlite3_column_text(queryStatement, 1) let name = String(cString: queryResultCol1!) // 5 print("Query Result:") print("\(id) | \(name)") } else { print("Query returned no results") } } else { print("SELECT statement could not be prepared") } // 6 sqlite3_finalize(queryStatement) } query() | cs |
데이터 수정(업데이트) 하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let updateStatementString = "UPDATE Contact SET Name = 'Chris' WHERE Id = 1;" func update() { var updateStatement: OpaquePointer? = nil if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK { if sqlite3_step(updateStatement) == SQLITE_DONE { print("Successfully updated row.") } else { print("Could not update row.") } } else { print("UPDATE statement could not be prepared") } sqlite3_finalize(updateStatement) } update() query() | cs |
데이터 삭제하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | let deleteStatementStirng = "DELETE FROM Contact WHERE Id = 1;" func delete() { var deleteStatement: OpaquePointer? = nil if sqlite3_prepare_v2(db, deleteStatementStirng, -1, &deleteStatement, nil) == SQLITE_OK { if sqlite3_step(deleteStatement) == SQLITE_DONE { print("Successfully deleted row.") } else { print("Could not delete row.") } } else { print("DELETE statement could not be prepared") } sqlite3_finalize(deleteStatement) } delete() query() //: ## Errors | cs |
Error 처리(핸들링)하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | let malformedQueryString = "SELECT Stuff from Things WHERE Whatever;" func prepareMalformedQuery() { var malformedStatement: OpaquePointer? = nil // 1 if sqlite3_prepare_v2(db, malformedQueryString, -1, &malformedStatement, nil) == SQLITE_OK { print("This should not have happened.") } else { // 2 let errorMessage = String.init(cString: sqlite3_errmsg(db)) print("Query could not be prepared! \(errorMessage)") } // 3 sqlite3_finalize(malformedStatement) } prepareMalformedQuery() | cs |
Swift 문자열 String 객체의 초기화 메소드로, 에러메시지를 변환하고 있다.
sqlite3_errmsg 함수의 반환값은 영문으로 작성된 오류 문자열로, UTF-8, UTF-16 각각 지원한다. 내부적으로 메모리 관리를 하기 때문에, 사용자가 직접 메모리를 해제해 줄 필요는 없다.
데이터베이스 연결 종료하기
1 | sqlite3_close(db) | cs |
SQLite.swift
현재 SQLite.swift 리포 오픈소스로 최대한 Swife-like 하게 코딩을 하도록 도와준다.
'iOS > iOS 자료실' 카테고리의 다른 글
[오류] Could not insert new outlet connection 해결법 (2) | 2018.08.03 |
---|---|
Swift 스터디. 4번째 (0) | 2018.05.19 |
Swift 스터디. 3번째 (0) | 2018.05.19 |
Swift 스터디. 4월 둘째주 (0) | 2018.04.15 |
Swift 스터디. 4월 첫째주 (0) | 2018.04.08 |
댓글