[iOS] Social Framework の「SLRequest」を使って Twitter に投稿する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは。モバイルアプリサービス部の平屋です。

本記事では、Social Framework の「SLRequest」クラスを使って Twitter に投稿する手順を紹介します。

SLComposeViewController と SLRequest

前回の記事「[iOS] 標準コンポーネント「SLComposeViewController」を使って Twitter や Facebook に投稿する」では、Social Framework の「SLComposeViewController」クラスを使って Twitter/Facebook に投稿させる方法を紹介しました。

「SLComposeViewController」クラスを使えば、ほんの少しのコードを追加するだけで SNS へ投稿する機能を提供できますが、以下の場合は「SLComposeViewController」クラスを使えません。

  • カスタムの投稿画面を使いたい
  • ユーザーの代わりに投稿したい

上記の場合は、以下のような方法で投稿機能を実装する必要があります。

  • Social Framework の SLRequest クラスを使う
  • Fabric の Twitter Kit を使う
  • Twitter の REST API にアクセスするクラスを自作して使う

本記事では Social Framework の「SLRequest」クラスを使って Twitter に投稿する方法を紹介します。

サンプルアプリについて

本記事で解説するコードは以下のリポジトリで公開してます。

twitter-post-with-slrequest-002

検証環境

  • OS X El Capitan 10.11.4
  • Xcode 7.3.1
  • 検証した iOS 端末
    • iPhone 4s iOS 8.4.1
    • iPhone 5s iOS 9.3.1

目次

ACAccountStore を使用してアカウント情報を取得する

前回と同様に、本記事でも OS の「設定アプリ」に設定されている Twitter アカウントを使用します。

まずは、投稿に使用するアカウント情報を取得する実装から見ていきましょう。

ACAccountStore と ACAccountType を作成する

「設定アプリ」に設定されているアカウントの情報を取得するには、Accounts Framework の ACAccountStore クラスを使用します。また、ACAccountType はアカウントのタイプに関する情報を保持するクラスであり、次の「ACAccount を取得する」で使用します。

@interface SPDTwitterClient ()

@property (nonatomic) ACAccountStore *accountStore;
@property (nonatomic) ACAccountType *accountType;

@end

@implementation SPDTwitterClient

- (instancetype)init
{
    self = [super init];

    if (self) {
        _accountStore = [ACAccountStore new];
        _accountType = [_accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    }

    return self;
}

...

@end

ACAccount を取得する

ACAccount は 1 つのアカウントの情報を保持するクラスです。ACAccountStore を使用すれば、ACAccount を取得できます。

まずは、ACAccountStorerequestAccessToAccountsWithType:options:completion: メソッドを使用して、アカウントへのアクセスを要求します。

- (void)loadAccountsWithCompletion:(void (^)(NSArray *accounts, NSError *error))completion
{
    [self.accountStore requestAccessToAccountsWithType:self.accountType
                                               options:nil
                                            completion:^(BOOL granted, NSError *error) {
                                                dispatch_async(dispatch_get_main_queue(), ^{
                                                    if (granted) {
                                                        NSArray *accounts =
                                                        [self.accountStore accountsWithAccountType:self.accountType];
                                                        completion(accounts, nil);
                                                    } else {
                                                        completion(nil, error);
                                                    }
                                                });
                                            }];
}

requestAccessToAccountsWithType:options:completion: メソッドを初めて呼んだタイミングで以下のアラートが表示されます。「許可しない」または「OK」のどちらかが既に選択されている場合は、アラートは表示されません。

twitter-post-with-slrequest-001

ユーザーがアラート上のボタンをタップすれば、completion ブロックの中身が実行されます。「OK」がタップされたのであれば、granted の値が YES になり、ACAccountStoreaccountsWithAccountType: メソッドを使用して ACAccount の配列を取得できます。

Twitter の場合は「設定アプリ」に複数のアカウントを設定できるので、複数の ACAccount が返ってくる可能性があります。アプリによっては、複数のアカウントの中から 1 つのアカウントを選択させる UI を提供したほうが良いかもしれません。

また、completion ブロックは任意のキューから呼ばれます。操作完了時に実行されるブロック内で UI を操作する場合は、メインスレッドで処理が実行されるようにしなければなりません。

SLRequest を使用して投稿する

Social Framework の SLRequest クラスを使用すれば、SNS の API に対する HTTP リクエストを簡単に作成できます。

SLRequest を使用して画像をアップロードする

投稿に画像を添付する場合は、まず「media/upload API」を使用して画像をアップロードします。

  • media/upload API

SLRequestrequestForServiceType:requestMethod:URL:parameters: メソッドを使用して、SLRequest オブジェクトを作成します。

次に、addMultipartData:withName:type:filename: メソッドを使用して画像データを追加し、account プロパティに「ACAccountStore を使用してアカウント情報を取得する」で取得した ACAccount オブジェクトをセットします。

最後に performRequestWithHandler: メソッドを使用してリクエストを実行します。

- (void)uploadImageWithAccount:(ACAccount *)account
                         image:(UIImage *)image
                    completion:(void (^)(NSString *mediaIdString, NSError *error))completion
{
    SLRequest *request =
    [SLRequest requestForServiceType:SLServiceTypeTwitter
                       requestMethod:SLRequestMethodPOST
                                 URL:[NSURL URLWithString:@"https://upload.twitter.com/1.1/media/upload.json"]
                          parameters:nil];
    [request addMultipartData:UIImageJPEGRepresentation(image, 1.0f)
                     withName:@"media"
                         type:@"image/jpeg"
                     filename:@"image.jpg"];
    [request setAccount:account];
    [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                completion(nil, error);
            } else {
                NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData
                                                                     options:NSJSONReadingMutableContainers
                                                                       error:nil];
                NSString *mediaIdString = json[@"media_id_string"];
                completion(mediaIdString, nil);
            }
        });
    }];
}

