Facebook SDK for iOSの導入方法とiOS標準のSocial.frameworkではできない投稿方法

iOSでFacebook投稿を行うにはSocail.frameworkを使う方法が一般的ですが、
Social.frameworkではできない投稿があったので、
Facebook SDK for iOSの導入方法と使い方を紹介します。

#Socail.frameworkではできない投稿

やりたいことはURLシェアで表示される画像や紹介文を変更した投稿です。
（サンプルではFacebookのチュートリアルサイトを六角形の画像表示し説明文を指定しています。）

![フィード投稿_PC.png](https://qiita-image-store.s3.amazonaws.com/0/34488/d292ae4a-7e72-308d-2f40-72c5c61c6023.png)

（上：PCで見たとき　下：スマートフォンのFBアプリで見たとき）

![フィード投稿_スマートフォン.png](https://qiita-image-store.s3.amazonaws.com/0/34488/b1b4fdb5-75da-bcd3-cbd0-060689fb99f7.png)


Facebook SDKを使用することで上のような画像指定でのURLシェア投稿ができます。

<b>
魅力的な画像でユーザーの興味を引きつつ、
URLを開いてもらえるような投稿をすることが本記事の目的です。
</b>

#Social.frameworkでできる投稿

iOS標準のSocial.frameworkを使用してできる投稿をまとめておきます。

###Social.frameworkによる画像指定なしのURLシェア投稿
Social.frameworkを使用して画像指定なしでURLリンクを投稿した場合には以下のように表示されます。

![SocialFrameworkによる投稿で画像指定なし_PC.png](https://qiita-image-store.s3.amazonaws.com/0/34488/5a905d4c-8196-b9e6-dc36-d4a523fb6254.png)

URLリンク先から画像が自動で表示されます。
また、URLの説明文( name, description, caption )の内容が自動で挿入されており、
Social.frameworkでは指定できません。
name        :"Sharing in iOS"
description :"This tutorial walks ..."
caption     :"DEVELOPERS.FACEBOOK.COM"
さらに、投稿したアプリ名の表示も変更できず、"iOS"とのみ表示されます。

この投稿は、投稿機能のクラスSLComposeViewControllerにある、
addURLのメソッドを追加して投稿しました。
addImageのメソッドは使用していません。


###Social.frameworkによる画像指定ありのURLシェア投稿

Social.fragmeworkを使用して画像付きのURLリンクを投稿した場合には以下のような表示になります。

![SocialFrameworkによる投稿_PC.png](https://qiita-image-store.s3.amazonaws.com/0/34488/21586c4a-4d42-f8ac-8ee0-26c270f7d4c5.png)

（上：PCで見たとき　下：スマートフォンのFBアプリで見たとき）

![SocialFrameworkによる投稿_スマートフォン.png](https://qiita-image-store.s3.amazonaws.com/0/34488/61e5d762-0a77-789d-4496-6230c8e52318.png)

上のようにスマートフォンのFBアプリで見たときに、
画像が過剰に大きく表示されてしまいます。
URLの紹介文を表示されていません。
また、画像をタップするとURLが開かれること無く画像が拡大表示されるのみです。

![SocialFrameworkによる投稿で画像タップ時_PC.png](https://qiita-image-store.s3.amazonaws.com/0/34488/868ac4f2-c495-aacc-a5cc-7d4a5398cc2d.png)

（上：PCで見たとき　下：スマートフォンのFBアプリで見たとき）

![SocialFramework投稿で画像タップ時.png](https://qiita-image-store.s3.amazonaws.com/0/34488/006605cf-5e57-d901-9326-497615cef741.png)

この投稿は、投稿機能のクラスSLComposeViewControllerにある以下のメソッドを併用して投稿しました。
- addImage
- addURL


違いをまとめると以下のようになります。

|方法|画像押下時の動作|画像の表示|リンクの説明文の変更|
|:---|:--|:--|:--|
| Social.framework |画像が拡大される| facebookアプリで表示すると巨大に表示されてしまう|x|
| Facebook SDK |URLを開く|◯|◯|





#Facebook SDKの導入方法

2014年12月29日現在、Facebook SDK for iOSのversionはv3.21です。

Facebook Developerのページに沿って導入方法を紹介します。
https://developers.facebook.com/

![アプリの追加.png](https://qiita-image-store.s3.amazonaws.com/0/34488/a7dca1cc-a076-b0cb-19a4-c5d374b1a4d9.png)

上部の青いタブから「Apps」をクリックして「App a New App」を選択します。

![アプリの追加2.png](https://qiita-image-store.s3.amazonaws.com/0/34488/a03d3162-2bcf-f51d-39f0-5fa86fb1c889.png)

プラットフォームの選択画面が表示されるので、「iOS」を選択します。

![アプリ名の指定.png](https://qiita-image-store.s3.amazonaws.com/0/34488/7d6cea76-9781-7078-44c6-bd62b24b23af.png)

Facebook機能を導入しようとしているアプリ名をを入力してください。
※名前に「Facebook」を含むものは指定できません。

![アプリの設定.png](https://qiita-image-store.s3.amazonaws.com/0/34488/94ae6fbc-cabd-b5eb-585b-60fb95c1bcdf.png)

名前を入力するとカテゴリの設定を求められます。
カテゴリの選択は必須です。
※「いいえ」のスイッチは既にFacebookに登録済みのアプリがあり、そのアプリのテストアプリとして登録する場合に使います。

![インストールガイド.png](https://qiita-image-store.s3.amazonaws.com/0/34488/0e189132-c732-f7f9-2de2-8999dcb80639.png)

登録した結果、画像内の青字で表示されている値が与えられます。
- FacebookAppID
- FacebookDisplayName
- URL Schemes
※画像内の値はサンプルですので、ご自身のアプリに与えられた値に置き換えてください。

画像の右端が見きれてしまっているので、Xcodeの画像を用いて詳細に説明します。

![BundleIdentifier.png](https://qiita-image-store.s3.amazonaws.com/0/34488/5407fff2-ee14-ce85-f66c-4f0581c3373b.png)

Facebook機能を導入するアプリのBundle Identifierを確認します。
※サンプルでは"jp.qiita.FacebookPostSample"とします。

![BundleIdentifierの登録.png](https://qiita-image-store.s3.amazonaws.com/0/34488/0d99949e-5259-4c48-79ed-24715ce20f9c.png)

Bundle Identifierの値を先ほどのWebページに入力します。

次にXcode側の登録をします。

SDKをダウンロードします。
https://developers.facebook.com/docs/ios?locale=ja_JP

![FacebookSDKフレームワークの追加.png](https://qiita-image-store.s3.amazonaws.com/0/34488/f6605d1a-7d62-a6f5-72d1-6b30f11fa815.png)

展開されたフォルダ内にあるFacebookSDK.frameworkをprojectに追加します。

![FacebookAppIDの登録.png](https://qiita-image-store.s3.amazonaws.com/0/34488/b9f8eb64-8ea3-2c3c-542d-0ecabc3c2075.png)

アプリ登録時に与えられた青字の値をinfo.plistに設定していきます。
アプリの「Info」のKeyに新規に「FacebbookAppID」を追加します。
Typeは「String」のまま、Valueは与えられた値を入れてください。

![FacebookDisplayNameの登録.png](https://qiita-image-store.s3.amazonaws.com/0/34488/e9ec3469-1081-05a6-eaaa-e3d9a43a1274.png)

同じように「FacebookDisplayName」を追加します。

![URLスキームの指定.png](https://qiita-image-store.s3.amazonaws.com/0/34488/98eda02a-b348-e7db-c51b-4a126baaef68.png)

URLスキームの指定も行います。

以上で設定作業は完了です。


#Facebook SDKによる投稿

Facebook SDKによる投稿方法は以下の３つがあります。

|手法|投稿方法|OpenGraphによる投稿|メリット|デメリット|
|:---|:--|:--|:--|:--|
| FBDialogs |facebookアプリにURLスキームして投稿画面を表示|◯|実装が容易|アプリから離れてしまう|
| FBWebDialogs |WebViewで投稿画面を開く|✕|アプリから離れない。投稿画面が容易されている。|OpenGraphの投稿はできない|
| FBRequestConnection |投稿画面を自作して投稿用APIを叩く|◯|アプリから離れない。投稿画面のUIを自作できる。|許可申請や投稿画面を用意する必要がある|

冒頭の画像付きURLシェア投稿を実現するためには、どの方法でも可能です。
FBWebDialogsが実装としては一番シンプルで簡単です。
実装は困難であるが自作できるという点でFBRequestConnectionも便利です。
AndroidのようにiOSはURLスキームがシームレスでないため、FBDialogsは好ましくないかと考えます。


##FBDialogsによる投稿
Facebook が推奨するFacebookアプリにURLスキームさせてFBアプリで投稿する方法です。
自身のアプリから離れてしまうのがUX的に好ましくありませんので筆者は非推奨です。

```objc:Objective-Cのサンプルコード
/**
 * FBDialogsによりFacebookアプリへURLスキームして投稿
 */
- (void)postByFBDialogs {
    // Facebook アプリがインストールされているかの確認
    if (![FBDialogs canPresentShareDialog]) {
        return;
    }
    
    // FacebookアプリへURLスキームして投稿画面を表示する
    [FBDialogs presentMessageDialogWithLink:[NSURL URLWithString:@"https://developers.facebook.com/docs/ios/share/"]
                                       name:@"なまえ"
                                    caption:@"キャプション"
                                description:@"説明文"
                                    picture:[NSURL URLWithString:@"http://i.imgur.com/g3Qc1HN.png"]
                                clientState:nil
                                    handler:^(FBAppCall *call, NSDictionary *results, NSError *error) {
                                        if(error) {
                                            NSLog(@"エラー: %@", error.description);
                                        } else {
                                            NSLog(@"投稿に成功しました %@", results);
                                        }
                                    }];
    
}

```

##FBWebDialogsによる投稿
アプリ上でWebViewの投稿画面を表示する方法です。

投稿画面を表示させたときにログイン情報が無ければログイン画面が表示されます。

![FBWebDialogsのログイン画面.png](https://qiita-image-store.s3.amazonaws.com/0/34488/ca8c81b6-862b-7fba-f6cc-10fc0e94f817.png)

ログイン後、投稿画面が表示されます。

![FBWebDialogsの投稿画面.png](https://qiita-image-store.s3.amazonaws.com/0/34488/6c31e991-2fcf-bf9a-311c-006ecea0fde3.png)




```objc:Objective-Cのサンプルコード


/**
 * FBWebDialogsによるFacebook投稿画面の表示
 */
- (void)postByWebDialogs {
    NSDictionary *params = @{@"name"       : @"なまえ",
                             @"caption"    : @"キャプション",
                             @"description": @"説明文",
                             @"link"       : @"https://developers.facebook.com/docs/ios/share/",
                             @"picture"    : @"http://i.imgur.com/g3Qc1HN.png"};
    
    // 投稿画面をWebViewで表示
    [FBWebDialogs presentFeedDialogModallyWithSession:nil
                                           parameters:params
                                              handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
                                                  if (error) {
                                                      NSLog(@"エラー: %@", error.description);
                                                  }
                                              }];
}

```

##FBRequestConnectionによる投稿
アプリ内で投稿用APIを叩いて投稿する方法です。
投稿の前に投稿のためのpermissionを取得する必要があります。
方法はいくつかありますが、アプリから離脱させずにSocial.frameworkと同様の許可の取り方を紹介します。

![FacebookPermission.png](https://qiita-image-store.s3.amazonaws.com/0/34488/a5c4c171-c28f-80d1-081f-371266a2ac38.png)

許可確認画面は投稿のために２回表示する必要があります。
最初に"basic_info"の許可確認画面を表示し、その後に"publish_actions"の許可確認画面を表示します。

ちなみに、許可確認画面を二度出したくない場合は、permissionsのパラメータに@[@"basic_info", @"publish_actions"]を指定すれば、Warningは吐きますが、投稿することはできます。



```objc:Objective-Cのサンプルコード

/**
 * Facebookの基本情報への使用許可("basic_info")を取得する
 */
- (void)getFacebookPermissionBasicInfo {
    FBSession *fbSession = [[FBSession alloc] initWithAppID:nil
                                                permissions:@[@"basic_info"]
                                            defaultAudience:FBSessionDefaultAudienceEveryone // 公開範囲は友人を指定
                                            urlSchemeSuffix:nil
                                         tokenCacheStrategy:nil];
    
    [FBSession setActiveSession:fbSession];
    [fbSession openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent
              completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
                  if (error) {
                      NSLog(@"エラー：%@", error.description);
                      return;
                  }
                  
                  [FBSession setActiveSession:session];
              }];

}

/** 
 * Facebookの投稿機能の許可("publish_actions")を取得する
 * "basic_info"のpermissionを取得した後に実行してください。
 */
- (void)getFacebookPermissionPublishAction {
    [[FBSession activeSession] requestNewPublishPermissions:@[@"publish_actions"]
                                            defaultAudience:FBSessionDefaultAudienceFriends // 公開範囲は友人を指定
                                          completionHandler:^(FBSession *session, NSError *error) {
                                              if (error) {
                                                  NSLog(@"エラー：%@", error.description);
                                                  return;
                                              }
                                              
                                              [FBSession setActiveSession:session];
                                          }];

}

/**
 * Facebookの投稿用APIを叩いて投稿する
 * （投稿用のUIは自作してください。）
 * messageはユーザーが入力した文章を想定しています。
 */
- (void)postFacebookFeed:(NSString *)message {
    NSDictionary *params = @{@"message"    : message,
                             @"name"       : @"なまえ",
                             @"caption"    : @"キャプション",
                             @"description": @"説明文",
                             @"link"       : @"https://developers.facebook.com/docs/ios/share/",
                             @"picture"    : @"http://i.imgur.com/g3Qc1HN.png"};
    
    // Facebookの投稿用APIを叩いて投稿する
    [FBRequestConnection startWithGraphPath:@"/me/feed"
                                 parameters:params
                                 HTTPMethod:@"POST"
                          completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
                              if (error) {
                                  NSLog(@"エラー：%@", result);
                                  return;
                              }
                          }];
}


```

#トラブルシューティング

実装中に出会ったエラーと対策を紹介します。

エラーメッセージ：
"The Facebook server could not fulfill this access request: no stored remote_app_id for app"
↓
対策：
WebのFacebook Developerのアプリページに投稿するアプリののBundler Identifierを登録する


エラーメッセージ：
"The Facebook server could not fulfill this access request: The app must ask for a basic read permission like email at install time."
↓
対策：
permissionの取得は"basic_info"からする。


エラーメッセージ：
'FBSession: Publish permissions were requested without specifying an audience; use FBSessionDefaultAudienceJustMe, FBSessionDefaultAudienceFriends, or FBSessionDefaultAudienceEveryone'
↓
対策：
publicsh_actionsのFBSesstionを作るときは、defaultAudienceを一緒に設定する。


#まとめ
Facebook SDKを使用することで可能になるURL投稿時の画像指定とURL紹介文の変更方法を紹介しました。
Social.frameworkによる実装は容易ですが、Webページのシェア投稿ではFacebook SDKに及ばないところがありますし、
Social.frameworkがiOS7、iOS8で更新されませんでしたので機能追加される望みは薄いです。
よりユーザーにURLをアピールできるFacebook投稿のために、Facebook SDKの導入を検討してみてはいかがでしょうか。


#参考
・Facebookの投稿方法(iOS)
https://developers.facebook.com/docs/ios/share

・iOSのシステム標準なpermissionの取得方法
http://stackoverflow.com/questions/23476499/native-facebook-login-stopped-working-after-sdk-update-to-3-14