포스트

Todoey (6)

Realm 사용해보기.

사이트참고

1. AppDelegate 수정하기

1
2
3
4
5
6
7
8
import RealmSwift

// didFinishLaunchingWithOptions
do {
    let realm = try Realm()
    } catch {
            print("Error initializing new Realm, \(error)")
    }

2. 파일 생성

1
2
3
4
5
6
import Foundation
import RealmSwift

class Data: Object {
    
}

파일을 생성하고 Object를 상속하게 하자. Object는 Object is a class used to define Realm model objects.

Realm을 설치하면서 추가되었다.

그리고 변수를 만들때 dynamic을 붙여준다.

아무래도 Realm을 사용할때 쓰는듯 하다.

Dynamic Dispatch를 사용하라는 의미인데, 앱이 실행되는 동안 Dynamic으로 설정한 변수들을 모니터 한다.

사용자가 name을 변경 할 경우, 앱이 실행되는동안 Realm이 데이터베이스에서 변경 사항을 동적으로 업데이트 하게된다.

@objc를 쓰지않았지만 빌드 후 에러가 발생하지 않았다.

AppDelegate로 돌아가서,

1
2
3
let data = Data()
        data.name = "Harold"
        data.age = 10

이렇게 작성을 해보았다.

그리고

1
2
3
4
5
6
7
8
do {
            let realm = try Realm()
            try realm.write {
                realm.add(data)
            }
        } catch {
            print("Error initializing new Realm, \(error)")
        }

이렇게 하고 실행하니 바로 Exception Error가 발생했다 Data에 관한 내용이었고, @objc를 적지않아 발생한 문제다.

즉 위에 Objc를 쓰지만 에러가 발생하지 않았다는 건 그냥 빌드했을때이고, 실제로 실행할때는 에러가 발생하므로, 꼭 objc를 적어주자.

지금은 실행해도 CoreData와 Realm이 같이 공존하고 있다.

3. Realm 파일의 위치 확인해보기

print(Realm.Configuration.defaultConfiguration.fileURL) 를 적어보자.

실행하면 console에 해당 위치가 출력이된다.

AppStore에 RealmBrowser가 있으니 설치를 해주자.

encryption key가 필요하다고해서 Realm Studio를 설치했다.

그리고 Command+Shift+G를 눌러 해당 경로를 복붙하면 이동이 훨씬 편해진다.

확인결과

CleanShot 2024-04-15 at 11 40 50@2x

등록이 되었다.

두개가 되어있는건 두번실행했기 때문.

CRUD 중 Create 구현

이전에 Data작성이 확인이 되었으니, 다시 해당부분을 지워주자

1
2
3
4
5
do {
            let realm = try Realm()
        } catch {
            print("Error initializing new Realm, \(error)")
        }

이렇게만 다시 남겨두도록 한다.

1. 파일 생성

기존 Data.swift는 지우고 Item, Category 이렇게 두개의 파일을 만들어준다.

CoreData의 Entity와 동일하다고 생각하자.

기존에는 CoreData의 하나의 큰 Container에 여러 Entity가 있었지만

Realm으로 오면서 AppDelegate에 Realm이라는 Container를 만들고, Entity는 Swift 파일로 관리한다고 생각하면 된다.

1
2
3
4
5
6
7
8
9
10
import Foundation
import RealmSwift

class Item: Object {
    
    @objc dynamic var title: String = ""
    @objc dynamic var done: Bool = false
    
}

이렇게 만들어줬더니 갑자기?

CleanShot 2024-04-15 at 11 53 18@2x

중복된 선언이라고 한다. 이게 무슨소리지? 라고 생각할 수 있지만.

이전에 CoreData하면서 만들어두었던 Entity때문이다.

CleanShot 2024-04-15 at 11 57 44@2x

Entity는 만들면서 자연스럽게 NSObject의 class파일을 생성하게된다.

Codegen에 Class Definition으로 해두었기 때문.

이젠 CoreData를 놓아주도록 하자.

2. Relationship 연결하기.

기존에 CoreData로 Relationship을 연결했는데 이제는 Realm에도 연결을 해보도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Category: Object {
    
    @objc dynamic var name: String = ""
    let items = List<Item>()
    
}

class Item: Object {
    
    @objc dynamic var title: String = ""
    @objc dynamic var done: Bool = false
    var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
    
}

Type은 카테고리타입 그 자체를 사용하기에 self가 들어갔다.

2. VC 수정

먼저 CategoryVC에 Realm을 인스턴스화 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
let newCategory = Category() // modified

func saveCategories(category: Category) {
        
        do {
            try realm.write { // modified
                realm.add(category)
            }
        } catch {
            print(error.localizedDescription)
        }
        tableView.reloadData()
    }

에러가 있는 부분을 모두 주석처리하고 add가 잘되는지 확인을 해보도록 하자

CleanShot 2024-04-15 at 12 33 39@2x

저장이 잘 되는걸 알 수 있다.

CRUD 중 Read 구현

1
2
3
4
5
6
7
8
func loadCategories () {
        
        categories = realm.objects(Category.self)
        
        
        
        tableView.reloadData()
    }

categories를 다음과 같이 작성한다.

작성하고 나니 Type Error가 난다.

var categories = [Category]() 이기 때문.

CleanShot 2024-04-15 at 12 40 22@2x

Result 역시 Realm에서 제공하는데, categories를 realm에 맞게 타입변형을 해주면 된다.

var categories: Results<Category>!

우선은 Force Unwrapping을 하는걸로.

그러면 이제 self.categories.append(newCategory)여기 부분에서 에러가 난다.

그전에는 categories가 Category라는 배열이었는데, 배열이 아니기 때문.

그냥 지워주면된다.

Q: 그러면 기존에는 배열에 값을 저장하고 Load할때 배열에 값을 넣어 그 배열에 대한 값을 출력했는데 어떻게 하나?

A: CleanShot 2024-04-15 at 12 44 31@2x auto-updating Container라는것을 우리는 주목해야한다.

실행하니 아까 적었던게 그대로 나온다.

simulator_screenshot_05F8ACA2-82D6-4E46-8499-9D5133E8567D

Simple해졌다.

1. Optional Binding

var categories: Results<Category>? !에서 ?로 바꿔주고 Optional 타입으로 한뒤에,

관련되어있는 부분을 병합연산자 ?? 를 사용하여 nil일때 Default Value를 만들어 준다.

1
2
3
cell.textLabel?.text = categories?[indexPath.row].name ?? "No Categories added Yet"

return categories?.count ?? 1

ViewDidload에서 loadCategory 트리거를 잠시 주석으로 바꾸고 실행하자.

즉 아무것도 없는 nil상태로 출력을 해보면

simulator_screenshot_061011AE-89B9-459C-982A-B25A5103E357

예외처리도 잘 되었다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.