<commit_before># Decodable
Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by [Argo](http://github.com/thoughtbot/Argo), but without a bizillion functional operators.

[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Cocoapods version](https://cocoapod-badges.herokuapp.com/v/Decodable/badge.png)](https://cocoapods.org/pods/Decodable)
[![Platforms](https://cocoapod-badges.herokuapp.com/p/Decodable/badge.png)](https://cocoadocs.org/docsets/NSStringMask)
[![Travis](https://img.shields.io/travis/Anviking/Decodable/master.svg)](https://travis-ci.org/Anviking/Decodable/branches)


```swift
struct Repository {
    let name: String
    let description: String
    let stargazersCount: Int
    let language: String?
    let sometimesMissingKey: String?
    
    let owner: User // Struct conforming to Decodable
    let defaultBranch: Branch // Struct NOT conforming to Decodable
    
    var fullName: String { return "\(owner.login)/\(name)" }
}

extension Repository: Decodable {
    static func decode(j: Any) throws -> Repository {
        return try Repository(
                    name: j => "nested" => "name", 
                    description: j => "description", 
                    stargazersCount: j => "stargazers_count", 
                    language: j => "language", 
                    sometimesMissingKey: j =>? "sometimesMissingKey",
                    owner: j => "owner", 
                    defaultBranch: Branch(name: j => "default_branch")
                )
    }
}

do {
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
    let repo = try [Repository].decode(json)
} catch {
    print(error)
}
```

## How does it work?

### A protocol
```swift
public protocol Decodable {
    static func decode(json: AnyObject) throws -> Self
}
```
### A parse-function
```swift
public func parse<T>(json: AnyObject, path: [String], decode: (AnyObject throws -> T)) throws -> T
```

### And shameless operator-overloading
The too-many generated overloads, all calling the `parse`-function, can be found in [Overloads.swift](https://github.com/Anviking/Decodable/blob/master/Sources/Overloads.swift). Return types include `T?`, `[T?]`, `[T?]?`, `AnyObject` and `[String: T]?`. When conditional protocol conformance is supported in Swift this won't be necessary, and automagic decoding of infinitly nested generic types (like `[[[[[[[[[A???]]: B]]]?]]?]]`) would work.

An overload may look like this:
```swift
public func => <T: Decodable>(json: AnyObject, keyPath: KeyPath) throws -> T
```

## KeyPaths
Keypaths can be created from string and array literals as well as with explicit initializers. They can also be joined using the operators `=>` and `=>?`. `=>?` is another operator that indicates that `nil` should be returned if the key to the right is missing.

- When composing `=>` and `=>?` operators in the same keypath, the strictness of `=>` is still honoured.
- Optional key paths (`=>?`) require an optional return type

```swift
let a: KeyPath = "a"
let b: KeyPath = ["a", "b"]
let c: KeyPath = "a" => "b" => "c"
let string: String? = json =>? "key1" => "key2" => "key3"`
                                ^^^^ allowed to be missing
```
## Errors
Errors will be caught and rethrown in the decoding process to backpropagate metadata, like the JSON object that failed decoding, the key path to it, and the root JSON object.

From [DecodingError.swift](https://github.com/anviking/decodable/tree/master/Sources/DecodingError.swift):
```swift
public enum DecodingError: ErrorProtocol, Equatable {
    /// Thrown when optional casting from `AnyObject` fails.
    ///
    /// This can happen both when trying to access a key on a object
    /// that isn't a `NSDictionary`, and failing to cast a `Castable`
    /// primitive.
    case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)<commit_msg>Swift 3 updates for AnyObject -> Any<commit_after># Decodable
Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by [Argo](http://github.com/thoughtbot/Argo), but without a bizillion functional operators.

[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Cocoapods version](https://cocoapod-badges.herokuapp.com/v/Decodable/badge.png)](https://cocoapods.org/pods/Decodable)
[![Platforms](https://cocoapod-badges.herokuapp.com/p/Decodable/badge.png)](https://cocoadocs.org/docsets/NSStringMask)
[![Travis](https://img.shields.io/travis/Anviking/Decodable/master.svg)](https://travis-ci.org/Anviking/Decodable/branches)


```swift
struct Repository {
    let name: String
    let description: String
    let stargazersCount: Int
    let language: String?
    let sometimesMissingKey: String?
    
    let owner: User // Struct conforming to Decodable
    let defaultBranch: Branch // Struct NOT conforming to Decodable
    
    var fullName: String { return "\(owner.login)/\(name)" }
}

extension Repository: Decodable {
    static func decode(j: Any) throws -> Repository {
        return try Repository(
                    name: j => "nested" => "name", 
                    description: j => "description", 
                    stargazersCount: j => "stargazers_count", 
                    language: j => "language", 
                    sometimesMissingKey: j =>? "sometimesMissingKey",
                    owner: j => "owner", 
                    defaultBranch: Branch(name: j => "default_branch")
                )
    }
}

do {
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
    let repo = try [Repository].decode(json)
} catch {
    print(error)
}
```

## How does it work?

### A protocol
```swift
public protocol Decodable {
    static func decode(json: Any) throws -> Self
}
```
### A parse-function
```swift
public func parse<T>(json: Any, path: [String], decode: (Any throws -> T)) throws -> T
```

### And shameless operator-overloading
The too-many generated overloads, all calling the `parse`-function, can be found in [Overloads.swift](https://github.com/Anviking/Decodable/blob/master/Sources/Overloads.swift). Return types include `T?`, `[T?]`, `[T?]?`, `Any` and `[String: T]?`. When conditional protocol conformance is supported in Swift this won't be necessary, and automagic decoding of infinitly nested generic types (like `[[[[[[[[[A???]]: B]]]?]]?]]`) would work.

An overload may look like this:
```swift
public func => <T: Decodable>(json: Any, keyPath: KeyPath) throws -> T
```

## KeyPaths
Keypaths can be created from string and array literals as well as with explicit initializers. They can also be joined using the operators `=>` and `=>?`. `=>?` is another operator that indicates that `nil` should be returned if the key to the right is missing.

- When composing `=>` and `=>?` operators in the same keypath, the strictness of `=>` is still honoured.
- Optional key paths (`=>?`) require an optional return type

```swift
let a: KeyPath = "a"
let b: KeyPath = ["a", "b"]
let c: KeyPath = "a" => "b" => "c"
let string: String? = json =>? "key1" => "key2" => "key3"`
                                ^^^^ allowed to be missing
```
## Errors
Errors will be caught and rethrown in the decoding process to backpropagate metadata, like the JSON object that failed decoding, the key path to it, and the root JSON object.

From [DecodingError.swift](https://github.com/anviking/decodable/tree/master/Sources/DecodingError.swift):
```swift
public enum DecodingError: ErrorProtocol, Equatable {
    /// Thrown when optional casting from `Any` fails.
    ///
    /// This can happen both when trying to access a key on a object
    /// that isn't a `NSDictionary`, and failing to cast a `Castable`
    /// primitive.
    case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)
{'fraction_non_alphanumeric': 0.13697258044322022, 'fraction_numerical': 0.0018780518342306248, 'mean_word_length': 3.4625698324022345, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 60, 'https://': 20, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '275685', 'n_tokens_mistral': 2502, 'n_tokens_neox': 2368, 'n_words': 770}