Realmを前提としたリポジトリパターンの実装
いろんなところで Realm が良いと聞くようになり最近やっと Realm を使い始めました。実際 Realm を使ってみると予想していたよりも使い勝手がよく Core Data には戻れそうにないです…。ソースコードは Swift3 に対応しています。
リポジトリパターンの実装
Core Data に比べてお作法的なものが少ないとはいえ、いくつかのお決まりのパターンがあるので DDD のリポジトリパターンを適用してみました。クリーンアーキテクチャをまったく意識してないお気軽実装です。
import Foundation import RealmSwift class Repository<DomainType: Object> { var realm: Realm init() { self.realm = try! Realm() } // PK検索 func find(primaryKey: String) -> DomainType? { return realm.objects(DomainType.self).filter("id == %@", primaryKey).first } // 全部取ってくる func findAll() -> [DomainType] { return realm.objects(DomainType.self).map({$0}) } // 条件指定 func find(predicate: NSPredicate) -> Results<DomainType> { return realm.objects(DomainType.self).filter(predicate) } // データ追加と更新 func add(domains: [DomainType]) { try! realm.write { realm.add(domains, update: true) } } // データ削除 func delete(domains: [DomainType]) { try! realm.write { realm.delete(domains) } } // トランザクション func transaction(_ transactionBlock: () -> Void) { try! realm.write { transactionBlock() } } // スレッドをまたいだオブジェクトの解決 func resolve<Confined: ThreadConfined>(_ reference: ThreadSafeReference<Confined>) -> Confined? { return self.realm.resolve(reference) } }
一つのリポジトリオブジェクトが一つの Realm オブジェクトを保持するようにしました。リポジトリオブジェクトをスレッドをまたいで使わない想定です。
リポジトリクラスの使い方
以下の Person クラスを例にリポジトリクラスの使い方をみてみましょう。
class Person: Object { dynamic var id = UUID().uuidString dynamic var name = "" // プライマリーキーを設定 override static func primaryKey() -> String? { return "id" } }
Person オブジェクトの生成から削除まで一通りやってみます。
// パーソンオブジェクト生成 let glassonion = Person() glassonion.name = "glassonion" let jude = Person() jude.name = "jude" // パーソンリポジトリ生成 let repo = Repository<Person>() // データ追加 repo.add(domains: [glassonion, jude]) // 全件取得 let allPeople = repo.findAll() // 条件を指定して取得 let predicate = NSPredicate(format: "name CONTAINS %@", "gl") let people = repo.find(predicate: predicate) // データの更新 repo.transaction { allPeople[0].name = "hoge" } // 削除 repo.delete(domains: allPeople)
スレッドをまたいだモデルオブジェクトのあつかいかた
スレッドをまたいでデータの更新をする場合は以下のようにします。
let repo = Repository<Person>() let person = repo.findAll().first! // スレッドセーフなオブジェクトに変換 let personRef = ThreadSafeReference(to: person) // スレッド生成 DispatchQueue(label: "io.realm.RealmTasks.bg").async { // 新たにリポジトリ生成 let repo = Repository<Person>() // スレッドの違うリポジトリと関連付ける if let person = repo.resolve(personRef)! // データ更新 repo.transaction { person.name = "Michelle" } }