iOSの設定で認証済みのTwitterアカウントを用いて、Twitter APIからタイムラインを取得したり、ツイートの投稿をしたい場合があります。今回はその方法を説明します。

※ iOS 6で仕様が変わったので、それに準じて、SLRequest / SLComposeViewControllerを使う方法を説明します。

準備

以下のフレームワークをプロジェクトへ追加してください。

  • Accounts.framework
  • Social.framework

Twitter Developersで得られるConsumerKeyやConsumerSecretは必要ありません。

iOSで認証済みのTwitterアカウントの取得

まずはACAccountStoreオブジェクトを生成し、ViewControllerのメンバ変数などで保持するようにしてください。

_accountStore = [[ACAccountStore alloc] init];

次にTwitterのACAccountTypeオブジェクトを取得し、それを元にアカウントを取得します。

ACAccountType *accountType = [_accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
NSArray *accounts = [_accountStore accountsWithAccountType:accountType];
if (accounts.count == 0) {
  NSLog(@"Please add twitter account on Settings");
  return;
}
  • ACAccountStore#accountTypeWithAccountTypeIdentifier – ACAccountTypeの取得
  • ACAccountStore#accountsWithAccountType – アカウント(ACAccount)を配列で取得

この段階でアカウントが取得できないということはiOSの設定でTwitterアカウントが1つも登録されていない状態です。これ以上は何もできませんので、Alertなどでユーザーに追加を促すなどしてください。

アカウントを取得できても、アカウントを使ってAPIにアクセスするなどの許可はまだありません。ユーザーの許可を得るために以下のメソッドを呼びます。

[_accountStore requestAccessToAccountsWithType:accountType
  options:nil
  completion:^(BOOL granted, NSError *error) {
    dispatch_async(dispatch_get_main_queue(), ^{
      if (granted) {
        _grantedAccounts = [_accountStore accountsWithAccountType:accountType];
      } else {
        NSLog(@"User denied to access twitter account.");
      }
    });
}];

ACAccountStore#requestAccessToAccountsWithTypeを呼ぶと、まだアプリに対してユーザーが許可していないなら、以下の様なAlertが表示されます。

alert

Alertのボタンを押すと、completionブロック関数が呼ばれます。許可したらなら引数grantedはYESです。2回目以降はこのメソッドを呼んでもAlert表示されません。その場合、completionブロックのgrantedの値は前回Alertで決定したものとなります

ACAccountStore#requestAccessToAccountsWithTypeのcompletionブロック関数はメインスレッドでないことをに気をつけてください。dispatch_get_main_queue()を使っているのはそのためです。

SLRequestでタイムラインの取得

ユーザーのホーム画面で表示されるタイムラインを取得する場合は以下のようにします。

NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/home_timeline.json"];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                        requestMethod:SLRequestMethodGET
                                                  URL:url
                                           parameters:nil];
// とりあえず取得したアカウントの一番最初のものを使う
[request setAccount:[_grantedAccounts objectAtIndex:0]];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
  dispatch_async(dispatch_get_main_queue(), ^{
    NSUInteger statusCode = urlResponse.statusCode;
    if (200 <= statusCode && statusCode < 300) {
       // JSONをパース
       NSArray *tweets = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil];
       // 何らかの処理
    } else {
       // エラー時の処理
    }
  });
}];
  • SLRequest#requestForServiceTypeでSLRequestオブジェクトを生成する。SLServiceTypeTwitterを指定
  • SLRequest#setAccountでアカウントを指定
  • SLRequest#performRequestWithHandlerでAPIにリクエストを投げる。ブロック関数で非同期にレスポンスが得られる。

SLRequest#performRequestWithHandlerのブロック関数もメインスレッドではないので、dispatch_get_main_queue()を使っています。

SLRequestでツイートの投稿

NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/update.json"];
NSDictionary *parameters = @{@"status": @"〜〜ツイートの内容〜〜"};
SLRequest * request = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                         requestMethod:SLRequestMethodPOST // POST
                                                  URL:url
                                           parameters:parameters];
[request setAccount:[_grantedAccounts objectAtIndex:0]];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
   dispatch_async(dispatch_get_main_queue(), ^{
      NSUInteger statusCode = urlResponse.statusCode;
      if (200 <= statusCode && statusCode < 300) {
〜〜あとはタイムライン取得の時と同様に〜〜

基本的にはタイムライン取得の時と同じです。違いは、SLRequest生成時にPOSTメソッドを示すSLRequestMethodPOSTを指定していることと、今回は投稿内容をparametersに渡していることです。

SLComposeViewControllerによる投稿

投稿画面を表示してユーザーが入力した内容を投稿させたいときは、SLComposeViewControllerを使うと楽です。

tweet

見栄えも良いですね。たぶんiOS 7になればデザインもそれに準拠したものになるはずです。特に理由がない限りは無理して自分で作らないほうが良いでしょう。

以下のようにして、この画面を表示できます。

if (![SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) return;

SLComposeViewController *con =
  [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[con setCompletionHandler:^(SLComposeViewControllerResult result) {
   if (result == SLComposeViewControllerResultDone) {
      // Dismiss automatically.
   } else if (result == SLComposeViewControllerResultCancelled) {
      [self dismissViewControllerAnimated:YES completion:nil];
   }
}];
[self presentViewController:con animated:YES completion:nil];
  • SLComposeViewController#isAvailableForServiceTypeで、Twitterへの投稿が可能かどうかを確認。
  • SLComposeViewController#composeViewControllerForServiceTypeで、投稿画面のViewControllerを生成。
  • SLComposeViewController#setCompletionHandlerで、Send(Done)、Cancelボタンを押した時の処理をする。

簡単ですね。

ただCancelボタンの挙動がおかしいです・・・2回押すと自動で画面が閉じられ1回目で閉じられません。なので、Cancelを押した場合は明示的にdismissViewControllerを呼んだ方が良さそうです。Sendボタンは1回目で自動で閉じてくれます。

またSLComposeViewControllerの以下のメソッドも場合によっては便利だと思います。

  • setInitialText – 最初から入力状態の文字列を設定
  • addImage – 最初から添付状態の画像を設定

 

以上、Twitter APIに関する説明でした。
今回のサンプルコードはGitHubにコミットしてあります。
https://github.com/stack3/ACAccountSamples