Edited at

CoreDataで自作クラスのAttributeを保存する方法


概要

CoreData noobということもあって、AttributeのTypeに標準で用意されていない型をTransformableを使用して保存したい時に戸惑ってしまったので、備忘録として残しておきます。

恐らく、正解の流れとしては


  1. 保存したい自作クラスを定義

  2. そのクラスをNSCodingプロトコルに準拠させる

  3. エンコード処理とデコード処理の実装

  4. EntityのTypeをTransformableに設定する

です。以下で詳しく述べます。

Swiftのバージョンは4.1です。


自作クラスの定義


User.swift

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を継承させます。


User.swift

...

class Pet: NSObject {
var name: String?
var gender: Gender?
}

...


これで怒られなくなりますが、まだCoreDataで扱える形にはなっていません。


NSCodingに準拠させる

続いて、PetクラスをNSCodingに準拠させ、エンコード処理とデコード処理を実装します。


User.swift

...

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に変更して完了です。

スクリーンショット 2018-09-13 13.07.34.png

どうでもいいですけど、"AttributeのTypeをTransformableにする"って文章ヤバイですね。ヤバイ...(語彙力0)