LoginSignup
48
44

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-05-15

サンプルのコードはこちら
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を使用しています.

要するに、AFNetworkingMantleをラップして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

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

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

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

Github APIリクエスト部分

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

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

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

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

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接続部分でほぼ必須なのは

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

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

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

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

Model

ヘッダーはこんな感じ

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へ変換する設定を書く

という感じ

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のドキュメントへ...

まとめ

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

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

48
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
44