Skip to content

p-x9/SDCALayer

Repository files navigation

SDCALayer

Server-Driven CALayer

Demo

demo

Document

Supported Layer

  • CALayer
  • CAShapeLayer
  • CATextLayer
  • CAScrollLayer
  • CAGradientLayer
  • CAReplicatorLayer

JSON to CALayer

let json: String = ""

let model = SDCALayer.load(fromJSON: json)

let layer: CALayer = model?.convertToLayer()

YAML to CALayer

let model = SDCALayer.load(fromYAML: yaml)

CALayer to JSON

let layer = CAShapeLayer()
/* ~ customize layer  ~ */

let model = SDCALayer(model: layer.codable())

let json: String = model?.json

CALayer to YAML

let yaml: String = model?.yaml

Formats of Layer Model

Layer's models are defined in the following directory. Models

{
    "frame": [
        [
            0,
            0
        ],
        [
            100,
            50
        ]
    ],
    "cornerRadius": 5.0,
    "borderColor": {
        "code": "#FF0088"
    },
    // other properties
}

Since we need to know the actual class of the Layer, we need to receive a model with the class name and a model for each class, as follows

{
    "class": "CAShapeLayer",
    "layerModel": {
        // CAShapeLayer Model
    }
}

By using the p-x9/IndirectlyCodable library, CALayer indirectly conforms to the Codable protocol, allowing inter-conversion with json.

Support for custom layer classes

Suppose we have the following customized layer class.

class AALayer: CALayer {
    var newProperty: String? = "AAAA"
}

create layer model like this.

click to expand
class JAALayer: JCALayer {
    typealias Target = AALayer // alias for target layer class

    // Coding key (codable)
    private enum CodingKeys: String, CodingKey {
        case newProperty
    }

    // Target class name to exact layer class
    // You must specify the class name including the product name and package name.
    // (ex. MyApp.AALayer)
    public override class var targetTypeName: String {
        String(reflecting: Target.self)
    }

    override init() {
        super.init()
    }

    // Decodable
    public required init(from decoder: Decoder) throws {
        try super.init(from: decoder)

        let container = try decoder.container(keyedBy: CodingKeys.self)

        newProperty = try container.decodeIfPresent(String.self, forKey: .newProperty)
    }

    public required convenience init(with object: CALayer) {
        self.init()

        reverseApplyProperties(with: object)
    }

    // Encodable
    public override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)

        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(newProperty, forKey: .newProperty)
    }

    // apply properties to taget from model
    // model -> target
    public override func applyProperties(to target: CALayer) {
        super.applyProperties(to: target)

        guard let target = target as? AALayer else { return }

        target.newProperty = newProperty
    }

    // apply properties to model from target
    // targe -> model
    public override func applyProperties(with target: CALayer) {
        super.applyProperties(with: target)

        guard let target = target as? AALayer else { return }

        newProperty = target.newProperty
    }

    public override func convertToLayer() -> CALayer? {
        let layer = AALayer()

        self.applyProperties(to: layer)

        return layer
    }
}

Finally, specify the model class in the layer class extension

extension AALayer {
    public typealias Target = JAALayer

    public override class var codableTypeName: String {
        String(reflecting: Target.self)
    }
}

Example

Websocket HotReload

Start the server, change the json, save it, and it will be reflected in the app. install calayer-ws app to yor iphone (or simulator) and connect your server.

A B
A B
python ./server/ws-hotreload-server.py "<path to json>"

example json file is here.

python ./server/ws-hotreload-server.py "./Example/json/star.json"

Licenses

MIT License