APIクライアントをAFNetworkingを用いてつくる手順

「ちょっとこの Web API 使ってなんかつくってみるか」ということがたまにある（とくにハッカソンで）ので、手早くつくれるように僕なりの手順をまとめてみました。

「共通処理の実装」までは簡単な文字列の置換で済むので、 **ファイルテンプレートにしておく** とより簡単になりそうです（参考：[Xcodeのファイルテンプレートを自作する](http://qiita.com/shu223/items/5d8a245e5743f77df9eb)）。

##1. 共通処理の実装

APIキーをセットするメソッドの定義と実装と、オブジェクト初期化用のメソッド実装。


###ヘッダ

````
+ (void)setAPIKey:(NSString *)APIKey;
````

###実装ファイル

````
#import "AFJSONRequestOperation.h"
````

````
#define API_BASE_URL      @"http://xxxx"
````

````
@interface XXXX ()
@property (nonatomic) NSString *APIKey;
@end
````

````
+ (XXXX *)sharedClient
{
    static XXXX *sharedClient = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedClient = [[XXXX alloc] initWithBaseURL:[NSURL URLWithString:API_BASE_URL]];
    });
    
    return sharedClient;
}

- (id)initWithBaseURL:(NSURL *)url
{
    if (self = [super initWithBaseURL:url]) {
        
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
        [self setParameterEncoding:AFJSONParameterEncoding];
        [self setDefaultHeader:@"Accept"     value:@"application/json"];
    }
    
    return self;
}

+ (void)setAPIKey:(NSString *)APIKey {
    
    [[XXXX sharedClient] setAPIKey:APIKey];
}
````


##2. 汎用Privateメソッドの実装

ここは各サービスの仕様に合わせて実装を変える必要がある。APIキーをセットする、とか、そのサービスにおけるAPIをたたく際の共通の処理をここで行う。

（APIキーをパラメータに渡す場合）

````
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                      path:(NSString *)path
                                parameters:(NSDictionary *)parameters
{
    NSAssert(self.APIKey, @"API Key has not been set.\n\n");
    
    path = [path stringByAppendingFormat:@"?api_key=%@", self.APIKey];

    for (NSString *key in [parameters keyEnumerator]) {
        
        NSString *value = [NSString stringWithFormat:@"%@",
                           [parameters valueForKey:key]];
        
        NSAssert2([key length] && [value length],
                  @"invalid param! key:%@: value:%@", key, value);
        
        path = [path stringByAppendingFormat:@"&%@=%@", key, value];
    }
    
    NSMutableURLRequest *req = [super requestWithMethod:method
                                                   path:path
                                             parameters:nil];
        
    return req;
}
````

##3. 各APIに対応したメソッドを書く

````
- (void)companyWithName:(NSString *)name
                handler:(void (^)(NSDictionary *result, NSError *error))handler
{
    NSAssert([name length], @"name is required");
    
    __weak XXXX *weakSelf = self;
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        NSString *path = [NSString stringWithFormat:@"company/%@.js", name];

        [weakSelf getPath:path
               parameters:nil
                  success:^(AFHTTPRequestOperation *operation, id responseObject) {
                      
                      dispatch_async(dispatch_get_main_queue(), ^{
                          
                          handler(responseObject, nil);
                      });
                      
                  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                      
                      dispatch_async(dispatch_get_main_queue(), ^{
                          
                          handler(nil, error);
                      });
                  }];
    });
}
````

##サンプル

この手順でつくったAPIクライアント

- [CrunchBase API Client for iOS](https://github.com/shu223/CrunchBase-API-Client-for-iOS)
  - 米国のテック系企業の情報が詰まったデータベース『CrunchBase』のAPIクライアント
  - 記事：[CrunchBase APIをiOSアプリで使用する手順のメモ、およびAPIクライアントつくりました](http://d.hatena.ne.jp/shu223/20130908/1378616621)
- [Klout API Client for iOS](https://github.com/shu223/Klout-API-Client-for-iOS)
  - Klout スコアを取得するAPIクライアント
  - 記事：[Kloutスコアを取得するライブラリをつくりました](http://d.hatena.ne.jp/shu223/20130908/1378629298)