概要
CoreData noobということもあって、AttributeのTypeに標準で用意されていない型をTransformableを使用して保存したい時に戸惑ってしまったので、備忘録として残しておきます。
恐らく、正解の流れとしては
- 保存したい自作クラスを定義
- そのクラスをNSCodingプロトコルに準拠させる
- エンコード処理とデコード処理の実装
- EntityのTypeをTransformableに設定する
です。以下で詳しく述べます。
Swiftのバージョンは4.1です。
自作クラスの定義
class User: NSManagedObject {
@NSManaged var name: String
@NSManaged var pet: Pet
}
class Pet {
var name: String?
var gender: Gender?
}
enum Gender: Int {
case male = 0
case female
}
上記は、今回扱うデータモデルです。_User_は名前と飼っているペットを持ち、_Pet_は名前と性別を持っていることにします。_User_クラスをCoreDataのEntityとして扱う為、_NSManagedObject_を継承させ、プロパティには@NSManaged
を付けています。
この記事のゴールは、___pet_プロパティをAttributeとして保存できるようにすること__です。
さて、上記のコードをそのまま書くと、「_pet_プロパティは@NSManaged
を使えんぞ!」と怒られてしまいます。どうやら、Obj-Cで表現できるクラスでなければならないようです。ですので、_Pet_クラスに_NSObject_を継承させます。
...
class Pet: NSObject {
var name: String?
var gender: Gender?
}
...
これで怒られなくなりますが、まだCoreDataで扱える形にはなっていません。
NSCodingに準拠させる
続いて、_Pet_クラスを_NSCoding_に準拠させ、エンコード処理とデコード処理を実装します。
...
class Pet: NSObject, NSCoding {
var name: String?
var gender: Gender?
func encode(with aCoder: NSCoder) {
aCoder.encode(self.name, forKey: "Name")
aCoder.encode(self.gender!.rawValue, forKey: "Gender")
}
required init?(coder aDecoder: NSCoder) {
super.init()
self.name = aDecoder.decodeObject(forKey: "Name") as? String
let genderValue = aDecoder.decodeInteger(forKey: "Gender")
self.gender = Gender(rawValue: genderValue)
}
}
...
これで_Pet_クラスをCoreDataで扱えるようになりました!
ここで注意すべき(僕が躓いた)ところは、enumもそのまま保存できるわけではないので、一旦_rawValue_を介してエンコード/デコードする必要があるところです。当然といえば当然なのですが...
Transformableに設定
最後に、AttributeのTypeをTransformableに変更して完了です。
どうでもいいですけど、"AttributeのTypeをTransformableにする"って文章ヤバイですね。ヤバイ...(語彙力0)