assaulter's diary

主にバイクについて

Facebook iOS SDK3.5で投稿処理を行う際の流れ(その2)

Facebook iOS SDKはコロコロ仕様が変わる(メソッドがいきなり非推奨になったり)ので、お使いのSDKのバージョンには気をつけましょう。

フローチャート

f:id:assaulter:20130516091915p:plain

この図の各部分の処理を書いていきます

セッションが開いているか

FBSession.activeSession.isOpen

コレがBOOL返すので、それで判断

投稿する権限があるか

if([FBSession.activeSession.permissions indexOfObject:@"publish_actions"] == NSNotFound)

セッションが持ってる権限は、FBSession.activeSession.permissionsにコレクションとして格納されているので、それを見て判断する。

iOS Versionを判断する

NSArray *aOsVersions = [[[UIDevice currentDevice]systemVersion] componentsSeparatedByString:@"."];
iOsVersion = [aOsVersions[0] intValue];

これで取得できる(らしい)

iOS6以上で、端末にFacebookアカウントを設定しているかどうか

#import <Accounts/Accounts.h>

    ACAccountStore* accountStore = [ACAccountStore new];
    ACAccountType* accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
    
    NSDictionary *options = @{@"ACFacebookAppIdKey": APP_ID_KEY,
                              @"ACFacebookPermissionsKey" : @[@"email"],
                              @"ACFacebookAudienceKey" : ACFACEBOOK_AUDIENCE_KEY};
    
    [accountStore requestAccessToAccountsWithType:accountType options:options completion:^(BOOL granted, NSError *error) {
        if (granted) {
            NSArray* accountArray = [accountStore accountsWithAccountType:accountType];
            if (accountArray > 0) {
                /// iOS6かつ、Facebookアカウントが設定されている場合の処理を開始する
            } else {
                /// iOS5と同じ処理に移動する
            }
        } else {
            DLog(@"error");
            /// iOS5と同じ処理に移動する
        }
    }];

わざわざAccountStore使って判断するのはめんどくさいのですが、とりあえずはコレで判断できるみたい・・・ここ自信ないです。

activeなセッションに対して、投稿権限を要求する

[FBSession.activeSession
     requestNewPublishPermissions:@[@"publish_actions"]
     defaultAudience:PUBLISH_AUDIENCE_KEY
     completionHandler:^(FBSession *session, NSError *error) {
         if (error) {
             /// Handle new permissions request errors
         } else {
             /// 権限が取得できたので、投稿する処理に以降する
         }
     }];

publishPermissionと一緒に、セッションをオープンする

    [FBSession openActiveSessionWithPublishPermissions:[NSArray arrayWithObject:@"publish_actions"] defaultAudience:PUBLISH_AUDIENCE_KEY allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
        if (!error && status == FBSessionStateOpen) {
            [self performPublishAction];
        } else {
            [self performSelectorOnMainThread:@selector(setFBError:messages:) withObject:error withObject:[self createAuthErrorMessage:error] waitUntilDone:YES];
        }
    }];

このメソッドが、Facebookアカウントを設定済みのiOS6と、Facebookアカウントを設定していないiOS6,iOS5で挙動が変わります。
OSにアカウントを設定している場合には、そちらから取得するので、アプリ内で解決します。 そうでない場合には、safariが起動して、権限を要求するページが開きます。その後、アプリに戻ってきます。

セッションをオープンする

[FBSession openActiveSessionWithAllowLoginUI:YES];

Facebookはちょっと前の仕様変更で、publish権限を取得剃る前にまずread権限を取得しないといけないという制限があります。iOS5とIDを設定してないiOS6は上記のrequestNewPublishPermissionsを呼べば、一度safariがオープンして、read権限とpublish権限の承認を両方得ることができます。
逆にIDを設定したiOS6では正式にreadを要求してから、publishを要求するというプロセスを踏む必要があるので、上記のメソッドをコールしたあとにpublish権限の取得にうつります。

Facebookに投稿する

  FBRequest* request = [FBRequest requestForUploadPhoto:self.photo];
    [request.parameters addEntriesFromDictionary:@{@"message": self.message}];

    FBRequestConnection* connection = [[FBRequestConnection alloc] initWithTimeout:TIMEOUT_INTERVAL];
    [connection addRequest:request completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
        if (error) {
            /// 投稿に失敗した
            [self performSelectorOnMainThread:@selector(setFBError:messages:) withObject:error withObject:[self createAPICallErrorMessage:error] waitUntilDone:YES];
        } else {
            /// 投稿に成功した
            [self performSelectorOnMainThread:@selector(setFBError:messages:) withObject:error withObject:[self createSuccessMessages] waitUntilDone:YES];
        }
    }];
    [connection start];

単純に写真を上げるだけなら、uploadPhoto的なメソッドをコールするだけですが、写真にコメントも付けたかったので、requestにメッセージも付加しています。

まとめ

コールバック地獄ですね。エラー処理は各Handlerに帰ってくるのでそれをインスタンス変数で持っておいて使う側で判断する形が一般的でしょうか?おそらくもっとスマートなやりかたがあると思うので、詳しい方コメントお願いします・・・