LoginSignup
6
2

More than 3 years have passed since last update.

SwiftUIのListでRealmのデータを削除するとクラッシュする時の直し方

Last updated at Posted at 2020-11-05

Index is out of boundsでクラッシュ!

SwiftUIのListの.onDeleteでperformに下記のメソッドを渡し、

func deleteRow(offsets: IndexSet) {
      guard  let index = offsets.first else {
           return
      }
      let deleteItem = hoges[index]
      try! realm.write {
        realm.delete(deleteItem)
      }
      self.hoges = realm.objects(Hoge.self)
}

試しに、このように記述していたら、 "Index ~ is out of bounds (must be less than ~)." とよく見るエラーでクラッシュしてしまいました。

クラッシュしない書き方

こちらを参考にindexをforEachで回すようにしたらクラッシュしなくなります!

func deleteRow(offsets: IndexSet) {
      offsets.forEach({ index in
       try! realm.write {
         realm.delete(self.hoges[index])
        }
      })
      self.hoges = realm.objects(Hoge.self)
}

追記:
上記で直ったと思っていたのですが、最後尾のデータ削除時のみクラッシュしなくなるだけで、他のcellを消したりすると同様にクラッシュしてしましました。
ググって出た情報を参考にforEachをカスタマイズしたり、データが存在するかどうか確かめるexistメソッドを生やしたりしましたが、どれもうまくいかず、、

Modelを作る

暫定的にこちらの記事を参考に、@objc dynamic var で定義しているRealmのデータと同じ値を定義したModelをstructで作り、ViewModelのイニシャライザでRealmから代入し、Listからは直接Realmを参照せず、Modelから値をとるようにしてクラッシュを回避しました。

import RealmSwift

class Hoge: Object, Identifiable {
    @objc dynamic var id: String = ""
    @objc dynamic var title: String = ""

    override static func primaryKey() -> String? {
        return "id"
    }
}
struct HogeModel {
    let id: String
    let title: String
}

開発規模が大きいとこのやり方も結構厄介かもしれません。

RealmSwiftの方にも似たようなissueが立っているようですが、SwiftUIでも更に使いやすくなると良いですね!
https://github.com/realm/realm-cocoa/issues/6635

Frozen Objectsを使う (20/11/16 追記)

もっと改善できないかと探すうちに、RealmのResultsをimmutableに扱えるFrozenObjectsがあるという事を知りました。
https://realm.io/blog/realm-database-a-new-architecture-and-frozen-objects/

まだ、詳しい使い方を把握できていませんが、わざわざModelを作り直すよりは簡単な方法を見つけました。
self.hoges = realm.objects(Hoge.self)?.freeze()
本当はこのように書きたいのですが、こう宣言して、deleteなどのrealm.writeを使う操作をするとクラッシュします。
なので、暫定的に、このようにimmutableなプロパティーをPublishedにしてイニシャライザや、realm.writeで書き込みをした際に、代入することでクラッシュを回避できるようになりました!

HogeViewModel.swift
  var hoges: Results<Hoge>?
  @Published var freezedHoges: Results<Hoge>?

  let realm = try! Realm()

  init() {
    hoges = realm.objects(Hoge.self)
    freezedHoges = hoges?.freeze()
  }

  func addHoge() {
    let hoge = Hoge()
    hoge.id = NSUUID().uuidString
    hoge.title = "fuga"
    try! realm.write {
      realm.add(hoge)
    }
    freezedHoges = hoges?.freeze()
  }

6
2
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
6
2