iOS6のSocial.frameworkを試してみた

iOS6からFacebook、微博(Weibo)、Twitterを統合したSocial.frameworkが追加されました。例によってAOuth認証などの面倒な手続きは全てiOS側で行なってくれるので投稿だけであれば、数行のコードで実装が可能です。
iOS5から追加されたTwitter.frameworkは廃止の方向になるので、新しく実装する場合はSocial.frameworkで実装した方がよさそうです。実装方法はTwitter.frameworkと同じような作りとなっているので簡単に移行できると思います。

SLComposeViewControllerでの投稿方法

プロジェクトのBuild PhasesのLink Binary with Librariesで「Social.framework」を追加します。

ヘッダにSocial.hをインポートします。

#import <Social/Social.h>

投稿するアクションにてSLComposeViewControllerを作成し、presentViewController で呼び出します。

    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) { //利用可能チェック
        NSString *serviceType = SLServiceTypeTwitter;
        SLComposeViewController *composeCtl = [SLComposeViewController composeViewControllerForServiceType:serviceType];
        [composeCtl setCompletionHandler:^(SLComposeViewControllerResult result) {
            if (result == SLComposeViewControllerResultDone) {
                //投稿成功時の処理
            }
        }];
        [self presentViewController:composeCtl animated:YES completion:nil];
    }

serviceTypeはSLServiceTypeTwitter、SLServiceTypeFacebook、SLServiceTypeSinaWeiboが用意されており、それぞれに投稿画面が用意されています。





標準の投稿画面を使用せずにカスタムビュー等で直接投稿する方法

SLRequestを使用することでSLComposeViewControllerを使わずに直接それぞれのWebAPIを叩くことができます。ACAccountStoreでiOSのアカウント認証情報がラップされておりOAuth認証周りをSLRequestが吸収してくれるのでWebAPIのみに専念できます。

プロジェクトのBuild PhasesのLink Binary with Librariesで「Accounts.framework」を追加します。

ヘッダにAccounts.hをインポートします。

#import <Accounts/Accounts.h>
Twitterの場合

投稿だけならTwitterの開発者登録(appkey等)は不要です。iOSのアカウント認証ACAccountが全て上手くやってくれます。

    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) {
        ACAccountStore *accountStore = [[ACAccountStore alloc] init];
        ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
        [accountStore
         requestAccessToAccountsWithType:accountType
         options:nil
         completion:^(BOOL granted, NSError *error) {
             if (granted) {
                 NSArray *accountArray = [accountStore accountsWithAccountType:accountType];
                 if (accountArray.count > 0) {
                     NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/update.json"];
                     NSDictionary *params = [NSDictionary dictionaryWithObject:@"SLRequest post test." forKey:@"status"];
                     
                     SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                             requestMethod:SLRequestMethodPOST
                                                                       URL:url
                                                                parameters:params];
                     [request setAccount:[accountArray objectAtIndex:0]];
                     [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                         NSLog(@"responseData=%@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
                     }];
                 }
             }
         }];
    }

この例では、iOS内で認証した複数アカウントが存在した場合は1件目で投稿していますが、ACAccount#identifierを保持しておいて、 ACAccountStore#accountWithIdentifier: でACAccountを取得するようにします。
また、iOS5から追加されたACAccountStore#requestAccessToAccountsWithType:withCompletionHandler:handler:はDEPRECATEDとなっているので、ACAccountStore#requestAccessToAccountsWithType:options:completion:を使うようにします。Twitterの場合はoptionsはnilで構いません。
TwitterREST APIドキュメントはこちら ⇨ https://dev.twitter.com/docs/api/1.1/post/statuses/update

Weiboの場合

WeiboもほとんどTwitterと同様で、WeiboAPIのversion2であればappkeyは不要のようです。尚、Weiboのアカウント認証設定を出すためには中国語用のキーボードを追加すればiOSの設定画面でWeiboが出現します。

    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeSinaWeibo]) {
        ACAccountStore *accountStore = [[ACAccountStore alloc] init];
        ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierSinaWeibo];
        [accountStore
         requestAccessToAccountsWithType:accountType
         options:nil
         completion:^(BOOL granted, NSError *error) {
             if (granted) {
                 NSArray *accountArray = [accountStore accountsWithAccountType:accountType];
                 if (accountArray.count > 0) {
                     NSURL *url = [NSURL URLWithString:@"https://api.weibo.com/2/statuses/update.json"];
                     NSDictionary *params = [NSDictionary dictionaryWithObject:@"SLRequest post test." forKey:@"status"];
                     SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeSinaWeibo
                                                             requestMethod:SLRequestMethodPOST
                                                                       URL:url
                                                                parameters:params];
                     [request setAccount:[accountArray objectAtIndex:0]];
                     [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                         NSLog(@"responseData=%@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
                     }];
                 }
             }
         }];
    }

