{"id":16322279,"url":"https://github.com/p-x9/sdcalayer","last_synced_at":"2025-03-22T21:32:29.007Z","repository":{"id":114894826,"uuid":"561298035","full_name":"p-x9/SDCALayer","owner":"p-x9","description":"Server-Driven CALayer.","archived":false,"fork":false,"pushed_at":"2024-04-09T04:40:27.000Z","size":141,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-01T19:04:19.605Z","etag":null,"topics":["hot-reload","server-driven-ui","serverdrivenui"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/p-x9.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-03T11:48:20.000Z","updated_at":"2025-03-01T00:07:54.000Z","dependencies_parsed_at":"2024-10-28T15:09:32.433Z","dependency_job_id":"30ce3ec9-456d-4ba3-a96d-f915877eb7e7","html_url":"https://github.com/p-x9/SDCALayer","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p-x9%2FSDCALayer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p-x9%2FSDCALayer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p-x9%2FSDCALayer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p-x9%2FSDCALayer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/p-x9","download_url":"https://codeload.github.com/p-x9/SDCALayer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244246546,"owners_count":20422458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hot-reload","server-driven-ui","serverdrivenui"],"created_at":"2024-10-10T22:50:28.596Z","updated_at":"2025-03-22T21:32:28.733Z","avatar_url":"https://github.com/p-x9.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SDCALayer\n\nServer-Driven CALayer\n\n## Demo\n![demo](https://user-images.githubusercontent.com/50244599/203554672-2dc2de2a-0f65-4921-8b93-ab7bf87775d6.gif)\n\n\n## Document\n### Supported Layer\n- CALayer\n- CAShapeLayer\n- CATextLayer\n- CAScrollLayer\n- CAGradientLayer\n- CAReplicatorLayer\n\n### JSON to CALayer\n```swift\nlet json: String = \"\"\n\nlet model = SDCALayer.load(fromJSON: json)\n\nlet layer: CALayer = model?.convertToLayer()\n```\n\n### YAML to CALayer\n```swift\nlet model = SDCALayer.load(fromYAML: yaml)\n```\n\n### CALayer to JSON\n```swift\nlet layer = CAShapeLayer()\n/* ~ customize layer  ~ */\n\nlet model = SDCALayer(model: layer.codable())\n\nlet json: String = model?.json\n```\n### CALayer to YAML\n```swift\nlet yaml: String = model?.yaml\n```\n\n### Formats of Layer Model \nLayer's models are defined in the following directory.\n[Models](./Sources/SDCALayer/Model/)\n```json\n{\n    \"frame\": [\n        [\n            0,\n            0\n        ],\n        [\n            100,\n            50\n        ]\n    ],\n    \"cornerRadius\": 5.0,\n    \"borderColor\": {\n        \"code\": \"#FF0088\"\n    },\n    // other properties\n}\n```\n\nSince 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\n```json\n{\n    \"class\": \"CAShapeLayer\",\n    \"layerModel\": {\n        // CAShapeLayer Model\n    }\n}\n```\n\nBy using the [p-x9/IndirectlyCodable](https://github.com/p-x9/IndirectlyCodable) library, CALayer indirectly conforms to the `Codable` protocol, allowing inter-conversion with json.\n\n#### Support for custom layer classes\nSuppose we have the following customized layer class.\n```swift\nclass AALayer: CALayer {\n    var newProperty: String? = \"AAAA\"\n}\n```\n\ncreate layer model like this.\n\u003cdetails\u003e\n\u003csummary\u003eclick to expand\u003c/summary\u003e\n\n```swift\nclass JAALayer: JCALayer {\n    typealias Target = AALayer // alias for target layer class\n\n    // Coding key (codable)\n    private enum CodingKeys: String, CodingKey {\n        case newProperty\n    }\n\n    // Target class name to exact layer class\n    // You must specify the class name including the product name and package name.\n    // (ex. MyApp.AALayer)\n    public override class var targetTypeName: String {\n        String(reflecting: Target.self)\n    }\n\n    override init() {\n        super.init()\n    }\n\n    // Decodable\n    public required init(from decoder: Decoder) throws {\n        try super.init(from: decoder)\n\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n\n        newProperty = try container.decodeIfPresent(String.self, forKey: .newProperty)\n    }\n\n    public required convenience init(with object: CALayer) {\n        self.init()\n\n        reverseApplyProperties(with: object)\n    }\n\n    // Encodable\n    public override func encode(to encoder: Encoder) throws {\n        try super.encode(to: encoder)\n\n        var container = encoder.container(keyedBy: CodingKeys.self)\n\n        try container.encode(newProperty, forKey: .newProperty)\n    }\n\n    // apply properties to taget from model\n    // model -\u003e target\n    public override func applyProperties(to target: CALayer) {\n        super.applyProperties(to: target)\n\n        guard let target = target as? AALayer else { return }\n\n        target.newProperty = newProperty\n    }\n\n    // apply properties to model from target\n    // targe -\u003e model\n    public override func applyProperties(with target: CALayer) {\n        super.applyProperties(with: target)\n\n        guard let target = target as? AALayer else { return }\n\n        newProperty = target.newProperty\n    }\n\n    public override func convertToLayer() -\u003e CALayer? {\n        let layer = AALayer()\n\n        self.applyProperties(to: layer)\n\n        return layer\n    }\n}\n```\n\u003c/summary\u003e\n\u003c/details\u003e\n\nFinally, specify the model class in the layer class extension\n```swift\nextension AALayer {\n    public typealias Target = JAALayer\n\n    public override class var codableTypeName: String {\n        String(reflecting: Target.self)\n    }\n}\n```\n\n## Example\n### Websocket HotReload\nStart the server, change the json, save it, and it will be reflected in the app.\ninstall [calayer-ws app](./Example/calayer-ws/) to yor iphone (or simulator) and connect your server.\n|  A  |  B  |\n| ---- | ---- |\n|  ![A](https://user-images.githubusercontent.com/50244599/203506188-cf3bd4a0-3c5e-451c-9f0b-fdd99e1caadd.PNG)  |  ![B](https://user-images.githubusercontent.com/50244599/203506216-25f25871-f0a0-410b-b918-7b687e373ecc.PNG)  |\n\n```sh\npython ./server/ws-hotreload-server.py \"\u003cpath to json\u003e\"\n```\nexample json file is [here](./Example/json/).\n\n```sh\npython ./server/ws-hotreload-server.py \"./Example/json/star.json\"\n```\n## Licenses\n\n[MIT License](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp-x9%2Fsdcalayer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fp-x9%2Fsdcalayer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp-x9%2Fsdcalayer/lists"}