LoginSignup
50
61

More than 5 years have passed since last update.

2017年版Realmのエレガントな使い方

Last updated at Posted at 2017-04-26

はじめに

Realmと言えば、とにかく高速なモバイルDBという感じでしたが、最近では「双方向編集」、「リアルタイム同期」など、公式サイトのキャッチフレーズも変わってきてます。2017年版のハマりポイントと、エレガントな使い方のパターンの紹介です

1.検索してから更新する

下記のように、RLMResultsをインスタンス変数に持った状態で、HogeHogeオブジェクトを追加すると、resultsに自動的に追加されます。

self.results = HogeHoge.allObjects()

// 適当にオブジェクトを生成
 let realm = RLMRealm.default()
 try realm.transaction {
    let hoge = HogeHoge()
    realm.addOrUpdate(draft)
 }

self.results.count // 増えてる

逆に、トランザクションの直後に再度取得した場合、反映されないことがありました。

 let realm = RLMRealm.default()
 try realm.transaction {
    let hoge = HogeHoge()
    realm.addOrUpdate(draft)
 }
let results = HogeHoge.allObjects() //書いたオブジェクトがない!!!!

またRLMResultsには更新された際に、発火するブロックを追加できます。

let token = results.addNotificationBlock({[weak self] (results, change, error) in
}

まとめると、Realmで更新直後に、更新内容を含む、データを取得したい場合は

// 1.検索する
self.results = HogeHoge.allObjects()


// 2.更新された際のブロックを追加

self.results.addNotificationBlock({[weak self] (results, change, error) in

}

// 3.更新する
 let realm = RLMRealm.default()
 try realm.transaction {
    let hoge = HogeHoge()
    realm.addOrUpdate(draft)
 }

とするとできます。

2.TableView/CollectionView パターン

TableViewとCollectionViewの場合の実装パターンです

1.viewDidLoadで、RLMResultsをインスタンス変数に持ち、更新フックで、reloadDataを呼び出します

override func viewDidLoad() {
   self.datas = HogeHoge.allObjects() 
   self.datas.addNotificationBlock({[weak self] (results, change, error) in
      self.collectionView.reloadData()
   }
}

2.DataSourceで、RLMResultsを使って、行数を返します

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
   return self.datas.count
}

このように実装すると、データが更新された際に、ビューが自動的に更新されます。

3.PageViewControllerパターン

1ページを表すUIViewControllerにRLMObjectをコピーしたstructを作成し、structのインスタンスを変数として持ちます。RLMObjectを直接保持すると、そのRLMObjectが削除された後に、変数にアクセスするとAssertionErrorが発生します。

class FooViewController: UIViewController {
   var hoge: HogeHogeWrapper!
}

次にDataSourceクラスにて下記のように実装します。

var datas: RLMResults<RLMObject>

override init() {
   datas = HogeHoge.allObjects()
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

   //UIViewControllerから現在のオブジェクトを取得
   guard let hoge  = (viewController as? FooViewController)?.hoge else { return nil }

   //indexを求める
   let index = indexOf(medium: hoge)
   if index <= 0 { return nil}

   //隣のオブジェクトをセットする
   controller.hoge = datas.object(at: (index-1)) as! Hoge
   return controller
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
   guard let hoge  = (viewController as? FooViewController)?.hoge else { return nil }
   let index = indexOf(medium: hoge)
   if (index+1) >= datas.count { return nil }
   controller.hoge = datas.object(at: (index+1)) as! Hoge
   return controller 
}

今のコントローラの持つ、オブジェクトを元に、RLMResultsのindexを求めることで、データが追加された際に、自動的に追従できるようになります。ただしこのままでは、ページ1とページ2がある場合に、データがページの間に追加された場合に、追従しないため更新フックを使って、ページビューを再作成する必要がありますが、大体のケースでは機能します。

現場からは以上です。

50
61
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
61