iOSの通信からJSONマッピングまでを劇的に便利にするOvercoatを解説してみました（サンプルコード付き

サンプルのコードはこちら
https://github.com/unhappychoice/overcoat-github-example

## Overcoatとは

リポジトリはこちら
https://github.com/gonzalezreal/Overcoat

公式より引用

> Overcoat is a small but powerful library that makes creating REST clients simple and fun. 
> It provides a simple API for making requests and mapping responses to model objects.
> Overcoat is built on top of AFNetworking and uses Mantle to map responses into plain or Core Data model objects.

簡単に訳すと、
> OvercoatはRESTクライアントを作成することを簡単かつ楽しくする、小さいけれども強力なライブラリです.
> リクエストの作成や、responseをmodelへとマッピングするシンプルななAPIを提供します.
> AFNetworkingの上に作られており、CoreDataやplainなmodelへのマッピングはMantleを使用しています.

要するに、[AFNetworking](http://github.com/AFNetworking/AFNetworking)と[Mantle](http://github.com/Mantle/Mantle)をラップしてAPIサーバーとの通信からJSONのマッピング、
modelのインスタンス作成まで一発にやってしまおうというライブラリです

面倒なHTTP周りのほぼすべてを受け持ってくれ、設定を書くような感覚でAPIからオブジェクトを作成できます
**ビバ宣言的！**

インストールはCocoaPods経由で

```rb
pod Overcoat
pod Overcoat/PromiseKit    # おこのみで
pod Overcoat/ReactiveCocoa # おこのみで
```
です、簡単

それでは、サンプルコードの解説へ

## サンプル解説

サンプルコードはこちら
https://github.com/unhappychoice/overcoat-github-example

時代をときめく`ReactiveCocoa`でなく`PromiseKit`を使用しているのは単に勉強不足のためです...
RACを使う場合は `Overcoat/ReactiveCocoa`をPodfileに書いておいてください！

### ルートのViewController

```objc:UsersViewController.m
@implementation UsersViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadUsers];
    _tableView.delegate = self;
}

// ~~ 省略 ~~

- (void)loadUsers {
    _client = [GithubClient new];       // 通信用クラス OVCHTTPSessionManagerというOvercoatのサブクラス
    [_client fetchRandomUsers].then(^(NSArray *users){          // Promiseでuserの配列を受け取って...
        _dataSource = [[GithubUsersDataSource alloc] initWithUsers:users]; // Datasourceつくって...
        _tableView.dataSource = _dataSource;                               // tableViewに設定してあげて
        [_tableView reloadData];                                           // 画面に反映
    });
}
@end
```

なんてことはないですね
`viewDidLoad`で`loadUsers`を呼んで、APIからユーザーオブジェクトの配列をもらってきています

ここの`GithubClient`がOvercoatのAPIのサブクラスとなっています
ので、見て行きましょう

### Github APIリクエスト部分

```objc:GithubClient.h
@interface GithubClient : OVCHTTPSessionManager
- (PMKPromise *)fetchRandomUsers;  // ランダムなユーザーをAPIから取得
@end
```

`GithubClient`は`OVHTTPSessionManager`のサブクラスとなっています
おなじみの`AFNetworking`でいう、`AFHTTPSessionManager`に対応するクラスです

iOS7.0以上の対応であればこちら推奨なのですが、iOS6に対応させるためには、
`OVCHTTPRequestOperationManager`のほうを使用しないといけないようです

それでもって、実装の方はこんな感じ

```objc:GithubClient
@implementation GithubClient

# pragma mark - lifecycle

- (instancetype)init {
    // 見に行くAPIのベースのURL 今回はGithub APIを利用
    self = [super initWithBaseURL:[NSURL URLWithString:@"https://api.github.com/"]];
    return self;
}

+ (NSDictionary *)modelClassesByResourcePath { // URLパスに対応させるクラスの設定
    return @{
             // http://api.github.com/users をモデルのクラスGithubUserにマッピング
             @"users": [GithubUser class] 
             };
}

- (PMKPromise *)fetchRandomUsers {
   // 適当にランダムな数作成
    NSInteger index = arc4random() % 100000 + 100000;                            

    // リクエストするパラメーター作成
    // sinceで、取得するuserのIDを指定 ( 詳しくはGithubAPIのリファレンスへ...
    NSDictionary *params = @{@"since": [NSNumber numberWithInteger:index] };     

    // AFNetworkingと同じ使い勝手！
    return [self GET:@"users" parameters:params].then(^(OVCResponse *response) {
        return response.result; 
        //@[user1, user2, user3...] 上でマッピングしたGithubUserクラスの配列が帰ってくる!
    });
}
@end
```

API接続部分でほぼ必須なのは

```objc
- (id)initWithBaseURL:(NSURL *)url;
- (NSDictionary *)modelClassesByResourcePath;
```

を利用、実装することです
`modelClassesByResourcePath`では、モデルと、URLパスの関係を書いておきます
このあたり、すごく宣言的に書けてOvercoatの威力を感じるところですね！

みんな大好き`AFNetworking`と同様に、各種HTTPのメソッドが揃っています（GET, POST, PUT, DELETE, etc... 
これらのメソッドを利用すると、その結果の`OVCResponse`の`result`プロパティには、
`modelClassesByResourcePath`で設定したクラスのオブジェクトが勝手に入っています

つまり、JSON -> Modelの変換を勝手にやってくれているのですが、
その設定はModelに書いてあるので、見て行きましょう

### Model

ヘッダーはこんな感じ

```objc:GithubUser.h
#import <Mantle/Mantle.h>

@interface GithubUser : MTLModel <MTLJSONSerializing>
@property (copy, nonatomic, readonly) NSNumber *userID;
@property (copy, nonatomic, readonly) NSString *name;
@property (copy, nonatomic, readonly) NSURL *url;
@property (copy, nonatomic, readonly) NSURL *avatarURL;
@end
```

`Mantle`をインポートしているのがわかると思いますが、実のところModelの部分は`Mantle`の利用そのものになっています！

ので、他のプロジェクトで「`Mantle`は使ったことがある！」というような方はすぐに理解できるでしょう

中身を少し見てみると、Modelは`MTLModel`を継承し、`MTLJSONSerializing`を実装しています
それぞれ大雑把に言うと、

#### MTLModel
`NSCopying`や、`isEqual:`や、`description`などのメソッドのデフォルト実装で煩わしいboilerplateから開放してくれる

#### MTLJSONSerializing
JSONへ変換する設定を書く

という感じ

```objc:GithubUser.m
@implementation GithubUser

# pragma mark - MTLJSONSerializing

// JSONのキーとモデルのプロパティの対応を設定
// 左側がモデル、右側がJSONのキー
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
    return @{
             @"userID": @"id",
             @"name": @"login",
             @"url": @"html_url",
             @"avatarURL": @"avatar_url",
             };
}

// <プロパティ名>JSONTransformerというメソッドを実装すると、
// シリアライズの際に変換を行ってくれる

+ (NSValueTransformer *)urlJSONTransformer { // NSStringで受け取ったurlをNSURLに変換
    return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}

+ (NSValueTransformer *)avatarURLJSONTransformer { // 同上
    return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}

@end
```

はい、これだけです
ほぼ設定を書くだけ、と言った感じ

今回は簡単なクラスでしたが、もっと複雑な子や孫をもつリレーションでも設定できるので、怖いものなしです
( 詳しくは[Mantle](https://github.com/Mantle/Mantle)のドキュメントへ...

## まとめ

という感じで、宣言的かつ簡潔かつ可読性高くかけることが伝わっていただけたらと思います！
筆者はこれを知ってもう以前のboilerplate地獄には戻れなくなりました

その他のサンプル等は公式のリポジトリでも紹介されているので、チェックをしてみてください！