Core Dataのエンティティが持つ属性 (attribute) の型は整数、文字列、日付などいくつかの決まったものしかとることができませんが、Transformableを指定することで任意の型を NSData
に変換して保存することができるようになっています。このとき保存したい型と NSData
の変換を担うのがvalue transformerです。
Core Dataのtransformableを利用しているプロジェクトをXcode 12でビルドしたときに、以下のような警告が出るようになりました。
warning: Misconfigured Property: <NSManagedObjectのサブクラス>.<プロパティ> is using a nil or insecure value transformer. Please switch to NSSecureUnarchiveFromDataTransformerName or a custom NSValueTransformer subclass of NSSecureUnarchiveFromDataTransformer
これは、これまでのvalue transformerが NSCoding
を使ったものであるため非推奨になっており、 NSSecureCoding
をベースとした NSSecureUnarchiveFromDataTransformer
に置き換える必要があるということです。
今回この警告の対応のために調べた、 NSSecureUnarchiveFromDataTransformer
を使ってtransformableな属性をセキュアに実装する方法をまとめました。
カスタムクラスが不要なパターン
NSSecureUnarchiveFromDataTransformer
が扱える型は allowedTopLevelClasses
で規定されていて、以下の通りです。
NSArray, NSDictionary, NSSet, NSString, NSNumber, NSDate, NSData, NSURL, NSUUID, NSNull
これらの型の組み合わせで表現できるもの、例えば文字列の配列 [String]
の属性であれば、モデルエディタでTransformerの欄に NSSecureUnarchiveFromDataTransformerName
を指定するだけでOKです。
カスタムクラスが必要なパターン
上に挙げた型以外の NSSecureCoding
に準拠した標準の型(UIColor
など)や、NSSecureCoding
に準拠した独自の型を使う場合は、 NSSecureUnarchiveFromDataTransformer
を継承したカスタムクラスを実装する必要があります。
// Core Dataで利用するために @objc が必要です
@objc final class SecureValueTransformer: NSSecureUnarchiveFromDataTransformer {
// クラス名をvalue transformerのnameとします
static let name = NSValueTransformerName(rawValue: String(describing: SecureValueTransformer.self))
// superが返すクラスの配列に追加する形で使用するクラスを指定します
override static var allowedTopLevelClasses: [AnyClass] {
super.allowedTopLevelClasses + [
UIColor.self,
NSTimeZone.self,
CustomTypeA.self,
CustomTypeB.self,
]
}
// value transformerを登録する処理です。Core Data Stackの初期化前に呼びます。
public static func register() {
let transformer = SecureValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
モデルエディタではTransformerの欄に name
で指定したクラス名を入力します。
最後にCore Data Stackの初期化前に以下を実行して、value transformerを登録します。
SecureValueTransformer.register()