iOS/iOS 자료실

Swift(iOS) 스터디. 5번째 SQLite

Chipmunks 2018. 6. 3.
728x90

데이터를 저장하는 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 하게 코딩을 하도록 도와준다.


다음 시간에는 CocoaPodAlamofire 오픈소스로 웹서버와의 통신을 실습 해 볼 예정이다.

'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

댓글