ちょっと未来

ちょっと未来を感じさせるガジェットやアプリ、そして作ったものについて書きます

iPhoneアプリとDropBoxを連携する

最近iOSアプリを作っています。

エディタ系のアプリなのでファイルを、iPhoneiPad間で同期できればいいな、、、 と思ってると

DropboxAPIが結構いい感じ! ファイルをDropbox上に保存しておいて、端末間でファイル同期ができます。

なのでDropbox連携を組み込むことにしてみました。 その記録をメモっておきます。
前にQiitaに書いたもののまとめです...

[1]Dropboxデベロッパ登録

登録

まず作るアプリごとにDropboxに登録する必要があります。

下記 URL の”Create app”よりアプリを登録します。

https://www.dropbox.com/developers/apps

まずは各種設定です。

f:id:wasan:20140320065136p:plain

  • はじめにDrop-insとDropbox API
    今回は一番柔軟に実装ができるCore APIを使いたいので、DropBox APIを選びます。

  • What type of data...
    ファイルを丸ごと保存したいのでFile and DataStoreを選びます。 DataStoreとはDropboxが提供するkey-value型のストレージのようです。

  • Can your app limited ...
    Yesを選びます。 自分のアプリで保存したファイル以外は操作しないのでYesを選びます。 自動的に自分のアプリ用のフォルダが各ユーザのDropbox内に作られて、そこがルートフォルダになります。

  • アプリ名
    適当に入れます。他のアプリと重複しなければOKです。

確認

登録完了です。 APP Key、App Secretは後で使う事になります。

f:id:wasan:20140320070127p:plain

[2]SDK組み込み

XcodeSDKを組み込んで、認証させるところまでやります。

SDKダウンロード

デベロッパサイトからSDKをダウンロードしてきます。

f:id:wasan:20140320072849p:plain

Xcodeに組み込み

解凍したファイルをXcodeのプロジェクトツリーにドラッグ&ドロップします。

f:id:wasan:20140320073028p:plain

Security.framework とQuartzCore.frameworkの設定

Dropbox APIが使用するライブラリ

  • Security.framework
  • QuartzCore.framework

を設定する必要があります。

xcodeのファイルエクスプローラーでターゲットのプロジェクトを選択し、

"Build Phases"タブの"Link Binary with Libraries"の"+"ボタンをクリックします。

表示されたダイアログでSecurity.frameworkを検索して追加します。

QuartzCore.frameworkについても同様に繰り返します。

f:id:wasan:20140320073350p:plain

URL Schemaの設定

xcodeのファイルエクスプローラーでターゲットのプロジェクトを選択し、 "info"→"URL Types"の"URL Schemas"に db-APP_KEY (APP_KEYは先ほど取得したもの)と入れる

f:id:wasan:20140320080150p:plain

[3]認証処理のコーディング

Dropboxアカウントの認証はOAuthで行いますが、ややこしいOAuth認証は全てライブラリがやってくれます。
以下に手順をまとめます。

まずは初期化の処理です。
下記のAPP KEY, APP SECRETには、先ほどデベロッパーサイトで入手したものを指定してください。
* AppDelegate.m

#import <DropboxSDK/DropboxSDK.h>
DBSession *dbSession = [[DBSession alloc]
      initWithAppKey:@"INSERT_APP_KEY" //デベロッパーサイトで入手したもの
      appSecret:@"INSERT_APP_SECRET"    //デベロッパーサイトで入手したもの
      root: kDBRootAppFolder]; // kDBRootAppFolder or kDBRootDropbox ※アプリ毎のフォルダを作ってる場合kDBRootAppFolder
[DBSession setSharedSession:dbSession];

次に
適当なボタンを押した時に、以下メソッドを実行します。
初回はDropboxのパスワード入力用の画面が表示されます。 * ViewController.m

- (IBAction)didPressLink {
    if (![[DBSession sharedSession] isLinked]) {
        [[DBSession sharedSession] linkFromController:self];
    }
}

最後に認証完了の処理を追加します。
* AppDelegate.m

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
    sourceApplication:(NSString *)source annotation:(id)annotation {
    if ([[DBSession sharedSession] handleOpenURL:url]) {
        if ([[DBSession sharedSession] isLinked]) {
            NSLog(@"App linked successfully!");
            // At this point you can start making API calls
        }
        return YES;
    }
    // Add whatever other url handling code your app requires here
    return NO;
}

[4]エミュレータで確認

