<commit_before>APIKit
======

[![Circle CI](https://img.shields.io/circleci/project/ishkawa/APIKit/master.svg?style=flat)](https://circleci.com/gh/ishkawa/APIKit)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)

APIKit is a library for building type safe web API client in Swift.

- Parameters of a request are validated by type system.
- Type of a response is inferred from type of its request.
- A result of request is represented by [Result<Value, Error>](https://github.com/antitypical/Result), which is also known as Either.
- All the endpoints can be enumerated in nested class.

```swift
let request = GitHub.Endpoint.SearchRepositories(query: "APIKit", sort: .Stars)

GitHub.sendRequest(request) { result in
    switch result {
    case .Success(let response):
        self.repositories = response // inferred as [Repository]
        self.tableView.reloadData()

    case .Failure(let error):
        print(error)
    }
}
```


## Requirements

- Swift 2
- iOS 8.0 or later
- Mac OS 10.9 or later

If you want to use APIKit with Swift 1.2, try [0.8.2](https://github.com/ishkawa/APIKit/releases/tag/0.8.2).

## Installation

#### [Carthage](https://github.com/Carthage/Carthage)

- Insert `github "ishkawa/APIKit"` to your Cartfile.
- Run `carthage update`.
- Link your app with `APIKit.framework` and `Result.framework` in `Carthage/Checkouts`.

#### [CocoaPods](https://github.com/cocoapods/cocoapods)

- Insert `pod "APIKit"` to your Podfile.
- Run `pod install`.

## Usage

1. Create a request protocol that inherits `Request` protocol.
2. Add `baseURL` variable in extension of request protocol.
3. Create a API class that inherits `API` class.
4. Define request types that conforms to request protocol in API class.
    1. Create a type that represents a request of the web API.
    2. Assign type that represents a response object to `Response` typealiase.
    3. Add `method` and `path` variables.
    4. Implement `buildResponseFromObject(_:URLResponse:)` to build `Response` from raw object, which may be an array or a dictionary.

```swift
protocol GitHubRequest: Request {

}

extension GitHubRequest {
    var baseURL: NSURL {
        return NSURL(string: "https://api.github.com")!
    }
}

class GitHubAPI: API {
    struct GetRateLimit: GitHubRequest {
        typealias Response = RateLimit

        var method: HTTPMethod {
            return .GET
        }

        var path: String {
            return "/rate_limit"
        }

        func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
            guard let dictionary = object as? [String: AnyObject] else {
                throw APIKitError.UnexpectedResponse
            }

            guard let rateLimit = RateLimit(dictionary: dictionary) else {
                throw APIKitError.UnexpectedResponse
            }

            return rateLimit
        }
    }
}

struct RateLimit {
    let count: Int
    let resetDate: NSDate

    init?(dictionary: [String: AnyObject]) {
        guard let count = dictionary["rate"]?["limit"] as? Int else {
            return nil
        }

        guard let resetDateString = dictionary["rate"]?["reset"] as? NSTimeInterval else {
            return nil
        }

        self.count = count
        self.resetDate = NSDate(timeIntervalSince1970: resetDateString)
    }
}
```

### Sending request

```swift
let request = GitHubAPI.Endpoint.GetRateLimit()

GitHubAPI.sendRequest(request) { result in
    switch result {
    case .Success(let rateLimit):
        print("count: \(rateLimit.count)")
        print("resetDate: \(rateLimit.resetDate)")

    case .Failure(let error):
        print("error: \(error)")
    }
}
```

### Canceling request

```swift
GitHub.cancelRequest(GitHub.Endpoint.RateLimit)
```

If you want to filter requests to be cancelled, add closure that identifies the request shoule be cancelled or not.

```swift
GitHub.cancelRequest(GitHub.Endpoint.SearchRepositories.self) { request in
    return request.query == "APIKit"
}
```

### Configuring request

APIKit uses following 4 properties in `Request` when build `NSURLRequest`.

```swift
var baseURL: NSURL
var method: HTTPMethod
var path: String
var parameters: [String: AnyObject]
```

`parameters` will be converted into query parameter if `method` is one of `.GET`, `.HEAD` and `.DELETE`. Otherwise, it will be serialized by `requestBodyBuilder` and set to `HTTPBody` of `NSURLRequest`.

#### Configuring format of HTTP body

APIKit uses `requestBodyBuilder` when it serialize parameter into HTTP body of request, and it use `responseBodyParser` when it deserialize object from HTTP body of response. Default format of body of request and response is JSON.

```swift
var requestBodyBuilder: RequestBodyBuilder
var responseBodyParser: ResponseBodyParser
```

You can specify format of HTTP body implement this property.

```swift
var requestBodyBuilder: RequestBodyBuilder {
    return .URL(encoding: NSUTF8StringEncoding)
}
```

#### Configuring manually

```
func configureURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest {
    // You can add any configurations here
}
```

### Configuring response

#### Setting acceptable status code

APIKit decides if a request is succeeded or failed using `acceptableStatusCodes:`. If it contains the status code of a response, the request is judged as succeeded and `API` calls `responseFromObject(_:URLResponse:)` to get model from raw response. Otherwise, the request is judged as failed and `API` calls `errorFromObject(_:URLResponse:)` to get error from raw response.

```swift
var acceptableStatusCodes: Set<Int> {
    return Set(200..<300)
}
```

#### Building a model from a response

```swift
func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
    guard let dictionary = object as? [String: AnyObject] else {
        throw APIKitError.UnexpectedResponse
    }

    guard let rateLimit = RateLimit(dictionary: dictionary) else {
        throw APIKitError.UnexpectedResponse
    }<commit_msg>Grammar<commit_after>APIKit
======

[![Circle CI](https://img.shields.io/circleci/project/ishkawa/APIKit/master.svg?style=flat)](https://circleci.com/gh/ishkawa/APIKit)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)

APIKit is a library for building type-safe web API client in Swift.

- Parameters of a request are validated by type-system.
- Type of a response is inferred from the type of its request.
- A result of a request is represented by [Result<Value, Error>](https://github.com/antitypical/Result), which is also known as Either.
- All the endpoints can be enumerated in nested class.

```swift
let request = GitHub.Endpoint.SearchRepositories(query: "APIKit", sort: .Stars)

GitHub.sendRequest(request) { result in
    switch result {
    case .Success(let response):
        self.repositories = response // inferred as [Repository]
        self.tableView.reloadData()

    case .Failure(let error):
        print(error)
    }
}
```


## Requirements

- Swift 2
- iOS 8.0 or later
- Mac OS 10.9 or later

If you want to use APIKit with Swift 1.2, try [0.8.2](https://github.com/ishkawa/APIKit/releases/tag/0.8.2).

## Installation

#### [Carthage](https://github.com/Carthage/Carthage)

- Insert `github "ishkawa/APIKit"` to your Cartfile.
- Run `carthage update`.
- Link your app with `APIKit.framework` and `Result.framework` in `Carthage/Checkouts`.

#### [CocoaPods](https://github.com/cocoapods/cocoapods)

- Insert `pod "APIKit"` to your Podfile.
- Run `pod install`.

## Usage

1. Create a request protocol that inherits `Request` protocol.
2. Add `baseURL` property in an extension of request protocol.
3. Create an API class that inherits `API` class.
4. Define request types that conform to request protocol in API class.
    1. Create a type that represents a request of the web API.
    2. Assign type that represents a response object to `Response` typealiase.
    3. Add `method` and `path` variables.
    4. Implement `buildResponseFromObject(_:URLResponse:)` to build `Response` from a raw object, which may be an array or a dictionary.

```swift
protocol GitHubRequest: Request {

}

extension GitHubRequest {
    var baseURL: NSURL {
        return NSURL(string: "https://api.github.com")!
    }
}

class GitHubAPI: API {
    struct GetRateLimit: GitHubRequest {
        typealias Response = RateLimit

        var method: HTTPMethod {
            return .GET
        }

        var path: String {
            return "/rate_limit"
        }

        func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
            guard let dictionary = object as? [String: AnyObject] else {
                throw APIKitError.UnexpectedResponse
            }

            guard let rateLimit = RateLimit(dictionary: dictionary) else {
                throw APIKitError.UnexpectedResponse
            }

            return rateLimit
        }
    }
}

struct RateLimit {
    let count: Int
    let resetDate: NSDate

    init?(dictionary: [String: AnyObject]) {
        guard let count = dictionary["rate"]?["limit"] as? Int else {
            return nil
        }

        guard let resetDateString = dictionary["rate"]?["reset"] as? NSTimeInterval else {
            return nil
        }

        self.count = count
        self.resetDate = NSDate(timeIntervalSince1970: resetDateString)
    }
}
```

### Sending request

```swift
let request = GitHubAPI.Endpoint.GetRateLimit()

GitHubAPI.sendRequest(request) { result in
    switch result {
    case .Success(let rateLimit):
        print("count: \(rateLimit.count)")
        print("resetDate: \(rateLimit.resetDate)")

    case .Failure(let error):
        print("error: \(error)")
    }
}
```

### Canceling request

```swift
GitHub.cancelRequest(GitHub.Endpoint.RateLimit)
```

If you want to filter requests to be cancelled, add closure that identifies the request should be cancelled or not.

```swift
GitHub.cancelRequest(GitHub.Endpoint.SearchRepositories.self) { request in
    return request.query == "APIKit"
}
```

### Configuring request

APIKit uses following 4 properties in `Request` when build `NSURLRequest`.

```swift
var baseURL: NSURL
var method: HTTPMethod
var path: String
var parameters: [String: AnyObject]
```

`parameters` will be converted into query parameter if `method` is one of `.GET`, `.HEAD` and `.DELETE`. Otherwise, it will be serialized by `requestBodyBuilder` and set to `HTTPBody` of `NSURLRequest`.

#### Configuring format of HTTP body

APIKit uses `requestBodyBuilder` when it serialize parameter into HTTP body of a request, and it uses `responseBodyParser` when it deserialize an object from HTTP body of a response. Default format of the body of request and response is JSON.

```swift
var requestBodyBuilder: RequestBodyBuilder
var responseBodyParser: ResponseBodyParser
```

You can specify the format of HTTP body implement this property.

```swift
var requestBodyBuilder: RequestBodyBuilder {
    return .URL(encoding: NSUTF8StringEncoding)
}
```

#### Configuring manually

```
func configureURLRequest(URLRequest: NSMutableURLRequest) throws -> NSMutableURLRequest {
    // You can add any configurations here
}
```

### Configuring response

#### Setting acceptable status code

APIKit decides if a request is succeeded or failed by using `acceptableStatusCodes:`. If it contains the status code of a response, the request is judged as succeeded and `API` calls `responseFromObject(_:URLResponse:)` to get a model from a raw response. Otherwise, the request is judged as failed and `API` calls `errorFromObject(_:URLResponse:)` to get an error from a raw response.

```swift
var acceptableStatusCodes: Set<Int> {
    return Set(200..<300)
}
```

#### Building a model from a response

```swift
func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
    guard let dictionary = object as? [String: AnyObject] else {
        throw APIKitError.UnexpectedResponse
    }

    guard let rateLimit = RateLimit(dictionary: dictionary) else {
        throw APIKitError.UnexpectedResponse
    }
{'fraction_non_alphanumeric': 0.10328867235079171, 'fraction_numerical': 0.006008932196508323, 'mean_word_length': 3.5262771040058802, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 18, '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': '30561', 'n_tokens_mistral': 3634, 'n_tokens_neox': 3436, 'n_words': 1214}