0

我正在寻找使用 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")

谢谢

4

0 回答 0