Вот что я узнал из этого упражнения, которое было вызвано предупреждающим сообщением: в какой-то момент Core Data по умолчанию будет использовать NSSecureUnarchiveFromData, когда указано nil, и трансформируемые свойства, содержащие классы, которые не поддерживают NSSecureCoding, станут нечитаемыми.
Мое приложение собирало серию точек [CGPoint], созданных путем рисования на экране с помощью Apple Pencil или пальца, и сохраняло их в CoreData — по сути, это сердце того, что я назвал Scribble. Для хранения в CoreData я создал атрибут с именем «points» и установил тип Transformable. Пользовательский класс был установлен на [CGPoint]. Кроме того, я установил для CodeGen значение «Вручную», а не автоматическую опцию «Определение класса». Когда я сгенерировал файлы подкласса управляемых объектов CoreData, он генерирует файл +CoreDataClass.swift с критической интересующей строкой:
@NSManaged public var points: [CGPoint]?
Следует отметить, что на самом деле существует проблема, если вы используете автоматический параметр, поскольку сгенерированный файл не знает, что такое CGPoint, и его нельзя отредактировать, чтобы добавить импорт для UIKit, чтобы найти определение.
Это работало нормально, пока Apple не захотела поощрять безопасное кодирование. В приведенном ниже файле кода я разработал объект ScribblePoints для работы с кодировкой и связанным с ней преобразователем данных.
//
// ScribblePoints.swift
//
import Foundation
import UIKit
public class ScribblePoints: NSObject, NSCoding {
var points: [CGPoint] = []
enum Key: String {
case points = "points"
}
init(points: [CGPoint]) {
self.points = points
}
public func encode(with coder: NSCoder) {
coder.encode(points, forKey: Key.points.rawValue)
}
public required convenience init?(coder: NSCoder) {
if let sPts = coder.decodeObject(of: ScribblePoints.self, forKey: Key.points.rawValue) {
self.init(points: sPts.points)
} else {
return nil
}
}
}
extension ScribblePoints : NSSecureCoding {
public static var supportsSecureCoding = true
}
@available(iOS 12.0, *)
@objc(ScribblePointsValueTransformer)
final class ScribblePointsValueTransformer: NSSecureUnarchiveFromDataTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: ScribblePointsValueTransformer.self))
override static var allowedTopLevelClasses: [AnyClass] {
return [ScribblePoints.self]
}
public static func register() {
let transformer = ScribblePointsValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
override class func allowsReverseTransformation() -> Bool {
return true
}
override func transformedValue(_ value: Any?) -> Any? {
if let data = value as? Data {
// Following deprecated at iOS12:
// if let data = value as? Data {
// if let points = NSKeyedUnarchiver.unarchiveObject(with: data) as? [CGPoint] {
// return points
// }
// }
do {
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
unarchiver.requiresSecureCoding = false
let decodeResult = unarchiver.decodeObject(of: [NSArray.self, ScribblePoints.self], forKey: NSKeyedArchiveRootObjectKey)
if let points = decodeResult as? [CGPoint] {
return points
}
} catch {
}
}
return nil
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
if let points = value as? [CGPoint] {
// Following deprecated at iOS12:
// let data = NSKeyedArchiver.archivedData(withRootObject: points)
// return data
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: points, requiringSecureCoding: true)
return data
} catch {
}
}
return nil
}
}
Имея все вышеперечисленное, я смог, наконец, заполнить ScribblePointsValueTransformer для имени Transformer для атрибута «points» в CoreData.
Также можно переключить пользовательский класс с [CGPoint] на ScribblePoints. Похоже, это не влияет на выполнение кода. Однако, если вы повторно сгенерируете файл +CoreDataClass.swift, критической интересующей строкой станет:
@NSManaged public var points: ScribblePoints?
и когда вы перекомпилируете, вам нужно будет внести изменения в код, чтобы справиться с другим определением. Если вы начинали с нуля, возможно, вы захотите просто использовать определение ScribblePoints и избежать неприятностей, связанных с NSArrays и NSPoints и другими вещами, с которыми вы волшебным образом сталкиваетесь странными способами с [CGPoint].
Выше было со Swift 5.
person
anorskdev
schedule
10.05.2021