サンプルのコードはこちら
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とMantleをラップしてAPIサーバーとの通信からJSONのマッピング、
modelのインスタンス作成まで一発にやってしまおうというライブラリです
面倒なHTTP周りのほぼすべてを受け持ってくれ、設定を書くような感覚でAPIからオブジェクトを作成できます
ビバ宣言的!
インストールはCocoaPods経由で
pod Overcoat
pod Overcoat/PromiseKit # おこのみで
pod Overcoat/ReactiveCocoa # おこのみで
です、簡単
それでは、サンプルコードの解説へ
サンプル解説
サンプルコードはこちら
https://github.com/unhappychoice/overcoat-github-example
時代をときめくReactiveCocoa
でなくPromiseKit
を使用しているのは単に勉強不足のためです...
RACを使う場合は Overcoat/ReactiveCocoa
をPodfileに書いておいてください!
ルートのViewController
@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リクエスト部分
@interface GithubClient : OVCHTTPSessionManager
- (PMKPromise *)fetchRandomUsers; // ランダムなユーザーをAPIから取得
@end
GithubClient
はOVHTTPSessionManager
のサブクラスとなっています
おなじみのAFNetworking
でいう、AFHTTPSessionManager
に対応するクラスです
iOS7.0以上の対応であればこちら推奨なのですが、iOS6に対応させるためには、
OVCHTTPRequestOperationManager
のほうを使用しないといけないようです
それでもって、実装の方はこんな感じ
@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接続部分でほぼ必須なのは
- (id)initWithBaseURL:(NSURL *)url;
- (NSDictionary *)modelClassesByResourcePath;
を利用、実装することです
modelClassesByResourcePath
では、モデルと、URLパスの関係を書いておきます
このあたり、すごく宣言的に書けてOvercoatの威力を感じるところですね!
みんな大好きAFNetworking
と同様に、各種HTTPのメソッドが揃っています(GET, POST, PUT, DELETE, etc...
これらのメソッドを利用すると、その結果のOVCResponse
のresult
プロパティには、
modelClassesByResourcePath
で設定したクラスのオブジェクトが勝手に入っています
つまり、JSON -> Modelの変換を勝手にやってくれているのですが、
その設定はModelに書いてあるので、見て行きましょう
Model
ヘッダーはこんな感じ
#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へ変換する設定を書く
という感じ
@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のドキュメントへ...
まとめ
という感じで、宣言的かつ簡潔かつ可読性高くかけることが伝わっていただけたらと思います!
筆者はこれを知ってもう以前のboilerplate地獄には戻れなくなりました
その他のサンプル等は公式のリポジトリでも紹介されているので、チェックをしてみてください!