본문 바로가기
아이폰 개발/Swift

swift sqlite 예제 feat : FileManager, Bundle.main, defer

by 인생여희 2021. 3. 30.

swift sqlite 예제 feat : FileManager, Bundle.main, defer

 

 

sqlite import 

import SQLite3

 

 

주요 구문

    var db : OpaquePointer? = nil                    //SQLite 연결 정보 - 구조체    
    var stmt : OpaquePointer? = nil                  //컴파일된 SQL을 담을 객체
    
    sqlite3_open(dbPath, &db)                      // db연결함수 - db 객체가 생성
    sqlite3_prepare(db, sql, -1, &stmt, nil)       //구문을 전달 준비 - 컴파일된 sql 구문 객체가 생성됨
    sqlite3_step(stmt)    //컴파일된 sql 객체를 db에 전달 - stmt에는 db 연결 정보와 컴파일된 SQL 객체가 포함되어 있다
    
    //위 함수의 호출이 끝나면 작성한 sql 쿼리문이 실행되어 테이블이 생성된다.
    
    sqlite3_finalize(stmt)  //컴파일된 sql 구문 객체를 삭제
    sqlite3_close(db)      //db 연결 종료 - db 객체가 해제됨

 

 

[1] db.sqlite 파일의 경로를 찾아서 반환

func getDBPath() -> String {
        
        //데이터베이스 파일의 경로를 읽어오는 내용
        //urls(for:in:) 메서드는 배열 형태로 반환하기 때문에 인덱스를 사용해서 접근해야 합니다.
        //문서 디렉터리는 하나만 존재하기 때문에 first 속성을 사용하면 됩니다.
        
        let fileMgr = FileManager()
        let docPathURL = fileMgr.urls(for: .documentDirectory, in: .userDomainMask).first!
        let dbPath = docPathURL.appendingPathComponent("db.sqlite").path
        
       
        
        //dbPath 경로에 파일이 없다면, 앱 번들의 db.sqlite를 가져와 복사
        if fileMgr.fileExists(atPath: dbPath) == false {
            
            do {
                //번들 경로
                guard let dbSource = Bundle.main.path(forResource: "db", ofType: "sqlite")
                else{
                    print("Bundle에 db.sqlite 파일이 존재하지 않습니다.")
                    return "error"
                }
                
                try  fileMgr.copyItem(atPath: dbSource, toPath: dbPath)
            } catch {
                print("fail.. copying file...")
            }
        }
        
        print("db.sqlite 파일 경로 : \(dbPath)")
        return dbPath
    }

 

 

 

guard 구문을 사용하면 sqlite3_finalize(), sqlite3_close() 함수를 처리할 위치가 애매해집니다.

guard else 블록 내에 구현해 줄 수 있지만 같은 코드를 중복으로 작성해야 하기 때문에

defer 블록을 사용해서 구현합니다.

 

defer 블록은 코드의 흐름과 관계없이 가장 마지막에 실행되는 블록입니다.

함수의 종료 직전에 실행되며 종료 시점에 처리해야 할 내용이 있다면 defer 블록 내에 작성하면 됩니다.

이 블록은 함수에서 사용된 리소스의 해제를 할 때 사용됩니다.

 

defer 블록은 sqlite3_open()의 실패로 인해 return을 만나서 함수가 종료되면 실행되지 않습니다.

함수의 종료문보다 먼저 작성이 되어야 하며 작성된 순서의 역순으로 호출됩니다.

 

 

 

[2] 데이터 베이스 관련 코드

func dbExecute(dbPath : String){
        
        var db : OpaquePointer? = nil   //SQLite 연결 정보 - 구조체
        
        //db 연결 성공 확인
        guard sqlite3_open(dbPath, &db) == SQLITE_OK else {
            print("database connect fail...")
            return
        }
        
        //데이터 베이스 연결 종료
        defer {
            print("close database connection")
            sqlite3_close(db)
        }
        

        var stmt : OpaquePointer? = nil                  //컴파일된 SQL을 담을 객체
        let sql = "CREATE TABLE IF NOT EXISTS sequence (num INTEGER)"  //SQL 구문
        
        guard sqlite3_prepare(db, sql, -1, &stmt, nil) == SQLITE_OK else {
            print("prepare statment fail...")
            return
        }
        
        //stmt 변수 해제
        defer {
            print("Finalize Statment")
            sqlite3_finalize(stmt)
        }
        
        //테이블 생성
        if sqlite3_step(stmt) == SQLITE_DONE {
            print("create table success..")
        }
}
    

 

 

[3] 호출

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let dbPath = self.getDBPath()
        self.dbExecute(dbPath: dbPath)
        
    }

 

 

SQLLITETest.zip
0.03MB

 

 

참고

https://blog.naver.com/go4693/221388274001

https://blog.naver.com/go4693/221389139215