swiftcodablecashapelayernscodingdecodable

make CAShapeLayer and CGPath conform to Codable or to NSCoding


I'm trying to save and get info about CAShapeLayers at .json files, using Codable protocol, but have an error:

public class Node: Codable {


public var name: String = ""

public var path: CGPath?
    
public var baseLayer: CAShapeLayer?


public required init(from decoder: Decoder) throws {
    let container = try? decoder.container(keyedBy: NodeCodingKeys.self)
           
    let _name = try container?.decodeIfPresent(String.self, forKey: NodeCodingKeys.name)
    
    let _baseLayer = try container?.decodeIfPresent(CAShapeLayer.self, forKey: NodeCodingKeys.baseLayer)
    name = _name ?? ""
}

public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: NodeCodingKeys.self)
    try container.encode(name, forKey: NodeCodingKeys.name)
}
}

public enum NodeCodingKeys: CodingKey {
    
    case path
    case name
    case baseLayer
    
}

enter image description here

And the same for CGPath. Is there any way to save/receive them to/from device?


Solution

  • I couldn't find a way to make CAShapeLayer conforms to Codable, so I created class for my data object:

    public class ImageNode: NSObject, NSCoding, NSSecureCoding {
        public static var supportsSecureCoding = true
        var name: NSString
        var baseLayer: CAShapeLayer
        
        init(name: NSString,
             baseLayer: CAShapeLayer
             ) {
                self.name = name
                self.baseLayer = baseLayer
            }
        
        public func encode(with coder: NSCoder) {
            
            coder.encode(name, forKey: "name")
            coder.encode(baseLayer, forKey: "baseLayer")        
        }
        
        public required convenience init?(coder: NSCoder) {
            guard let name = coder.decodeObject(forKey: "name") as? NSString
                else { return nil }
            guard let baseLayer = coder.decodeObject(forKey: "baseLayer") as? CAShapeLayer
                else { return nil }
                 
            print("decode node")
                self.init(
                    name: name,
                    baseLayer: baseLayer
                )
        } }
    

    which works nice with NSKeyedArchiver and NSKeyedUnarchiver for saving data to file and retrieving it. Maybe this solution will helps someone.