IOS

Swift - Realm 통한 데이터 저장을 해보자

뭉기~ 2021. 1. 29. 22:16

iOS, Android 내에서 사용가능한 내부 모바일전용 데이터베이스 이며

기존에서 사용중인 SQLite 보다 속도가 빠르며 많은 기업에서도 사용중인 내부 데이터베이스 기술이다.

 

1. Realm 라이브러리 설치 

pod 'RealmSwift'

* Realm 라이브러리 설치중 pod install 까지 완료 했으나 적용에 문제가 있을경우

다음 명령어를 이용해 리셋후 재 설치가 가능하다.

pod cache clean Realm
pod cache clean RealmSwift
pod deintegrate || rm -rf Pods
pod install --verbose

2. 데이터 클래스 생성 (User)

import Foundation
import RealmSwift

class User: Object
{
    @objc dynamic var id: Int = 0
    @objc dynamic var name: String = ""
    @objc dynamic var age: Int = 0
    let infos = List<Info>()
    
    convenience init(id:Int, name:String, age: Int) {
        self.init()
        self.id = id
        self.name = name
        self.age = age
    }
    
    override class func primaryKey() -> String? {
        return "id"
    }
}

 * 클래스 내에 override 하여 pk 값을 지정 할수가 있다.


3. 배열 데이터 클래스 생성 (User -> Info)

import Foundation
import RealmSwift

class Info: Object
{
    @objc dynamic var major: String = ""
    @objc dynamic var hobby: String = ""
    
    convenience init(major:String, hobby: String) {
        self.init()
        self.major = major
        self.hobby = hobby
    }
}

4. 데이터 저장 (ViewController)

@IBAction func SaveRealm(_ sender: UIButton)
{
  let relam = try! Realm()

  let infos1 = Info(major: "developer", hobby: "soccer")
  let infos2 = Info(major: "progamer", hobby: "lol")

  let user1 = User(id: 0, name: "철수", age: 23)
  let user2 = User(id: 1, name: "영희", age: 30)
  user1.infos.append(infos1)
  user2.infos.append(infos2)

  let user3 = User(id: 2, name: "짱구", age: 5)

  try! relam.write {
  	relam.add([user1, user2, user3])
  }
}

5. 데이터 불러오기 (ViewController)

override func viewDidLoad() 
{
  super.viewDidLoad()

  do 
  {
  	let realm = try! Realm()
  	try realm.write {
  	let users = realm.objects(User.self)
  	// 쿼리 실행후 -> 파싱하여 사용
  	}
  } catch {
  	print(error)
  }
}
쿼리 :Results<User> <0x7fcedcc14550> (
	[0] User {
		id = 0;
		name = 철수;
		age = 23;
		infos = List<Info> <0x600000a00b40> (
		
		);
	},
	[1] User {
		id = 1;
		name = 영희;
		age = 30;
		infos = List<Info> <0x600000a00c00> (
			[0] Info {
				major = developer;
				hobby = soccer;
			},
			[1] Info {
				major = progamer;
				hobby = lol;
			}
		);
	},
	[2] User {
		id = 2;
		name = 짱구;
		age = 5;
		infos = List<Info> <0x600000a00cc0> (
		
		);
	}
)
  
print("값 1 : \(realm.objects(User.self).filter("name == '영희'").first)")
   - 값 1 : User 테이블에서 name이 영희 인 값을 가져오는데 중복값이 있을경우 첫번째 값만 가져온다.

let a = realm.objects(User.self).filter("name == [c] 'USER1'")

   - 값 2 : User 테이블에서 name이 USER1 인 값을 가져오지만 대소문자 구분을 하지 않는다.

                 중복값을 가져올경우 여러 리스트를 가져올수 있다 (id 및 날짜를 통한 정렬 필요)

                 해당 값은 배열 값을 가지고 있기에 배열 주소지를 선택해서 값을 가져올수 있다 (a[0].infos)

