Hatena::ブログ(Diary)

中継地点 RSSフィード Twitter

2012-10-11

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」を追加します。

f:id:h_mori:20121009121822p:image:w360

ヘッダに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が用意されており、それぞれに投稿画面が用意されています。

f:id:h_mori:20121009121823p:image:w240

f:id:h_mori:20121009121824p:image:w240

f:id:h_mori:20121009121825p:image:w240




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

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

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

f:id:h_mori:20121010230643p:image:w360

ヘッダに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は基本設定のところに表示されます。

f:id:h_mori:20121010223008p:image:w360

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

f:id:h_mori:20121010223009p:image:w360

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

f:id:h_mori:20121010223010p:image:w360


    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

f:id:h_mori:20121011000258p:image:w360

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が必要となる日がくるかもしれません。

Takahiro ItoTakahiro Ito 2012/10/12 16:52 参考にさせていただきました。ありがとうございます。

slasher04slasher04 2012/10/31 13:21 ありがとう。
参考にさせて頂きました。
しかし、実機ではtwitterしか動きませんね。
Facebook
weiboはスルーです.
?

nakanominakanomi 2013/01/31 18:40 参考にさせていただきました。ありがとうございます。

>slasher04
デバイスの設定はされましたか?もしまだでしたら記事を書いたので、読んでみてください(内容的にはこちらの記事にも書かれていますが、スクリーンショットを追加してみました)。
http://nakanomicblog.blog.fc2.com/blog-entry-5.html

ozawaozawa 2013/03/15 16:44 参考にさせていただきました。
一点確認させてください。
permission設定のところは"publish_actions"ではありませんでしょうか?
参考URL http://developers.facebook.com/docs/howtos/ios-6/

hiei0222hiei0222 2013/04/25 11:45 参考にさせていただきました。
Twitter API 1.1にも早速実装していただきたいと思います。
ありがとうございました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元
Connection: close