Search code examples
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.