print("값 3 : \(realm.objects(User.self).filter("id IN {1,2}"))")
  - 값 3 : User 테이블에서 id이 1 값과 2인 값을 가져온다.        

print("값 4 : \(realm.objects(User.self).filter("name BEGINSWITH 'u'"))")
  - 값 4 : User 테이블에서 name이 u로 시작하는 값을 가져온다.
  
print("값 5 : \(realm.objects(User.self).filter("name CONTAINS 'er'"))")
  - 값 5 : User 테이블에서 name에 'er'이 포함된 값을 가져온다.
  
print("값 6 : \( realm.objects(User.self) .filter("name CONTAINS '짱구'").sorted(byKeyPath: "id"))")
  - 값 6 : User 테이블에서 name에 '짱구'이 포함된 값에 대해 id 순으로 정렬한 값을 가져온다.

6. 데이터 삭제하기 (ViewController)

do
{
	let realm = try! Realm()
	try realm.write {
		let data = realm.objects(User.self).filter("name == '짱구'").first //조건부 삭제
        let data = realm.objects(User.self) // 전체삭제
		realm.delete(data!)
	}
} catch {
	print(error)
}

7. 데이터 확인하기 (Realm Studio)

   7-1 해당 Url 접속하여 운영체제에 맞게 설치 진행 -> docs.realm.io/sync/realm-studio

 

Realm Studio

 

docs.realm.io

    7-2 . URL 정보 가져오기 (Realm 주소값) 

let fileURL = Realm.Configuration.defaultConfiguration.fileURL
print(fileURL)

     * 해당 값을 출력한다 (앱을 삭제한후 재 설치할경우 해당 값이 변경 될수 있다)

     7-3. 터미널 실행후 해당 출력한 URL 정보 입력

      7-4. default.realm 를 클릭후 저장한 내용을 볼수 있다.


8. 데이터 마이그레이션

Realm 사용중에 기존 데이터 테이블의 컬럼 추가 와 같은 수정사항이 발생하게 되면 다음과 같은 오류 메세지를 띄우고 앱이 죽어버린다.

해당 내용 에대해 마이그레이션을 진행한다.

* 해당 Migration 는 최초에 1회만 적용 시키면 그 이후로는 잘 동작한다.

   8-1. 컬럼 추가

import Foundation
import RealmSwift

class User: Object
{
    @objc dynamic var id: Int = 0
    @objc dynamic var name: String = ""
    @objc dynamic var age: Int = 0
    @objc dynamic var number: Int = 0 // 추가할 컬럼
    let infos = List<Info>()
    
    convenience init(id:Int, name:String, age: Int, number:Int) {
        self.init()
        self.id = id
        self.name = name
        self.age = age
        self.number = number // 추가할 컬럼
    }
    
    override class func primaryKey() -> String? {
        return "id"
    }
}

     8-2. Appdelegate Migration

       * schemaVersion : 새로운 스키마 버전 셋팅 → 이값은 이전에 사용했던 버전보다 커야 합니다. (변동 사항 있을때 +1)

       * AppDelegate 생성시 application function 는 만들어져 있는 상태이기 때문에 안에 config 내용만 추가한다

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let config = Realm.Configuration (
                     
            // 중요! 스키마버전 셋팅
            // 이전 버전보다 반드시 커야함
            schemaVersion: 2,

            migrationBlock: { migration, oldSchemaVersion in
                 
                // 셋팅한 스키마 버전보다 낮을경우 해당 코드 호출
                if (oldSchemaVersion < 1) {
                     
                    // 신규 업데이트 내용 추가
                    migration.enumerateObjects(ofType: User.className()) { // 추가할 컬럼 클래스
                        oldObject, newObject in
                        newObject!["number"] = Int() // 추가한 컬럼값 = 자료형()
                    }
                }
            })
         
        Realm.Configuration.defaultConfiguration = config
        
        return true
    }

  참고 : https://realm.io/kr/docs/swift/latest/#importing-the-realm-framework

https://jintaewoo.tistory.com/45

https://aircook.tistory.com/entry/Migration-in-Realm