我正在寻找使用 Core Data 存储 MKPolyline 属性,但我能找到的唯一答案是旧的并且使用了 iOS 12 中不推荐使用的存档函数(NSKeyedUnarchiver.unarchiveObjectWithData)。它们已被替换为需要类的 (NSKeyedUnarchiver.unarchivedObject),并且不允许使用 MKPolyline,因为它不符合 NSCoding。 这是不再支持的版本:
我在 xcdatamodeld 中将属性设置为 Transformable 并尝试创建 Transformer 函数以允许自动值转换。但是我收到错误“静态方法'unarchivedObject(ofClass:from:)'要求'MKPolyline'符合'NSCoding'”。
我已经尝试了几种方法来使 MKPolyline 支持 Codable,但我似乎也无法做到这一点。
这是我的价值转换器代码:
import Foundation
import MapKit
@objc(MKPolylineValueTransformer)
public final class MKPolylineValueTransformer: ValueTransformer {
override public class func transformedValueClass() -> AnyClass {
return MKPolyline.self
}
override public class func allowsReverseTransformation() -> Bool {
return true
}
override public func transformedValue(_ value: Any?) -> Any? {
guard let polyline = value as? MKPolyline else { return nil }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: polyline, requiringSecureCoding: true)
return data
} catch {
assertionFailure("Failed to transform `MKPolyline` to `Data`")
return nil
}
}
override public func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
do {
let polyline = NSKeyedUnarchiver.unarchivedObject(ofClass: MKPolyline, from: data as Data)
return polyline
} catch {
assertionFailure("Failed to transform `Data` to `MKPolyline`")
return nil
}
}
}
extension MKPolylineValueTransformer {
/// Register the transformer.
static let name = NSValueTransformerName(rawValue: String(describing: MKPolylineValueTransformer.self))
public static func register() {
let transformer = MKPolylineValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
作为一种替代方法,我采用折线内的点并构建了一个自定义类来存储数据。这主要是因为我可以成功存储对象,但 NSUnarchive 返回 nil 导致“致命错误:在展开可选值时意外发现 nil”。
这是代码:
import Foundation
import MapKit
public extension MKMultiPoint {
var coordinates: [CLLocationCoordinate2D] {
var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid,
count: pointCount)
getCoordinates(&coords, range: NSRange(location: 0, length: pointCount))
return coords
}
}
class CodableCoordinate: NSObject, NSSecureCoding {
static var supportsSecureCoding: Bool = true
var latitude: Double = 0
var longitude: Double = 0
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
required init(coder aDecoder: NSCoder) {
super.init()
latitude = aDecoder.decodeDouble(forKey: "latitude")
longitude = aDecoder.decodeDouble(forKey: "longitude")
}
func encode(with coder: NSCoder) {
coder.encode(latitude, forKey: "latitude")
coder.encode(longitude, forKey: "longitude")
}
}
class CodableCoordinatesArray: NSObject, NSSecureCoding {
static var supportsSecureCoding: Bool = true
var coordinatesArray: [CodableCoordinate]
init(coordinatesArray: [CodableCoordinate]) {
self.coordinatesArray = coordinatesArray
}
required init(coder aDecoder: NSCoder) {
let x = aDecoder.decodeObject(forKey:"coordinatesArray")
coordinatesArray = aDecoder.decodeObject(forKey: "coordinatesArray") as! [CodableCoordinate]
}
convenience init(fromMKPolyline polyline: MKPolyline) {
self.init(coordinatesArray: [CodableCoordinate]())
for coord in polyline.coordinates {
coordinatesArray.append(CodableCoordinate(latitude: coord.latitude, longitude: coord.longitude))
}
}
func encode(with coder: NSCoder) {
coder.encode(coordinatesArray, forKey:"coordinatesArray")
}
func polyline() -> MKPolyline? {
if coordinatesArray.isEmpty { return nil }
var coords = [CLLocationCoordinate2D]()
for coord in coordinatesArray {
coords.append(CLLocationCoordinate2D(latitude: coord.latitude, longitude: coord.longitude))
}
return MKPolyline(coordinates: coords, count: coords.count)
}
}
@objc(MKPolylineValueTransformer)
public final class MKPolylineValueTransformer: ValueTransformer {
override public class func transformedValueClass() -> AnyClass {
return MKPolyline.self
}
override public class func allowsReverseTransformation() -> Bool {
return true
}
override public func transformedValue(_ value: Any?) -> Any? {
guard let polyline = value as? MKPolyline else { return nil }
do {
let arrayFromPolyline = CodableCoordinatesArray(fromMKPolyline: polyline)
let data = try NSKeyedArchiver.archivedData(withRootObject: arrayFromPolyline, requiringSecureCoding: true)
return data
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
override public func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
do {
let coordinatesArray = try NSKeyedUnarchiver.unarchivedObject(ofClass: CodableCoordinatesArray.self, from: data as Data)
return coordinatesArray?.polyline()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
extension MKPolylineValueTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: MKPolylineValueTransformer.self))
/// Registers the value transformer
public static func register() {
let transformer = MKPolylineValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
仅供参考,包含以下行是为了让我在投射前检查解码:
let x = aDecoder.decodeObject(forKey:"coordinatesArray")
谢谢