media/upload API にリクエストを送ると以下のようなレスポンスが返ってきます。media_id_string は本文投稿時に使用します。

{
    "expires_after_secs" = 86400;
    "image": {
        "w": 320,
        "h": 640,
        "image_type": "image/jpeg"
    }
    "media_id": 553639437322563584,
    "media_id_string": "553639437322563584",
    "size": 998865,
}

SLRequest を使用して本文を投稿する

本文を投稿するには「statuses/update API」を使用します。

  • statuses/update API

画像アップロードの場合と同様に SLRequestrequestForServiceType:requestMethod:URL: メソッドを使用して、SLRequest オブジェクトを作成します。requestForServiceType:requestMethod:URL:parameters:parameters 引数にはパラメータが格納された NSDictionary をセットします。

  • パラメータ用の NSDictionary
    • status キーの値
      • 本文
    • media_ids キーの値 (画像を添付する場合のみ)
      • 「SLRequest を使用して画像をアップロードする」で取得したmedia_id_string

次に、account プロパティに ACAccount オブジェクトをセットします。

最後に performRequestWithHandler: メソッドを使用してリクエストを実行します。

- (void)postWithAccount:(ACAccount *)account
                message:(NSString *)message
          mediaIdString:(NSString *)mediaIdString
             completion:(void (^)(NSError *error))completion
{
    NSDictionary *parameters = nil;

    if (mediaIdString) {
        parameters = @{ @"media_ids" : mediaIdString,
                        @"status" : message };
    } else {
        parameters = @{ @"status" : message };
    }

    SLRequest *request =
    [SLRequest requestForServiceType:SLServiceTypeTwitter
                       requestMethod:SLRequestMethodPOST
                                 URL:[NSURL URLWithString:@"https://api.twitter.com/1.1/statuses/update.json"]
                          parameters:parameters];
    [request setAccount:account];
    [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error){
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(error);
        });
    }];
}

動作結果

画像付きで投稿してみました。

twitter-post-with-slrequest-002

今回は、Twitter にアプリを登録せず、Social Framework のクラスを使用して投稿したので、OS から投稿された扱いになります。

twitter-post-with-slrequest-004

まとめ

本記事では Social Framework の「SLRequest」クラスを使って Twitter に投稿する手順を紹介しました。

今回解説したコードは以下のリポジトリで公開してますので参考にしてみてください。

参考記事

Developers.IO 内の関連記事

  • Atwood Wong

    Thanks very much for this useful tutorial
    どうもありがとう