WeiboのWebAPIドキュメントはこちら ⇨ http://open.t.sina.com.cn/wiki/2/statuses/update

Facebookの場合

ACAccountStoreでアカウントにアクセスする際に、Facebookアプリ登録といくつかの設定が必要となります。
FacebookAPI (iOS SDK)を拡張するで書いたようにFacebookアプリを登録します。ソースに埋め込むApp IDは基本設定のところに表示されます。

基本設定タブの「アプリをFacebookに結合する方法を選択」でネイティブiOSアプリを選択し、開発しているiOSアプリBundle identifierを入力します。

詳細設定タブの認証でAppTypeをNative/Desktopに、App Secret in ClientをNoに設定します。

    if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
        ACAccountStore *accountStore = [[ACAccountStore alloc] init];
        ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 @"2768xxxxxxxxxxx", ACFacebookAppIdKey,
                                 [NSArray arrayWithObjects:@"public_actions", @"publish_stream", @"offline_access", nil], ACFacebookPermissionsKey,
                                 ACFacebookAudienceOnlyMe, ACFacebookAudienceKey,
                                 nil];
        [accountStore
         requestAccessToAccountsWithType:accountType
         options:options
         completion:^(BOOL granted, NSError *error) {
             NSArray *accountArray = [accountStore accountsWithAccountType:accountType];
             for (ACAccount *account in accountArray) {
                 
                 NSString *urlString = [NSString stringWithFormat:@"https://graph.facebook.com/%@/feed", [[account valueForKey:@"properties"] valueForKey:@"uid"]] ;
                 NSURL *url = [NSURL URLWithString:urlString];
                 NSDictionary *params = [NSDictionary dictionaryWithObject:@"SLRequest post test." forKey:@"message"];
                 SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook
                                                         requestMethod:SLRequestMethodPOST
                                                                   URL:url
                                                            parameters:params];
                 [request setAccount:account];
                 [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                     NSLog(@"responseData=%@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
                 }];
             }
         }];
    }

optionsで、ACFacebookAppIdKey(Facebookアプリで登録した時のApp ID)、ACFacebookPermissionsKey(アプリのアクセス権限)、ACFacebookAudienceKey(投稿の公開範囲)でACFacebookAudienceEveryone/ACFacebookAudienceFriends/ACFacebookAudienceOnlyMeのいづれかを設定します。

FacebookのPermissionsの詳細についてはこちら ⇨ http://developers.facebook.com/docs/authentication/permissions/
投稿だけであれば、「public_actions」「publish_stream」「offline_access」を設定しておけば十分かと思います。

サンプルコード

サンプルをgithubにアップしました。
https://github.com/hmori/SocialPostSample

Show accountはiOSで認証したACAccountのオブジェクトを表示します。SLComposeViewControllerの各ボタンはiOS標準の投稿画面がポップアップします。SLRequestの各ボタンは固定メッセージを直接投稿しますのでご注意ください。


また、実装してみて1つ疑問が湧いたのですが、ACAccountStoreとSLRequestを利用すればTwitter、Weiboについてはアプリ登録なし(appkey不要)で任意のメッセージを投稿できてしまいます。おそらくどのソーシャルメディアもスパム対策としてアプリから投稿する場合にユーザーが意図しない内容を勝手に投稿してはいけない規約が存在します。スパムアプリと認定された場合はそのappkeyでのAPIに制限が掛けることが可能だと思いますが、SLRequestとACAccountでの実装はappkeyなしでも投稿が可能な仕組みです。AppStoreのアプリ配布制限もあくまでApple側の管理下なのでTwitter、Weibo側からの要求がなければスパムアプリを止めることができないように思います。いづれTwitter、Weiboのカスタム実装でFacebookのようにappkeyやpermissionが必要となる日がくるかもしれません。