iOS 7でTwitterのTLを取得するまでのまとめ(2013-10-08版)


※筆者Objective-C歴1ヶ月の初心者ですが勉強の為書かせて頂きます お見苦しい箇所等御座いますがご容赦ください．（何かありましたらコメント是非お願い致します）

↓このページを雑に翻訳させて頂きたいと思います↓

API requests with SLRequest
https://dev.twitter.com/docs/ios/making-api-requests-slrequest

#概説

SocialクラスのSLRequestというのを使います REST API（ツイートの取得や投稿等Twitterとの情報をやりとりする仕組み）を叩いてくれます
Twitter API v1.1の全てのREST APIに対する request は認証(OAuth)が必要です．

#ざっとrequestの送り方

1. SLRequestの **requestForServiceType** を叩く．
1. SLRequestの **setAccount** を使ってACAccountのインスタンスをアサイン
1. SLRequestの **performRequestWithHandler** を叩く

#例-ユーザのタイムライン取得

まずプロジェクトにSocial.frameworkとAccounts.frameworkを紐付けましょう


```Objective-C
// SCSimpleSLRequestDemo.m
#import <Accounts/Accounts.h>
#import <Social/Social.h>
#import "SCSimpleSLRequestDemo.h"
```

アカウント情報を持つインスタンスをつくります
@interface 〜 @end の間に入力します

```Objective-C
@property (nonatomic) ACAccountStore *accountStore;
```

initです
accountStoreを初期化します

```Objective-C
- (id)init
{
  self = [super init];
  if (self) {
    _accountStore = [[ACAccountStore alloc] init];
  }
  return self;
}
```

次にiDeviceになにかTwitter アカウントが紐付いているか問い合わせる(BOOL)userHasAccessToTwitter です．（シミュレータだと必ず YES を返してくるらしい・・・）

```Objective-C
- (BOOL)userHasAccessToTwitter
{
  return [SLComposeViewController
          isAvailableForServiceType:SLServiceTypeTwitter];
}
```

SLComposeViewControllerというのはiOSプリインのTwitter共有する画面のやつです．Safariでページを共有しようとすると出てくるアレ
これにiDeviceにアカウントがあるか問い合わせるメソッドがあるようです

最後にTimelineを取得する(void)fetchTimelineForUserです ゆっくり行きたいと思います．

### iDeviceにTwitter アカウントがあるか確認する

さっき作ったuserHasAccessToTwitterをたたけば一発ですね．

```Objective-C
- (void)fetchTimelineForUser:(NSString *)username
{
  //  Step 0: Check that the user has local Twitter accounts
  if ([self userHasAccessToTwitter]) {
 
```

紐付けられているアカウントが特にない場合このプログラムは終了します
else 節でNSLog吐かせても良いと思います

### アカウントへのアクセス

```Objective-C
//  Step 1:  Obtain access to the user's Twitter accounts
ACAccountType *twitterAccountType =
 [self.accountStore accountTypeWithAccountTypeIdentifier:
  ACAccountTypeIdentifierTwitter];
```

ACAccountType の **accountTypeWithAccountTypeIdentifier** メソッドでiDeviceに紐付いている指定したSNSのアカウントのひな形を持ってこれます（？）．
今回は Twitter なので引数に ACAccountTypeIdentifierTwitter を指定していきましょう

```Objective-C
[self.accountStore
     requestAccessToAccountsWithType:twitterAccountType
     options:NULL
     completion:^(BOOL granted, NSError *error) {
```

