2011年3月31日木曜日

Facebook iOS SDKを使ってiOSアプリからFacebookに画像をアップロードする方法

------------------------------------------------------
※ 注意
現在配布されているFacebook iOS SDKは認証に原則としてSingle Sign-On方式を採用しているため、この記事で記述されている手順とは必要な手順が異なります。

くわしくはFacebookのiOS Tutorialをご覧下さい。
------------------------------------------------------


FacebookのiOS SDKを使うとFacebookのGraph APIを簡単に使うことができます。
ただ、認証やデリゲートまわりでややこしいところがあるので簡単にまとめておきます。

個人的な理由で画像のアップロードの流れを書いてみます。
画像のアップロード以外でも
認証 & パーミッション付与 > Graph API呼び出し
という流れは基本的に一緒です。


GitHub - facebook/facebook-ios-sdk
https://github.com/facebook/facebook-ios-sdk


iOS SDKのセットアップなどは上のGitHubのページに詳しく説明されているのでそちらをどうぞ。


FacebookのアプリページAPI KeySecret Keyを確認しておきます。
static NSString *kFacebookAPIKey = @"API KEY";
static NSString *kFacebookSecretKey = @"SECRETI KEY";


とりあえずヘッダーファイルはこんな感じです。
@interface FacebookViewController : UIViewController
<FBRequestDelegate,  FBSessionDelegate,  FBDialogDelegate>
{
    FBSession *facebookSession;
    FBRequest *facebookUploadRequest;
}

@property (nonatomic, retain) FBSession *facebookSession;
@property (nonatomic, retain) FBRequest *facebookUploadRequest;

- (void)upload:(NSData *)uploadImageData;

@end


まずはFBSession を初期化して認証をします。FBSessionを認証やAPIの呼び出しで使い回します。
@implementation FacebookViewController

- (id)init {
    self = [super init];
    if (self != nil)
    {
        self.facebookSession = [FBSession sessionForApplication:kFacebookAPIKey
                                                                                secret:kFacebookSecretKey 
                                                                             delegate:self];
        
        // 認証済みかどうか確認
        // 認証済の場合 FBSessionDelegatesession:didLogin: が呼ばれる。
        [facebookSession resume];
    }
    return self;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    if (![facebookSession isConnected])
    { 
        // 未ログインの場合はログインダイアログを表示する
        // ダイアログ内のアクションは FBSessionDelegate でハンドリングする。
        FBLoginDialog *dialog = [[FBLoginDialog alloc] initWithSession:facebookSession];
        [dialog show];
        [dialog release];  
    } 
}

@end

ログイン/ログアウト時の処理はFBSessionDelegateでハンドリングします。
#pragma mark -
#pragma mark FBSessionDelegate

- (void)session:(FBSession*)session didLogin:(FBUID)uid {
    // ログイン成功時に呼ばれる。

    if (![[NSUserDefaults standardUserDefaults] boolForKey:@"has_facebook_photo_upload_perm"]) 
    {
        // 画像アップロードには「写真」のパーミッションが必要。
        // もしパーミッションが与えられていない場合はパーミッションを確認するダイアログを開く。
        FBPermissionDialog * dialog;
        dialog = [[[FBPermissionDialog alloc] initWithSession:facebookSession] autorelease];
        dialog.delegate = self;
        dialog.permission = @"photo_upload,user_photos"; // 「写真」関連のパーミッションを指定
        [dialog show];
    }
}

- (void)sessionDidNotLogin:(FBSession*)session {
    // ログインせずにダイアログを閉じた場合に呼ばれる。
}
- (void)session:(FBSession*)session willLogout:(FBUID)uid {
    // ログアウトする前に呼ばれる
}

- (void)sessionDidLogout:(FBSession*)session {
   // ログアウト後に呼ばれる
}


パーミッションの処理はFBDialogDelegateでハンドリングします。
#pragma mark -
#pragma mark FBDialogDelegate

- (void)dialogDidSucceed:(FBDialog*)dialog
{
    // パーミッション与えられた場合
   [[NSUserDefaults standardUserDefaults] setBool:YES
                                                                     forKey:@"has_facebook_photo_upload_perm"];
}



認証が通ってパーミッション(写真)があれば画像のアップロードができます。
- (void)upload:(NSData *)uploadImageData
{
    self.facebookUploadRequest = [FBRequest requestWithDelegate:self];
    [facebookUploadRequest call:@"facebook.photos.upload"
                                      params:params
                                dataParam:uploadImageData];
}


アップロード処理のハンドリングはFBRequestDelegateでおこないます。
#pragma mark -
#pragma mark FBRequestDelegate

- (void)requestLoading:(FBRequest*)request {
    // リクエスト送信直前に呼ばれる
}

- (void)request:(FBRequest*)request didReceiveResponse:(NSURLResponse*)response {
    // サーバーがレスポンスデータを返し始める時に呼ばれる
}

- (void)request:(FBRequest*)request didLoad:(id)result {
    // サーバーがレスポンスを返し、レスポンスデータがオブジェクトにパースされた時に呼ばれる
}

- (void)request:(FBRequest*)request didFailWithError:(NSError*)error {   
    // エラー時に呼ばれる
}

- (void)requestWasCancelled:(FBRequest*)request {
    // キャンセル時に呼ばれる
}

これで画像がFacebookに送信されます。


ただ、FBRequestDelegateにはアップロードの進捗度を通知してくれるものがないので、このままではプログレスバーを表示することができません。


ちょっと小細工してアップロードの進捗度をチェックできるようにしてみます。

FBRequestは内部的にNSURLConnectionを利用してサーバーと通信しています。
NSURLConnectionには connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite: というデリゲートメソッドがあって、こいつを利用できればアップロードの進捗度が確認できます。

FBRequestのサブクラスを作ったり、ライブラリのコードを直接改変したり(!!)、方法はいくつかあると思うのですが
ここではObjective-Cのカテゴリを使って FBRequest に connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite: メソッドを追加してみます。
@interface FBRequest (Upload)
@end

@implementation FBRequest (Upload)

- (void)connection:(NSURLConnection *)connection
  didSendBodyData:(NSInteger)bytesWritten 
 totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
    FacebookViewController *vc = (FacebookViewController *)_delegate;
    [vc didSendBodyData:bytesWritten
         totalBytesWritten:totalBytesWritten 
  totalBytesExpectedToWrite:totalBytesExpectedToWrite];
}

@end

@implementation FacebookViewController

- (void)didSendBodyData:(NSInteger)bytesWritten
           totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
    NSLog(@"bytesWritten : %d, totalBytesWritten : %d, totalBytesExpectedToWrite: %d",
                bytesWritten , totalBytesWritten, totalBytesExpectedToWrite);
}

@end

0 件のコメント: