#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 ~)." とよく見るエラーでクラッシュしてしまいました。
#クラッシュしない書き方
https://stackoverflow.com/questions/61165998/how-to-delete-data-from-swiftui-list-and-realm
こちらを参考に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で書き込みをした際に、代入することでクラッシュを回避できるようになりました!
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()
}