{"id":27399888,"url":"https://github.com/tattn/morecodable","last_synced_at":"2025-04-14T03:24:29.440Z","repository":{"id":63921115,"uuid":"121120706","full_name":"tattn/MoreCodable","owner":"tattn","description":"MoreCodable expands the possibilities of `Codable`.","archived":false,"fork":false,"pushed_at":"2023-12-01T18:25:55.000Z","size":106,"stargazers_count":380,"open_issues_count":2,"forks_count":23,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T23:18:04.900Z","etag":null,"topics":["codable","ios","linux","macos","swift"],"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/tattn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"tattn"}},"created_at":"2018-02-11T12:22:08.000Z","updated_at":"2024-01-21T02:43:16.000Z","dependencies_parsed_at":"2024-06-19T11:31:49.457Z","dependency_job_id":"df863c0a-5972-4240-b4c3-d0de3e761dfd","html_url":"https://github.com/tattn/MoreCodable","commit_stats":{"total_commits":59,"total_committers":9,"mean_commits":6.555555555555555,"dds":"0.18644067796610164","last_synced_commit":"95e3cc73810e1958a81a01d189a318828b14e44f"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattn%2FMoreCodable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattn%2FMoreCodable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattn%2FMoreCodable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tattn%2FMoreCodable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tattn","download_url":"https://codeload.github.com/tattn/MoreCodable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248814422,"owners_count":21165763,"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":["codable","ios","linux","macos","swift"],"created_at":"2025-04-14T03:24:28.819Z","updated_at":"2025-04-14T03:24:29.432Z","avatar_url":"https://github.com/tattn.png","language":"Swift","readme":"\u003ch1 align=\"center\"\u003eMoreCodable\u003c/h1\u003e\n\n\u003ch5 align=\"center\"\u003eMoreCodable expands the possibilities of \"Codable\".\u003c/h5\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.org/tattn/MoreCodable\"\u003e\n    \u003cimg src=\"https://travis-ci.org/tattn/MoreCodable.svg?branch=master\" alt=\"Build Status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/Carthage/Carthage\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat\" alt=\"Carthage compatible\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"http://cocoapods.org/pods/MoreCodable\"\u003e\n    \u003cimg src=\"https://img.shields.io/cocoapods/v/MoreCodable.svg\" alt=\"CocoaPods\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"http://cocoapods.org/pods/MoreCodable\"\u003e\n    \u003cimg src=\"https://img.shields.io/cocoapods/p/MoreCodable.svg\" alt=\"Platform\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://developer.apple.com/swift\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5-F16D39.svg\" alt=\"Swift Version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"./LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-green.svg?style=flat-square\" alt=\"license:MIT\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\n\n# Installation\n\n## Carthage\n\n```ruby\ngithub \"tattn/MoreCodable\"\n```\n\n## CocoaPods\n\n```ruby\npod 'MoreCodable'\n```\n\n# Feature\n\n## DictionaryEncoder / DictionaryDecoder\n\n```swift\nstruct User: Codable {\n    let id: Int\n    let name: String\n}\n\nlet encoder = DictionaryEncoder()\nlet user = User(id: 123, name: \"tattn\")\nlet dictionary: [String: Any] = try! encoder.encode(user) // =\u003e {\"id\": 123, \"name\": \"tattn\"}\n```\n\n```swift\nlet decoder = DictionaryDecoder()\nlet user = try decoder.decode(User.self, from: dictionary)\n```\n\n## URLQueryItemsEncoder / URLQueryItemsDecoder\n\n```swift\nstruct Parameter: Codable {\n    let query: String\n    let offset: Int\n    let limit: Int\n}\nlet parameter = Parameter(query: \"ねこ\", offset: 10, limit: 20)\nlet encoder = URLQueryItemsEncoder()\nlet params: [URLQueryItem] = try! encoder.encode(parameter)\n\nvar components = URLComponents(string: \"https://example.com\")\ncomponents?.queryItems = params\ncomponents?.url // https://example.com?query=%E3%81%AD%E3%81%93\u0026offset=10\u0026limit=20\n```\n\n```swift\nlet decoder = URLQueryItemsDecoder()\nlet parameter = try decoder.decode(Parameter.self, from: params)\n```\n\n## ObjectMerger\n\n```swift\nstruct APIResponse: Encodable {\n    let id: Int\n    let title: String\n    let foo: String\n}\n\nstruct APIResponse2: Encodable {\n    let tags: [String]\n}\n\nstruct Model: Decodable {\n    let id: Int\n    let title: String\n    let tags: [String]\n}\n\nlet response = APIResponse(id: 0, title: \"Awesome article\", foo: \"bar\")\nlet response2 = APIResponse2(tags: [\"swift\", \"ios\", \"macos\"])\nlet model = try ObjectMerger().merge(Model.self, response, response2)\n\n// success\nXCTAssertEqual(model.id, response.id)\nXCTAssertEqual(model.title, response.title)\nXCTAssertEqual(model.tags, response2.tags)\n```\n\n## RuleBasedCodingKey\n\n```swift\nstruct User: Codable {\n    let userId: String\n    let name: String\n\n    enum CodingKeys: String, RuleBasedCodingKey {\n        case userId\n        case name\n\n        func codingKeyRule(key: String) -\u003e String {\n            return key.uppercased() // custom rule\n        }\n    }\n}\n\nlet json = \"\"\"\n{\"USERID\": \"abc\", \"NAME\": \"tattn\"}\n\"\"\".data(using: .utf8)!\n\nlet user = try! JSONDecoder().decode(User.self, from: json) // =\u003e User(userId: \"abc\", name: \"tattn\")\n```\n\n### SnakeCaseCodingKey\n\n```swift\nstruct User: Codable {\n    let userId: String\n    let name: String\n\n    enum CodingKeys: String, SnakeCaseCodingKey {\n        case userId\n        case name\n    }\n}\n\nlet json = \"\"\"\n{\"user_id\": \"abc\", \"name\": \"tattn\"}\n\"\"\".data(using: .utf8)!\n\nlet user = try! JSONDecoder().decode(User.self, from: json) // ok\n```\n\n### UpperCamelCaseCodingKey\n\n```swift\nstruct User: Codable {\n    let userId: String\n    let name: String\n\n    enum CodingKeys: String, UpperCamelCaseCodingKey {\n        case userId\n        case name\n    }\n}\n\nlet json = \"\"\"\n{\"UserId\": \"abc\", \"Name\": \"tattn\"}\n\"\"\".data(using: .utf8)!\n\nlet user = try! JSONDecoder().decode(User.self, from: json) // ok\n```\n\n## Failable\u003cWrapped\u003e\n\n```swift\nlet json = \"\"\"\n[\n    {\"name\": \"Taro\", \"age\": 20},\n    {\"name\": \"Hanako\"}\n]\n\"\"\".data(using: .utf8)! // Hanako has no \"age\"\n\nstruct User: Codable {\n    let name: String\n    let age: Int\n}\n\nlet users = try! JSONDecoder().decode([Failable\u003cUser\u003e].self,\n                                      from: json)\n\n// success\nXCTAssertEqual(users[0].value?.name, \"Taro\")\nXCTAssertEqual(users[0].value?.age, 20)\nXCTAssertNil(users[1].value)\n```\n\n## StringTo\u003cT\u003e\n\n```swift\nlet json = \"\"\"\n{\n    \"int\": \"100\",\n    \"articleId\": \"abc\"\n}\n\"\"\".data(using: .utf8)!\n\nstruct Root: Codable {\n    let int: StringTo\u003cInt\u003e\n    let articleId: StringTo\u003cArticleId\u003e\n\n    struct ArticleId: LosslessStringConvertible, Codable {\n        var description: String\n\n        init?(_ description: String) {\n            self.description = description\n        }\n    }\n}\n\nlet root = try! JSONDecoder().decode(Root.self, from: json)\n\n// success\nXCTAssertEqual(root.int.value, 100)\nXCTAssertEqual(root.articleId.value.description, \"abc\")\n```\n\n## MultiDateFormat\n```swift\nlet json = \"\"\"\n{\n    \"date\": \"2019.05.27\",\n    \"dateTime\": \"2019-05-27T17:26:59+0000\",\n    \"timestamp\": 1558978068,\n    \"timestampMilliseconds\": 1558978141863,\n    \"custom\": \"1558978068\"\n}\n\"\"\".data(using: .utf8)!\n\nstruct Document: Codable {\n    var date: Date\n    var dateTime: Date\n    var timestamp: Date\n    var timestampMilliseconds: Date\n    var custom: Date\n}\n\nextension Document: MultiDateFormat {\n    \n    static var dateFormatter: DateFormatter = {\n        let formatter = DateFormatter()\n        formatter.timeZone = TimeZone(identifier: \"UTC\")\n        formatter.dateFormat = \"yyyy.MM.dd\"\n        return formatter\n    }()\n    \n    static func dateFormat(for codingKey: CodingKey) -\u003e DateFormat? {\n        switch codingKey {\n        case CodingKeys.date: return .formatted(dateFormatter)\n        case CodingKeys.dateTime: return .iso8601\n        case CodingKeys.timestamp: return .secondsSince1970\n        case CodingKeys.timestampMilliseconds: return .millisecondsSince1970\n        case CodingKeys.custom: return .custom({ (date, encoder) in\n            var container = encoder.singleValueContainer()\n            try container.encode(String(date.timeIntervalSince1970))\n        }, { (decoder) -\u003e Date in\n            let container = try decoder.singleValueContainer()\n            let string = try container.decode(String.self)\n            let timeInterval = TimeInterval(string)!\n            return Date(timeIntervalSince1970: timeInterval)\n        })\n        default: return nil\n        }\n    }\n    \n}\n\nlet decoded = try! MoreJSONDecoder().decode(Document.self, from: json)\nlet encoded = try! MoreJSONEncoder().encode(document)\n```\n\n## DictionaryCachableEncoder\n```swift\nstruct User: Codable, Hashable { // conform to Hashable\n    let id: Int\n    let name: String\n}\n\nlet encoder = DictionaryCachableEncoder()\nlet user = User(id: 123, name: \"tattn\")\nlet dictionary: [String: Any] = try! encoder.encode(user) // =\u003e {\"id\": 123, \"name\": \"tattn\"}\ntry! encoder.encode(user) // use the previous encoded result for the second time \n```\n\n# ToDo\n- [ ] XMLDecoder/XMLEncoder\n- [ ] CSVDecoder/CSVEncoder\n\n# Related project\n\n**DataConvertible**  \nhttps://github.com/tattn/DataConvertible\n\n# Contributing\n\n1. Fork it!\n2. Create your feature branch: `git checkout -b my-new-feature`\n3. Commit your changes: `git commit -am 'Add some feature'`\n4. Push to the branch: `git push origin my-new-feature`\n5. Submit a pull request :D\n\n## Support this project\n\nDonating to help me continue working on this project.\n\n[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/tattn/)\n\n# License\n\nMoreCodable is released under the MIT license. See LICENSE for details.\n","funding_links":["https://github.com/sponsors/tattn","https://paypal.me/tattn/"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftattn%2Fmorecodable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftattn%2Fmorecodable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftattn%2Fmorecodable/lists"}