(なぜか限定公開でPublicにしてなかったので今頃公開。基本的な考え方は今でも通じるはず)
なぜ考える必要があるのか
- 保存されたRealm object(Managed Realm object)はスレッドを直接またげなかったり、変更にはtransactionが必要だったりと気をつけることが多いです。
- そのため、運用ルール以外の方法で安全にRealm objectを取り回す方法を考える必要があります。
VOを何にするか
Realm object(DAO)をVOに変換すれば安全に取り回すことができるので、VOをどうするかを考えます。
1. struct
定義例
protocol PersonType {
var name: String { get }
var age: Int { get }
}
@objcMembers
class Person: Object, PersonType {
dynamic var name = ""
dynamic var age = 0
}
struct PersonObject: PersonType {
let name: String
let age: Int
}
- Realm objectとstructの相互変換する方法が必要
- Realm objectとstructで同一の定義が必要で冗長
2. Unmanaged Realm objectをprotocolで返す
定義例
protocol PersonType {
var name: String { get }
var age: Int { get }
}
@objcMembers
class Person: Object, PersonType {
dynamic var name = ""
dynamic var age = 0
}
- Managed Realm objectをUnmanaged Realm objectにする方法が必要
- setterを含むProtocolも返せばDTOとすることも可能
相互変換の方法
方法 | デメリット |
---|---|
変換コードを自前で書く | 変換コードのメンテナンスコストが高く、ミスが入る可能性も高い |
Codable経由で変換 | Decode/Encode分の処理コスト増、Codableに準拠するためのコードが必要 |
Extra/Realmのdetached() | 実装はディープコピー。ただ、ユニットテストが書かれていないので不安 |
fetchしたオブジェクトは正常にリフレクションできなかったので除外 | |
EasyRealmのunmanaged() | 実装はディープコピー。RealmSwiftのバージョンがEasyRealm.podspecに依存してしまう |
有力候補
EasyRealm
- ユニットテストあり。
let trainer = Trainer()
let pokedex = Pokedex()
trainer.pokemons.append(HelpPokemon.generateCapturedRandomPokemon())
trainer.pokedex = pokedex
trainer.pokemons.forEach {
$0.specialBoost.value = 42
}
try! trainer.er.save(update: true)
let managed = trainer.er.managed!
XCTAssertTrue(managed.er.isManaged)
XCTAssertTrue(managed.pokedex!.er.isManaged)
XCTAssertFalse(managed.pokemons.isEmpty)
managed.pokemons.forEach {
XCTAssertTrue($0.er.isManaged)
XCTAssertEqual($0.specialBoost.value!, 42)
}
let unmanaged = managed.er.unmanaged
XCTAssertFalse(unmanaged.er.isManaged)
XCTAssertFalse(unmanaged.pokedex!.er.isManaged)
XCTAssertFalse(unmanaged.pokemons.isEmpty)
unmanaged.pokemons.forEach {
XCTAssertFalse($0.er.isManaged)
XCTAssertEqual($0.specialBoost.value!, 42)
}
まとめ
- 公式がサポートするまで何らかの代替方法で乗り切るしかありません。
- ちなみにrealm-javaにはcopyFromRealm()があります。
- どの方法も変換コストがかかるのでパフォーマンスとのトレードオフになります。パフォーマンスを求めたい部分はRealm objectやResults等を直接扱って気をつけて運用するしかないのかなと思います。