概要
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)