{"id":19727639,"url":"https://github.com/hecrj/swiftdecodepipeline","last_synced_at":"2025-07-05T03:08:29.484Z","repository":{"id":62456496,"uuid":"75694639","full_name":"hecrj/SwiftDecodePipeline","owner":"hecrj","description":"A library for building JSON decoders using the pipeline operator. Inspired by https://github.com/NoRedInk/elm-decode-pipeline.","archived":false,"fork":false,"pushed_at":"2017-09-18T01:23:09.000Z","size":21,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-24T03:17:24.385Z","etag":null,"topics":["json","json-decoder","pipeline","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/hecrj.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}},"created_at":"2016-12-06T04:32:07.000Z","updated_at":"2019-03-14T20:35:00.000Z","dependencies_parsed_at":"2022-11-02T00:16:58.073Z","dependency_job_id":null,"html_url":"https://github.com/hecrj/SwiftDecodePipeline","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/hecrj/SwiftDecodePipeline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hecrj%2FSwiftDecodePipeline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hecrj%2FSwiftDecodePipeline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hecrj%2FSwiftDecodePipeline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hecrj%2FSwiftDecodePipeline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hecrj","download_url":"https://codeload.github.com/hecrj/SwiftDecodePipeline/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hecrj%2FSwiftDecodePipeline/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263674341,"owners_count":23494559,"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":["json","json-decoder","pipeline","swift"],"created_at":"2024-11-11T23:40:30.568Z","updated_at":"2025-07-05T03:08:29.457Z","avatar_url":"https://github.com/hecrj.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SwiftDecodePipeline\n\n[![CI Status](http://img.shields.io/travis/hecrj/SwiftDecodePipeline.svg?style=flat)](https://travis-ci.org/hecrj/SwiftDecodePipeline)\n[![Version](https://img.shields.io/cocoapods/v/SwiftDecodePipeline.svg?style=flat)](http://cocoapods.org/pods/SwiftDecodePipeline)\n[![License](https://img.shields.io/cocoapods/l/SwiftDecodePipeline.svg?style=flat)](http://cocoapods.org/pods/SwiftDecodePipeline)\n[![Platform](https://img.shields.io/cocoapods/p/SwiftDecodePipeline.svg?style=flat)](http://cocoapods.org/pods/SwiftDecodePipeline)\n\nA library for building JSON decoders using the pipeline `(|\u003e)` operator and plain function calls. Inspired by\n[elm-decode-pipeline](https://github.com/NoRedInk/elm-decode-pipeline).\n\n## Motivation\n\nAs a newcomer to Swift I wasn't entirely convinced by the JSON decoding libraries out there. After trying Elm, I was hoping something\nsimple and mostly functional would be available. \n\n[Argo][argo] got my attention. While it looked really close to what I wanted, it forces you to implement a `decode` method in your types.\nThis allows [Argo][argo] to automatically decode any type that implements the `Decodable` protocol.\nBut, this approach isn't flexible. It ties **one** (and only one) decoder to a specific type, **forever**. What if\nwe want to decode the responses of different APIs into the same type? What if some endpoint doesn't return the data in the exact same way?\nSounds familiar? Besides, [Argo][argo] uses many different infix operators (`\u003c^\u003e`, `\u003c*\u003e`, `\u003c|`, `\u003c||`) that make the decoder definitions hard to\nunderstand and reason about.\n\nMy take is that decoders should be pure functions that live independently from types. They accept some data (JSON, for now) and produce either an error\nor an instance of some type. Decoders should be easy to compose and reuse, and all it should be needed for that is one simple infix operator:\nthe pipeline `(|\u003e)`. This approach is really well implemented in [elm-decode-pipeline][edp], an [Elm][elmlang] library, producing simple, readable and\nreusable decoders.\n\n**SwiftDecodePipeline** tries to bring the spirit of [elm-decode-pipeline][edp] into Swift.\n\n## Example\n\nLet's say that we have this type:\n\n```swift\nstruct User {\n    let name: String\n    let surname: String\n    let image: String?\n    let score: Int\n    let sports: [String]\n    let role: String\n}\n```\n\nAnd we want to decode the data returned by some API endpoint `/users` that looks like this:\n\n```json\n[\n    {\n        \"uuid\": \"...\",\n        \"name\": \"John\",\n        \"last_name\": \"Doe\",\n        \"image\": null,\n        \"sports\": [\"basketball\", \"tennis\"],\n        \"score\": 5\n    }\n]\n```\n\nThen, we can write a `Decoder\u003cUser\u003e` for that endpoint easily:\n\n```swift\nlet decodeUser: Decoder\u003cUser\u003e =\n    decode(User.init)\n        |\u003e required(\"name\", string)\n        |\u003e required(\"last_name\", string)\n        |\u003e optional(\"image\", string)\n        |\u003e required(\"score\", int)\n        |\u003e required(\"sports\", array(string))\n        |\u003e hardcoded(\"athlete\")\n```\n\nNow we can decode the response:\n\n```swift\nlet data: String!\n\n// We make a request to /users and obtain the JSON here...\n\nlet result = decodeJSON(data, with: array(decodeUser))\n\nswitch result {\ncase .error(let error): print(\"Invalid format: \\(error)\") // error describes the decoding error\ncase .ok(let users): doSomething(with: users) // users has type [User] :D\n}\n````\n\nIn the example above, `data` is a `String`. However, the `decode` function supports different types for the first parameter to suit your needs.\nThe next section describes the different data types that can be decoded using this library.\n\nNotice how easy it is to transform and reuse decoders. In this case, our `decodeUser` is able to decode a single user, but we want to decode a list of users.\nThus, we end up using `array(decodeUser)` to transform our `Decoder\u003cUser\u003e` into a `Decoder\u003c[User]\u003e`. A `Decoder\u003cType\u003e` returns either `.error(String)`\nwhen the format is invalid, or `.ok(Type)` when the input was decoded sucessfully.\n\n\n## Available functions\n### `decodeJSON(json, with: decoder)`\n\nIt allows to use decoders for JSON decoding in a convenient way. Right now, there are 3 different `decodeJSON` definitions. Each one of them accepts\nJSON in a different form: `Data`, `String` and `Any`.\n\nThe `Any` definition is there to [support Alamofire response data](#library-support).\n\n### Primitives\n\nPrimitives are decoders by themselves. They decode JSON values into Swift values.\n\n```swift\nlet string: Decoder\u003cString\u003e \nlet bool:   Decoder\u003cBool\u003e\nlet int:    Decoder\u003cInt\u003e\nlet double: Decoder\u003cDouble\u003e\n```\n\n### Modifiers\n\nA modifier is a function that may take some configuration parameters and returns a function that can take a decoder and return a brand new decoder\nwith some additional behaviour.\n\nIn this section, the syntax `\u003cmodifier\u003e(\u003cconfigParams\u003e)` is used to describe the different modifiers.\n\n#### `required(String, Decoder\u003cA\u003e)` and `optional(String, Decoder\u003cA\u003e)`\n\n`required` extracts a field from a JSON object and decodes its value into an `A`, failing if the field does not exist or it is `null`.\n`optional` does the same while allowing the field to be missing or `null`, thus decoding into an `A?`.\n\n```swift\nstruct User {\n    let name: String\n    let image: String?\n}\n\nlet decodeUser: Decoder\u003cUser\u003e =\n    decode(User.init)\n        |\u003e required(\"name\", string)\n        |\u003e optional(\"image\", string)\n```\n\n#### `array`\nIt decodes a JSON array.\n\n```swift\nstruct Post {\n    // ...\n    let authors: [User]\n    // ...\n}\n\nlet decodePost: Decoder\u003cPost\u003e =\n    decode(Post.init)\n        //...\n        |\u003e required(\"authors\", decodeUser |\u003e array)\n        // We could use array(decodeUser), they are equivalent\n        // ...\n```\n\n#### `hardcoded(A)`\n\nIt always returns the provided value, independently of the JSON data. It is useful to hardcode data in the decoding pipeline.\n\n```swift\nlet decodeMockedUser: Decoder\u003cUser\u003e =\n    decode(User.init)\n        |\u003e hardcoded(\"some-id\")\n        |\u003e hardcoded(\"John\")\n        |\u003e hardcoded(\"Doe\")\n        // ...\n```\n\n#### `map((A) -\u003e B)`\n\nIt transforms a decoded value from `A` to `B`.\n\n```swift\nlet decodeLowercasedString: Decoder\u003cString\u003e = string |\u003e map { $0.lowercased }\n```\n\n## Library support\n\n### Alamofire\n\nThis library can be used with [Alamofire][alamofire] easily:\n\n```swift\nAlamofire.request(\"https://example.com/users\").validate().responseJSON { response in\n    switch response.result {\n    case .success(let data):\n        // We want to decode a list of users, so we use array\n        let decodingResult = decodeJSON(data, with: array(decodeUser))\n\n        switch decodingResult {\n        case .error(let error): print(\"Invalid format: \\(error)\") // error describes the decoding error\n        case .ok(let users): doSomething(with: users) // users has type [User]\n        }\n    // Handle request error here...\n    }\n}\n```\n\n### SwiftyJSON\n\nThis library uses [SwiftyJSON](swiftyjson) internally. You should be able to decode `JSON` types using decoders\ndirectly:\n\n```swift\nlet json: JSON!\nlet result = decodeUser(json)\n```\n\n## Installation\n\nSwiftDecodePipeline is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'SwiftDecodePipeline'\n```\n\n## Author\n\nHéctor Ramón Jiménez\n\n## License\n\nSwiftDecodePipeline is available under the MIT license. See the LICENSE file for more info.\n\n## Contributing\n\n1. Fork the repository.\n2. Make your changes, tests will be appreciated.\n3. Open a Pull Request in this repository.\n\n## Special mentions (and thanks)\n* [SwiftyJSON][swiftyjson], as it is really easy to use and this library uses it internally.\n* [Curry][curry], another cool library that is used behind the scenes.\n* [Argo][argo]\n* [Elm][elmlang]\n\n\n[argo]: https://github.com/thoughtbot/Argo\n[edp]: https://github.com/NoRedInk/elm-decode-pipeline\n[alamofire]: https://github.com/Alamofire/Alamofire\n[swiftyjson]: https://github.com/SwiftyJSON/SwiftyJSON\n[curry]: https://github.com/thoughtbot/Curry\n[elmlang]: http://elm-lang.org\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhecrj%2Fswiftdecodepipeline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhecrj%2Fswiftdecodepipeline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhecrj%2Fswiftdecodepipeline/lists"}