NSSecureCoding and transformable
在iOS 12中,苹果已经开始在整个平台上采用NSSecureCoding。对于Core Data,这意味着默认的ValueTransformer(它使用NSCoding将自定义数据类型转换为可存储在持久存储中的格式)在某个时刻也会发生变化。
因此,您可能会在项目中看到以下警告:
CoreData: One or more models in this application are using
transformable properties with transformer names that are either
unset, or set to NSKeyedUnarchiveFromDataTransformerName.
Please switch to using “NSSecureUnarchiveFromData” or a subclass
of NSSecureUnarchiveFromDataTransformer instead. At some point,
Core Data will default to using “NSSecureUnarchiveFromData” when
nil is specified, and transformable properties containing classes that
do not support NSSecureCoding will become unreadable.
本周,让我们来看看这个警告的含义以及如何解决它。我们将从可转换属性和对象序列化的一些概念开始。
Transformable properties
开箱即用的Core Data支持各种数据类型,例如,整数、布尔值、字符串等。在大多数情况下,这就足够了,但有时你想存储不同类型的数据,例如UIColor。您可以通过创建Transformable属性来做到这一点。当将属性声明为Transformable Core Data时,将自定义数据类型保存到持久存储时转换为二进制数据,并从存储中提取时转换回自定义数据类型。它通过一个值转换器来实现这一点。
ValueTransformer只是一个将一个值转换为另一个值的类。当您没有为「可转换属性」定义「自定义转换」 时,Core Data将使用默认的转换。默认的转换器与NSCoding一起工作,NSCoding是在自定义数据类型上实现的协议,因此可以对其进行编码和解码。编码和解码本身是由NSKeyedArchiver和NSKeyedUnarchiver完成的,这符合NSCoder。正因为如此,默认的转换器工作在任何符合NSCoding协议的类型上。
NSSecureCoding
NSSecureCoding通过允许您在前面传入类型信息来解决这个问题,这样归档程序就可以在取消归档之前进行类检查。你可以通过NSKeyedUnarchiver上的unarchivedObject(ofClass:from:)方法来做到这一点。如果在解归档过程中它遇到了不同类型的类或不符合NSSecureCoding的类,它将停止并抛出异常。这将防止不好的事情发生。
Solving the warning
现在让我们回到警告。在第一句话中,它告诉我们,我们的数据模型包含一个或多个具有可转换属性的实体,该属性使用默认值转换器。要么是因为我们没有在数据模型检查器的transformer字段中指定任何东西,要么是因为它被设置为NSKeyedUnarchiveFromDataTransformerName。
valuetransformer-data-inspector.png [图片上传中...(valuetransformer-data-inspector.png-dadc2b-1666275811301-0)]
然后它告诉我们,在未来这将默认为NSSecureUnarchiveFromData转换器,它与NSSecureCoding一起工作。当这种情况发生时,所有不采用安全编码的可转换属性都将变得不可读。
Conform to the NSSecureCoding protocol:
final class MyCustomObject: NSSecureCoding {
static var supportsSecureCoding = true
}
用decodeObjectOfClass:forKey:替换init(coder:)实现中对decodeObject(forKey:)的所有调用。
Setting a ValueTransformer
新的默认值转换器名为NSSecureUnarchiveFromData,开箱即用支持所有plist类型,例如NSDate和NSString。因此,如果你的transformable属性符合这个要求,你可以开始使用新的默认转换如下:
secure-coding-value-transformer.png
对于非plist类型,例如UIColor, Core Data将无法找出unarchiver应该检查的类,并将抛出异常。因为Swift不允许我们捕捉Objective-c和c++异常,这将使我们的应用程序崩溃,并出现一个fatalError。我们可以通过创建一个自定义值转换器来解决这个问题。下面是一个简单的实现示例:
// 1. Subclass from `NSSecureUnarchiveFromDataTransformer`
@objc(UIColorValueTransformer)
final class ColorValueTransformer: NSSecureUnarchiveFromDataTransformer {
/// The name of the transformer. This is the name used to register the transformer using `ValueTransformer.setValueTrandformer(_"forName:)`.
static let name = NSValueTransformerName(rawValue: String(describing: ColorValueTransformer.self))
// 2. Make sure `UIColor` is in the allowed class list.
override static var allowedTopLevelClasses: [AnyClass] {
return [UIColor.self]
}
/// Registers the transformer.
public static func register() {
let transformer = ColorValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
事情是这样的:
-
就像默认的转换器一样,我们从NSSecureUnarchiveFromDataTransformer继承子类。我们需要使用@objc属性来指示Swift使这个类在Objective-C中可访问和可用。否则Core Data将不能使用value transformer,因为框架是用Objective-C编写的。
-
确保UIColor在 allowed class list 中。当归档或取消归档数据时,归档程序将根据此列表进行检查。
你需要做的下一件事是确保Core Data知道它应该为你的可转换属性使用ColorValueTransformer。
最后一步是通过我们创建的 public static func register() 类方法,确保 value transformer可用。A good time to do this is right before setting up the persistent container.
结论
现在您已经了解了所有这些工作原理,可以继续进行并修复项目中的警告。在执行此操作时,最好更新任何现有的值转换器,以便它们也使用NSSecureCoding。这样做会让你的应用程序更安全。
本文为翻译搬运,关注原文:
https://www.kairadiagne.com/2020/01/13/nssecurecoding-and-transformable-properties-in-core-data.html