Home

Awesome

JSONUtilities

Build Status codecov.io Carthage Compatible Swift Version

Easily load JSON objects and decode them into structs or classes. The json(atKeyPath:) function infers the type from the constant or variable definition to decode meaning no casting is needed. Both string keys and keypaths (keys separated by dots .) are supported when decoding JSON.

Installation

CocoaPods:

Add the line pod 'JSONUtilities' to your Podfile.

Carthage:

Add the line github "lucianomarisi/JSONUtilities" to your Cartfile.

Manual:

Add the files inside the Sources folder into your Xcode project.

Swift Package Manager:

Add the line .Package(url: "https://github.com/lucianomarisi/JSONUtilities", majorVersion: 3) to your Package.swift.

Types supported

JSON raw types:

Array of JSON raw types:

Custom JSON objects and custom JSON object arrays

e.g. if MyClass and MyStruct conform to JSONObjectConvertible protocol

Dictionaries with a JSONKey

String already conforms to JSONKey. It's also easy to make enum keys by just making them conform to JSONKey which requires a var key: String { get }, and an init?(rawValue: String) which RawRepresentable already conforms to e.g:

enum MyEnum: String, JSONKey {
	case one
	case two
	
	var key: String {
		return rawValue
	}
}

InvalidItemBehaviour

When decoding arrays or dictionaries an invalidItemBehaviour parameter can be passed which controls what happens when an error occurs while decoding child items

KeyPath

Each json(atKeyPath:) function takes a KeyPath. This is an enum with 2 cases:

KeyPath can be initialized with a string literal. When doing so, if there are any . it will be treated as a keypath instead of a simple key.

When providing keyPaths, an int allows lookup up a value in an array by index. eg: "myArray.1" string literal will look for the key myArray and then the 2nd item if that value is an array (indices are 0 based)

Examples of JSON loading

From file

let filename = "myjsonfile"
let dictionary: [String: AnyObject] = try JSONDictionary.from(filename: filename)

From data

let data: Data = ...
let dictionary: [String: AnyObject] = try JSONDictionary.from(jsonData: data)

Examples of JSON decoding

Consider a JSON object that represents a person:

{
  "name" : "John Doe",
  "age" : 24,
  "weight" : 72.4
}

Decode JSON inline

let jsonDictionary = try JSONDictionary.from(filename: "person.json")
let name: String = try jsonDictionary.json(atKeyPath: "name")
let age: Int = try jsonDictionary.json(atKeyPath: "age")
let weight: Int = try jsonDictionary.json(atKeyPath: "weight")
let profession: String? = jsonDictionary.json(atKeyPath: "profession") // Optional decoding

Decode structs or classes

struct Person { //OR class Person {

  let name: String
  let age: Int
  let weight: Double
  let profession: String?

  init(jsonDictionary: JSONDictionary) throws {
    name = try jsonDictionary.json(atKeyPath: "name")
    age = try jsonDictionary.json(atKeyPath: "age")
    weight = try jsonDictionary.json(atKeyPath: "weight")
    profession = jsonDictionary.json(atKeyPath: "profession")
  }

}

Decode nested structs or classes by conforming to the JSONObjectConvertible protocol

Consider a company JSON object:

{
    "name" : "Working name LTD.",
    "employees": [
        {
            "name": "John Doe",
            "age": 24,
            "weight": 72.4
        },
        {
            "name": "Jane Doe",
            "age": 22,
            "weight": 70.1
        }
    ]
}

The Company struct can decode an array of Person structs/classes by making Person conform to the JSONObjectConvertible protocol

struct Company {
  let name: String
  let employees: [Person]

  init(jsonDictionary: JSONDictionary) throws {
    name = try jsonDictionary.json(atKeyPath: "name")
    employees = try jsonDictionary.json(atKeyPath: "employees")
  }
}

Support custom primitive types by conforming to JSONPrimitiveConvertible

Any type can extend the JSONPrimitiveConvertible protocol in order to allow decoding. For example extending URL: Note that this extension come out of the box :

extension URL: JSONPrimitiveConvertible {

  public typealias JSONType = String

  public static func from(jsonValue: String) -> Self? {
    return self.init(string: jsonValue)
  }

}

let urlDictionary = ["url": "www.google.com"]
let url: URL = try! urlDictionary.json(atKeyPath: "url") // www.google.com

It's also possible to have an array of JSONPrimitiveConvertible values, for example:

let urlsDictionary = ["urls": ["www.google.com", "www.yahoo.com"]]
let urls: [URL] = try! urlsDictionary.json(atKeyPath: "urls") // [www.google.com, www.yahoo.com]