ビルドして先ほどのボタンを押して認証処理を走らせます。
Dropboxのパスワードを入力する画面がニョキニョキっと出てこればOKです!

f:id:wasan:20140320231901p:plain

[5]ファイルのアップロード,ダウンロードなどなど

DBRestClientを作る

DropBoxと連携するテキスト編集アプリをつくってるとします。まずDBRestClientを準備します。DBRestClientは様々なファイル操作の入口になります。

  • YourViewController.m
#import <DropboxSDK/DropboxSDK.h>

@interface YourViewController () <DBRestClientDelegate>
@property (nonatomic, strong) DBRestClient *restClient;
@end
...

- (void)viewDidLoad {
    [super viewDidLoad];

    self.restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
    self.restClient.delegate = self;
}

ファイルのアップロード

DBRestClientの準備ができたら、まずはファイルのアップロードを試してみます。

[DBRestClient uploadFile:toPath:withParentRev:fromPath:] でアプリ内のローカルファイルをDropBoxにアップロードできます。

以下はworking-draft.txtというファイルを作ってDropBoxにアップロードするサンプルコードです。 新規ファイルの場合は上書き保存を防ぐために、prentRevをnilにしておきます。 既存ファイルを上書きする場合には、parentRevに現在のrevを指定します。これはmetadataのrev属性から取得できます。

  • YourViewController.m
// Write a file to the local documents directory
NSString *text = @"Hello world.";
NSString *filename = @"working-draft.txt";
NSString *localDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *localPath = [localDir stringByAppendingPathComponent:filename];
[text writeToFile:localPath atomically:YES encoding:NSUTF8StringEncoding error:nil];

// Upload file to Dropbox
NSString *destDir = @"/";
[self.restClient uploadFile:filename toPath:destDir withParentRev:nil fromPath:localPath];

全てのDBRestClientのメソッドは即座に戻り値を返さず、代わりに結果を返すDBRestClientDelegateを2つ(成功/失敗)持っています。 uploadFileのcallbackは以下の2つになります。

YourViewController.m

- (void)restClient:(DBRestClient *)client uploadedFile:(NSString *)destPath
    from:(NSString *)srcPath metadata:(DBMetadata *)metadata {
    NSLog(@"File uploaded successfully to path: %@", metadata.path);
}

- (void)restClient:(DBRestClient *)client uploadFileFailedWithError:(NSError *)error {
    NSLog(@"File upload failed with error: %@", error);
}

上記のUploadFileを実行して、成功すればコンソールログに以下のようなログが出るはずです。

File uploaded successfully to path: /working-draft.txt

DropBox上のファイル一覧取得

次にDropBox上のファイル一覧を取得します。 loadMetadtaを実行します。

YourViewController.m

[self.restClient loadMetadata:@"/"];

結果は以下のコールバックで取得できます。

ViewController.m

- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
    if (metadata.isDirectory) {
        NSLog(@"Folder '%@' contains:", metadata.path);
        for (DBMetadata *file in metadata.contents) {
            NSLog(@"    %@", file.filename);
        }
    }
}

- (void)restClient:(DBRestClient *)client loadMetadataFailedWithError:(NSError *)error {
    NSLog(@"Error loading metadata: %@", error);
}

メタデータには現在のrevisionが格納されています。revisionはあらゆるファイル変更がなされたときに更新されます。revisionをローカルファイルにも保存しておけば、revisionを比較して更新すべきかどうか判断できます。

DropBoxからファイルをダウンロード

次にDropBox上のファイルをダウンロードしてみます。 ダウンロードには[DBRestClient loadFile:intoPath:]を使います。

適当なViewControllerで以下を実行します。 dropboxPathはDropBox内のファイルパスです。上記のDBMetadataのpath属性で取得できます。 localPathは端末内の保存したい場所のファイル名フルパスです。

  • YourViewController.m
[self.restClient loadFile:dropboxPath intoPath:localPath];

ダウンロード完了時に呼ばれるコールバックは以下になります。

YourViewController.m

- (void)restClient:(DBRestClient *)client loadedFile:(NSString *)localPath
    contentType:(NSString *)contentType metadata:(DBMetadata *)metadata {
    NSLog(@"File loaded into path: %@", localPath);
}

- (void)restClient:(DBRestClient *)client loadFileFailedWithError:(NSError *)error {
    NSLog(@"There was an error loading the file: %@", error);
}

DropBoxのファイルを削除

ファイル削除には[DBRestClient deletePath:dropboxPath:]を使います。

YourViewController.m

[self.restClient deletePath:dropboxPath];