willSave()はsetPrimitiveValue(:forKey)を使え - ひとしれずひっそり

ひとしれずひっそり

主にソフトに関することをメモしていきます。過程をそのまま書いていたりするので間違いが含まれます。鵜呑みしない様に。

willSave()はsetPrimitiveValue(:forKey)を使え

Core Dataを使っていて保存前に作成時刻や更新時刻を記録したい時にwillSaveのタイミングで記録できる。

developer.apple.com

とりあえずカスタムクラスを作成して、willSaveで普通にプロパティで設定してみる。

import Foundation
import CoreData

@objc(Item)
public class Item: NSManagedObject {

    override public func willSave() {
        super.willSave()
        
        let now = Date()
        if createdAt == nil {
            createdAt = now
        }
        updatedAt = now
    }
}

するとオプジェクト作成時にExceptionが出てクラッシュしてしまった。

プロパティを変更するとchangeフラグがセットされて再帰的にwillSave()が呼び出されクラッシュに至った。

createdAtはnilの時だけしかセットしないので、1回目はchangeフラグが立つが2回目は立たないので、このままでも問題ない。
updatedAtは毎回changeフラグが立ってしまうので問題がある。
試しにupdatedAt = nowの行をコメントアウトするとオブジェクト作成可能である。

@objc(Item)
public class Item: NSManagedObject {

    override public func willSave() {
        super.willSave()
        
        let now = Date()
        if createdAt == nil {
            createdAt = now
        }
        // updatedAt = now
    }
}

updatedAtはsetPrimitiveValue(:forKey:)で対応する。
setPrimitiveValue()はchangeフラグを立てない。

以前hasChangesにならないので悩んだがこれではっきりした。

katsuyoshi.hatenadiary.com

@objc(Item)
public class Item: NSManagedObject {

    override public func willSave() {
        super.willSave()
        
        let now = Date()
        if createdAt == nil {
            createdAt = now
        }
        setPrimitiveValue(now, forKey: "updatedAt")

    }
}

今度はupdatedAtでも問題ない。

createdAtも2回呼び出されるのでsetPrimitiveValue()を使う方がいいだろう。

developer.apple.com

コードはこちら
CoreDataのwillSave()を使う際はsetPrimitiveValue(:forkey:)を使う · GitHub