【Swift】Realmのマネージドオブジェクトを生成スレッド外で変更する方法

Swift

こんちには、フリーのITエンジニアでWeb(PHP:Laravel)のバッグエンドをメインにフルリモートでお仕事させて頂きながら、個人開発でiOSアプリを作っているMoritaです。

個人でアプリを作る場合、少しづつ機能を追加していくため、Realmの更新処理が散乱してしまったので、まとめた方法を紹介します。

検証環境

Xcode:11.4.1
端末:iPhone 7 Plus
iOS:13.4.1

今回の問題点

プロトタイプを作成しながら開発をしているので、機能を追加したときにとりあえずRealmの更新処理をその都度書いていたのですが、モデルのプロパティが増加するたびに全ての箇所を変更しなければならず、変更漏れが発生していました。
(Repositoryを作成してその中でRealmのaddメソッド読んでます。)

let rp: AnyRepository<BookModel> = AnyRepository(VocabularyBookRealmRepository())
let model = BookModel()
model.id = self.bookModel!.id
model.name = self.bookModel!.name
model.frontTextPosition = self.bookModel!.frontTextPosition
model.backTextPosition = self.bookModel!.backTextPosition
model.commentTextPosition = self.bookModel!.commentTextPosition
model.frontTextSize = self.bookModel!.frontTextSize
model.backTextSize = self.bookModel!.backTextSize
model.commentTextSize = self.bookModel!.commentTextSize
model.created_at = self.bookModel!.created_at
model.updated_at = Date()
rp.update(domains: [model])

改善方法

Managerクラスを作成して、その中で更新処理を呼ぶ方法に変更しました。

    static func update(bookModel: BookModel) {
        let rp: AnyRepository<VocabularyBookModel> = AnyRepository(VocabularyBookRealmRepository())
        let model = VocabularyBookModel()
        model.id = bookModel.id
        model.name = bookModel.name
        model.frontTextPosition = bookModel.frontTextPosition
        model.backTextPosition = bookModel.backTextPosition
        model.commentTextPosition = bookModel.commentTextPosition
        model.frontTextSize = bookModel.frontTextSize
        model.backTextSize = bookModel.backTextSize
        model.commentTextSize = bookModel.commentTextSize
        model.created_at = bookModel.created_at
        model.updated_at = bookModel.updated_at
        model.deleted_at = bookModel.deleted_at
        rp.update(domains: [model])
    }

呼び出し元で保持しているRealmのモデルのオブジェクトのプロパティを変更して、
このManagerクラスのupdateメソッドに渡して更新しようと考えたのですが、
保持しているRealmのモデルオブジェクトはマネージドオブジェクトになっているため、witeメソッド内で変更しなければエラーが発生します。

そこで、モデルオブジェクトにNSCopyingを継承させて、copyメソッドの中でインスタントの入れ替えをするようにしました。

class BookModel: Object, NSCopying {
    @objc dynamic var id: String = NSUUID().uuidString
    @objc dynamic var name: String?
    @objc dynamic var deleted_at: Date?
    @objc dynamic var created_at: Date?
    @objc dynamic var updated_at: Date?
    let vocabularies = List<VocabularyModel>()
    func copy(with zone: NSZone? = nil) -> Any {
        let model = VocabularyBookModel()
        model.id = id
        model.name = name
        model.frontTextPosition = frontTextPosition
        model.backTextPosition = backTextPosition
        model.commentTextPosition = commentTextPosition
        model.frontTextSize = frontTextSize
        model.backTextSize = backTextSize
        model.commentTextSize = commentTextSize
        model.created_at = created_at
        model.updated_at = updated_at
        model.deleted_at = deleted_at
        let vocabulaiesRow = vocabularies.map{$0}
        let newVocabularies = vocabulaiesRow.map{
            vocabulary -> VocabularyModel in
            let vocabularyModel = VocabularyModel()
            vocabularyModel.id = vocabulary.id
            vocabularyModel.book_id = vocabulary.book_id
            vocabularyModel.front = vocabulary.front
            vocabularyModel.back = vocabulary.back
            vocabularyModel.comment = vocabulary.comment
            vocabularyModel.isCheck = vocabulary.isCheck
            vocabularyModel.isFavorito = vocabulary.isFavorito
            vocabularyModel.deleted_at = vocabulary.deleted_at
            vocabularyModel.created_at = vocabulary.created_at
            vocabularyModel.updated_at = vocabulary.updated_at
            return vocabularyModel
        }
        newVocabularies.forEach{ model.vocabularies.append($0) }
        return model
    }

プロパティにListを持っている場合も取り出して、再度モデルオブジェクトに入れ直したものでListを作成しています。

呼び出しもとは以下のようになります。
呼び出し元で保持していたモデルオブジェクトに対してcopyメソッドを使用して再度モデルオブジェクトを作成しています。

let model = self.bookModel?.copy() as! BookModel
model.name = "test"
RealmManager.update(bookModel: model)

まとめ

めんどくさくなてRealmの更新処理をコピペで量産していたら、プロパティのコピー忘れでデータ不整合が起きて無駄に原因調査の時間がかかってしまいました。
やばそうだと思った時に早めに対応しておく方が後々やっぱりいいですね。

RealmのObjectを簡単にコピーする方法 - Qiita
ReaalmのObject(RLMObject)をコピーするのに手こずったのでまとめました。コピーすればスタンドアロンに戻るのでプロパティを操作できます。方法は簡単でNSCopyingのプロトコル…

コメント

タイトルとURLをコピーしました