[TIL #23] 2023 / 05 / 16
Realm 사용 시, 유용한 코드들을 정리하려고 합니다.
Realm 모델 만드는 방법
기존에는 @objc Dynamic를 사용했는데, 이제는 @Persisted를 사용한다고 하네요.
@Persisted은 Realm에서 만든 일종의 프로퍼티 래퍼(미리 어떤 행동을 할지 선언과 동시에 정하는 기능)입니다.
// Object은 hashable로 동작합니다.
class RealmModel: Object {
// primaryKey 일종의 UUID라고 생각하면 됩니다. (ObjectId 대신 UUID를 사용해도 됩니다)
// 중복 방지하고 CRUD에서 원하는 데이터에 접근하기 위해 사용합니다.
@Persisted(primaryKey: true) var id: ObjectId
@Persisted var name: String?
@Persisted var gender: String?
// 생성자를 만들어 주고 싶으면 이와 같이 convenience를 사용하고 self.init() 후 사용
convenience init(name: String? = nil, gender: String? = nil) {
self.init()
self.name = name
self.gender = gender
}
}
CRUD 사용하기
모델을 만들어 봤으니, CRUD 방법을 간단히 알아봅시다.
Realm을 사용하기 위해 Realm 객체를 만들어주고, 또한 사용할 모델 객체도 만들어 줍시다.
var realm = try! Realm()
var model1 = RealmModel(name: "Danny", gender: "Male")
var model2 = RealmModel(name: "Jane", gender: "Female")
생성, 수정, 삭제 시 무조건 write 메서드의 컴플리션 블록 안쪽에서 정의를 해줘야 합니다.
Create
단일, 여러 모델을 추가가 가능합니다.
func createRealm() {
do {
try realm.write {
realm.add(model1)
// 이렇게 배열로 묶어서 여러개 추가도 가능하다
// realm.add([model1, model2])
}
} catch {
print("Failed to create")
}
}
Read
원하는 특정 데이터 및 모든 모델 데이터 찾기
// 1. PrimaryKey를 통해 특정 모델 데이터 찾기
func loadRealm() {
// 1. PrimaryKey를 통해 특정 모델 데이터 찾기
let primaryKey = model1.id
let model = realm.object(ofType: RealmModel.self, forPrimaryKey: primaryKey)
}
// 2. 모든 모델 데이터 얻기
func loadRealm() {
let models = realm.objects(RealmModel.self)
}
Update
기본 사용 방법은 아래와 같습니다.
func updateRealm() {
let firstModel = realm.objects(RealmModel.self).first!
do {
try realm.write {
firstModel.name = "대니"
firstModel.gender = "남자"
// Key-Value 값으로 변경도 가능
// firstModel.setValue("대니", forKey: "name")
// firstModel.setValue("남자", forKey: "gender")
}
} catch {
print("Failed to update")
}
}
두 번째 방법, 이미 저장 된 객체가 존재하고 primaryKey를 알고 있다면, primaryKey를 갖고 업데이트도 가능합니다.
참고. 같은 primaryKey(id)로 새로운 모델을 만들어 저장하면 앱이 터집니다.
(여긴, "add(newModel, update: .modified)"를 사용합니다)
func updateRealmWithPrimaryKey() {
let primaryKey = model1.id
let newModel = RealmModel(value: ["id": primaryKey,
"name": "대니",
"gender": "남자"])
do {
try realm.write {
realm.add(newModel, update: .modified)
}
} catch {
print("Failed to update")
}
}
다시, 간단하게 말해서 primaryKey를 알고 있다면, 업데이트 방법은 Create와 거의 동일하며
다만, add 메서드에서 update가 포함돼 있는 메서드를 사용해 주면 됩니다.
realm.add(newModel, update: .modified)
Delete
모델을 불러와서 삭제하기 및 전체 삭제
만약, PrimaryKey를 알고 있다면 특정 모델을 불러와서 지워주면 되겠죠?
func deleteRealm() {
let firstModel = realm.objects(RealmModel.self).first!
do {
try realm.write {
realm.delete(firstModel)
// 모든 모델 데이터를 삭제하는 방법
// realm.deleteAll()
}
} catch {
print("Failed to delete")
}
}
필터 기능
where절을 사용하여, Swift의 거의 모든 논리(술어)를 사용할 수 있습니다.
where절 안쪽에서 아래 기능들을 사용할 수 있습니다.
// Prefix
NOT ! swift let results = realm.objects(Person.self).query { !$0.dogsName.contains("Fido") || !$0.name.contains("Foo") }
// Comparisions
Equals ==
Not Equals !=
Greater Than >
Less Than <
Greater Than or Equal >=
Less Than or Equal <=
Between .contains(_ range:)
// Collections
IN .contains(_ element:)
Between .contains(_ range:)
// Map
@allKeys .keys
@allValues .values
// Compound
AND &&
OR ||
// Collection Aggregation
@avg .avg
@min .min
@max .max
@sum .sum
@count .count swift let results = realm.objects(Person.self).query { !$0.dogs.age.avg >= 0 || !$0.dogsAgesArray.avg >= 0 }
// Other
NOT !
Subquery ($0.fooList.intCol >= 5).count > n
간단히 Where를 사용해 비교하는 예제
func filterRealm() {
// 전체 모델에서 name이 Danny인 데이터만 필터링
let filterModels = realm.objects(RealmModel.self).where {
$0.name == "Danny"
}
print(filterModels)
}
추가로 sort도 가능합니다.
모델들의 name을 비교해서 오름차순으로 정렬하기
func sortRealm() {
let models = realm.objects(RealmModel.self)
let sortedModels = models.sorted(byKeyPath: "name", ascending: true)
print(sortedModels)
}
Realm의 Data Base 파일 자체를 삭제하기
파일을 FInder에서 찾아서 지우는 방법 말고,
Xcode 내장 클래스인 FileManager를 이용하여 코드로 파일을 지우는 방법이 있습니다.
// 저장된 DB 파일 경로
print(Realm.Configuration.defaultConfiguration.fileURL)
// 프린트를 찍어보면 (file:///Users/.../Documents/default.realm) 이와 같이 경로가 찍힙니다.
// 이 경로상에서 제거할 파일의 확장자 추가를 하고 FileManager를 통해 파일을 삭제합니다.
let realmURL = Realm.Configuration.defaultConfiguration.fileURL!
let realmURLs = [
realmURL,
realmURL.appendingPathExtension("lock"),
realmURL.appendingPathExtension("management")
]
for URL in realmURLs {
do {
// 파일 제거하기
try FileManager.default.removeItem(at: URL)
} catch {
print("Faild to delete file")
}
}
참고 - https://pipe0502.tistory.com/entry/Realm-Swift
데이터 읽을 때, Results 타입
이와 같이 데이터를 읽을 때, Results 타입으로 모델이 만들어집니다.
여기서 Results는 Lazy 하게 동작합니다.
let models: Results<RealmModel> = realm.objects(RealmModel.self)
Lazy 하게 동작하는 이유
Realm은 데이터베이스에서 데이터를 효율적으로 로드하고 처리하기 위해서,
Lazy로 동작하게 만들어 줬다고 합니다.
여기서 문제
테이블뷰나 컬렉션뷰에 데이터를 뿌려주는 건 문제가 없지만,
만약 map이나 filtter으로 변경하게 되면, LazySequence로 타입이 변경됩니다.
let filterModels: LazyFilterSequence<Results<RealmModel>> = models.filter { $0.gender == "남자"}
복잡해 보이는 타입인데요...
그런데 해결방법은 단순하더라고요.
RealmModel 타입을 사용하려면 그냥 Array로 캐스팅을 해주면 해결됩니다.
let filterModels: LazyFilterSequence<Results<RealmModel>> = models.filter { $0.gender == "남자"}
let models: [RealmModel] = Array(filterModels)
계속 업데이트 중...
'프로젝트' 카테고리의 다른 글
[Swift/ TIL #25] DispatchGroup, DispatchSemaphore (추가로 async/await) (0) | 2023.05.31 |
---|---|
[Swift/ TIL #24] FileManager를 통해 이미지 저장 (0) | 2023.05.17 |
[Swift/ TIL #22] LocationManager 사용 (0) | 2023.05.11 |
[Swift/TIL #21] 키보드가 화면을 가릴 때, Y축으로 뷰 이동 방법 (0) | 2023.05.09 |
[Swift/TIL #20] 이미지 메모리 최적화 방법들 (WWDC 18) (0) | 2023.05.02 |