Go to file
Alexandr Goncharov d45406b0cb Update README.md 2017-10-08 22:57:31 +03:00
.circleci Add CircleCI config 2017-10-08 06:05:07 +03:00
Sources/JSONDecoder-Keypath Add nested keypath support 2017-10-08 04:09:45 +03:00
Tests Add nested keypath support 2017-10-08 04:09:45 +03:00
.gitignore Create a package 2017-10-08 02:16:04 +03:00
.swift-version Create a package 2017-10-08 02:16:04 +03:00
.swiftlint.yml Create a package 2017-10-08 02:16:04 +03:00
JSONDecoder-Keypath.podspec Add podspec file 2017-10-08 22:35:13 +03:00
LICENSE Initial commit 2017-10-08 01:26:00 +03:00
Package.swift Create a package 2017-10-08 02:16:04 +03:00
README.md Update README.md 2017-10-08 22:57:31 +03:00

README.md

JSONDecoder-Keypath

CircleCI Isues License Swift Package Manager Cocoapods

Add nested key path support to the Foundation.JSONDecoder

Rationale

At the time of writing, I found that most of the popular frameworks (mostly network wrappers) when dealing with keypath do some terrible things.

So, suppose we have a Decodable (Codable) object but in the API response we are getting it under some custom path. Most of the solutions already have interface to extract object by keypath and also added Codable support. But the problem is that in almost all cases that I've seen implementation is next:

  1. Extract [String: Any] from the Data with JSONSerialization
  2. Follow keypath in this dictionary
  3. Convert given object to Data
  4. Use JSONDecoder to parse object

It is obvious that the conversion of data back and forth is an extra waste of resources. In addition, on large amounts of data, this is highly not advisable.

This package eliminates first 3 steps.

Usage

Say you have a Item model

struct Item: Codable {
 ...
}

And we have a following JSON:

{
 "foo" : <actual object>
}

To parse this you need to write:

let jsonData: Data = ...
let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo")

Nested keypath are also supported:

let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo.bar")

Keypath separator can be configured:

let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo/bar", keyPathSeparator: "/")

Under the hood

Package adds new method to the JSONDecoder

func decode<T>(_ type: T.Type,
                   from data: Data,
                   keyPath: String,
                   keyPathSeparator separator: String = ".") throws -> T where T : Decodable

In this call keypath is stored in the JSONDecoder.userInfo and then standard decode method is called with private class KeyPathWrapper<T> as a type parameter. In the KeyPathWrapper constructor keypath data is fetched from the userInfo and decoder is traversed with this values. After that original type is decoded.

Installation

Swift Package Manager

Add the line .Package(url: "https://github.com/0111b/JSONDecoder-Keypath.git") to your Package.swift

Cocoapods

    pod 'JSONDecoder-Keypath'

Conclusion

Mostly this is a naive implementation of the custom object encoding and took from me no more than a few hours. But it is showing that we must think how we are working with provided API's and not be lazy to look under the hood

Plans and improvements

  • Extend unit tests
  • Add cocoapods spec
  • Add Carthage support