次です
ここに来て，最初に作っておいた accountStore インスタンスを使っていきます
たたくのは **requestAccessToAccountsWithType** メソッドです．これがユーザの保護された領域にアクセスする権限を取りに行ってくれます
typeは *twitterAccountType* optionsは特にないので *NULL* completionですがブロック変数を
書いていきます．※ブロック変数については[こちら](http://www.objectivec-iphone.com/introduction/block/block.html)を参照してください

### リクエストの生成

```Objective-C
if (granted) {
         //  Step 2:  Create a request
         NSArray *twitterAccounts =
         [self.accountStore accountsWithAccountType:twitterAccountType];
         NSURL *url = [NSURL URLWithString:@"https://api.twitter.com"
                       @"/1.1/statuses/user_timeline.json"];
         NSDictionary *params = @{@"screen_name" : username,
                                  @"include_rts" : @"0",
                                  @"trim_user" : @"1",
                                  @"count" : @"1"};
         SLRequest *request =
         [SLRequest requestForServiceType:SLServiceTypeTwitter
                            requestMethod:SLRequestMethodGET
                                      URL:url
                               parameters:params];
 
         //  Attach an account to the request
         [request setAccount:[twitterAccounts lastObject]];
```

if(granted) ですがまずこのメソッドが通ったか通っていないかでフィルターをかけます（？）．通らなければ後の else 節でNSLogを吐きます
通った場合， まずaccountStore の **accountsWithAccountType** メソッドをたたき，iDeviceに入っているTwitter アカウントたちを配列で取得します．
次にNSURLクラスでTLを取得するAPIのURLを持たせて
NSDictionaryクラスのparamsに今回拾いたい値を入れていきます

最後にSLRequestのオブジェクトを **requestForServiceType** メソッドで初期化しておきます
初期化できたら **setAccount** メソッドでtwitterAccounts配列にある最後のアカウントを指定しておきます．

### リクエストの実行

```Objective-C
//  Step 3:  Execute the request
         [request performRequestWithHandler:
          ^(NSData *responseData,
            NSHTTPURLResponse *urlResponse,
            NSError *error) {
 
           if (responseData) {
             if (urlResponse.statusCode >= 200 &&
                 urlResponse.statusCode < 300) {
 
               NSError *jsonError;
               NSDictionary *timelineData =
               [NSJSONSerialization
                JSONObjectWithData:responseData
                options:NSJSONReadingAllowFragments error:&jsonError];
                if (timelineData) {
                 NSLog(@"Timeline Response: %@\n", timelineData);
               }
               else {
                 // Our JSON deserialization went awry
                 NSLog(@"JSON Error: %@", [jsonError localizedDescription]);
               }
             }
             else {
               // The server did not respond ... were we rate-limited?
               NSLog(@"The response status code is %d",
                urlResponse.statusCode);
             }
           }
         }];
       }
       else {
         // Access was not granted, or an error occurred
         NSLog(@"%@", [error localizedDescription]);
       }
     }];
  }
}
@end
```

最後までいってしまいましたが，1行ずつ見ていきましょう
早速requestの **performRequestWithHandler** を使います
ここにもブロック構文が入ってます
宣言するのは返ってきたつぶやきのデータを格納する（？）NSData型のresponseData，NSHTTPURLResponse型のurlResponse，あとNSErrorのerrorとなります

responseDataが返ってきたらurlResponseのHTTPステータスコードを確認します HTTPステータスコードですが，

|数字|意味|
|:--|--:|
|1xx|情報|
|2xx|成功|
|3xx|リダイレクション|
|4xx|クライアントエラー|
|5xx|サーバエラー|

です．これが200台でなければエラーなのでそれをチェックします．

これから responseData を解体していきます
まず json の解体に失敗した場合のために NSError 型のオブジェクトを持っておきます
次に NSDictionary の timelineData オブジェクトを **NSJSONSerialization** クラスの **JSONObjectWithData** メソッドで初期化していきます．引数に入れるのは responseData 
options に NSJSONReadingAllowFragments ， error は jsonError オブジェクトです．
NSJSONReadingAllowFragments ですが，「断片化を許容する」という感じの意味っぽいです．

timelineData が存在すればそれをコンソールに書き出していきます．そうでなければ JSON Error ということで localizedDescription メソッドでエラーメッセージを書きだしてもらいます．
次の else 節は urlResponse の statusCode が200台でなかった場合のものですがここではその statusCode を書き出してエラーメッセージということにしています．
最後の else 節は accountStore の requestAccessToAccountsWithType が失敗した時のものですね．

# テストしてみた

Xcode で Empty Application → TwitterExample というプロジェクト名で開始
上記のように SCSimpleSLRequestDemo.m を作って（スーパクラスは適当に NSObject とかで？），適宜 SCSimpleSLRequestDemo.h を修正

```Objective-C:SCSimpleSLRequestDemo.h
@interface SCSimpleSLRequestDemo : NSObject

- (void)fetchTimelineForUser:(NSString *)username;

@end

```

AppDelegate.m で fetchTimelineForUser をたたく

```Objective-C:AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    
    SCSimpleSLRequestDemo *request = [[SCSimpleSLRequestDemo alloc] init];
    [request fetchTimelineForUser:@"（取得したい Twitter アカウントID）"];
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    return YES;

}
```

iPhone シミュレータで事前に何か1つ以上Twitter アカウントを連携させておく．
これで Run を押せば

```
2014-09-06 13:36:34.542 TwitterExample[0000:0x00] Timeline Response: (
        {
        contributors = "<null>";
        coordinates = "<null>";
        "created_at" = "Sat Sep 06 00:00:00 +0000 2014";
        entities =         {
            hashtags =             (
            );
            symbols =             (
            );
            urls =             (
                                {
                    "display_url" = "xxxxxxxx";
                    "expanded_url" = "http://xxxxxxxx";
                    indices =                     (
                        00,
                        00
                    );
                    url = "http://t.co/xxxxxxxxx";
                }
            );
            "user_mentions" =             (
            );
        };
        "favorite_count" = 0;
        favorited = 0;
        geo = "<null>";
        id = 000000000000000000;
        "id_str" = 000000000000000000;
        "in_reply_to_screen_name" = "<null>";
        "in_reply_to_status_id" = "<null>";
        "in_reply_to_status_id_str" = "<null>";
        "in_reply_to_user_id" = "<null>";
        "in_reply_to_user_id_str" = "<null>";
        lang = ja;
        place = "<null>";
        "possibly_sensitive" = 0;
        "retweet_count" = 0;
        retweeted = 0;
        source = "<a href=\"https://hoge" rel=\"nofollow\">Twitter for Websites</a>";
        text = "JSONObjectWithData:options:error: XXXX - XXXX http://t.co/xxxxxxxxx";
        truncated = 0;
        user =         {
            id = 00000000;
            "id_str" = 00000000;
        };
    }
)

```

このように（プライバシーの為適宜0なりXで隠しましたが）コンソールにタイムラインのデータが無事読み込まれました
動かない場合はシミュレータ側にアカウントを紐付けているかもう一度確認してみてください．