Google+ もご覧ください
ユーザーアイコン

作って学ぶSDKの使い方

Dropbox Sync API SDKを使ってファイル一覧の取得、追加/削除を行ってみよう

Hirobe

Dropbox Sync API SDKを使ってファイル一覧の取得、追加/削除を行ってみよう

前回まででDropbox Sync APIを扱う準備ができました。今回からいよいよ実際にAPIを使ってみます。今回はまずファイルの一覧を取得したり、追加したり、削除したりする処理を行いましょう。

Dropbox Sync APIについて

ファイルを扱う際には、DropboxのSync APIを呼び出します。

Sync APIのSDKは、大きく分けてアカウント管理のAPIと、ローカルファイルアクセスのAPIで構成されています。APIはここに公開されています。アプリからは、アカウント管理のAPIを使ってDropboxとリンクし、ファイルアクセスのためのAPIを使ってファイルの読み書きをすることになります。

実際の同期は、SDKがDropboxのやり方で自動的に行ってくれますので、アプリから明示的に同期操作を行う必要はありません。一般的にアプリとサーバのデータを同期しようとすると考えるべきことが山ほどあるのですが、ありがたいことにその辺はDropboxのSDKが自動で行ってくれます。

逆に、アプリ側から同期の方法を細かく制御することはできないようです。例えば、同期を一時停止したり、特定のファイルを優先的に送信したりといったAPIはありません。Dropbox Syncを使う場合は、アプリの設計前にDropbox Syncを試しておき、同期の作法について慣れておくのが望ましいでしょう。

ちなみに、フォルダを指定したら後はすべて自動で同期してくれるのを期待した方もいるかもしれません(私がそうでした)。残念ながらそこまで簡単ではありません。サーバと同期する性質上、それぞれのAPIは同期を考慮したものになっており、挙動もNSFileManagerと少し違います。この辺りの動きは以下で説明します。

では、使い方をみてみましょう。

SMFilesViewCotntroller.m

SMFilesViewController.mを以下のように編集します。

冒頭部分を以下のように変更します。

必要なヘッダファイルを読み込み、プロパティとしてファイル名のArrayであるfilesArrayを追加します。initWithStyle:でfilesArrayを空に初期化します。

#import "DSMFilesViewController.h"
#import "DSMEditorViewController.h"
#import <Dropbox/Dropbox.h>
#import "MBProgressHUD.h"

@interface DSMFilesViewController ()
@property (nonatomic) NSArray *filesArray;
@end

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        self.filesArray = @[];
    }
    return self;
}

ファイル一覧の取得と表示

まず、ファイルの一覧を取得するメソッドupdateDataを作成しましょう。updateDataの中では、スレッドを生成してDBFilesystemクラスのlistFolder:メソッドを呼びます。listFolder:はファイル情報(DBFileInfo型)の入ったNSArrayを返してきます。DBFileInfoオブジェクトからファイル名のみを取得して、self.filesArrayに保持します。それが終わるとTableViewを更新します。

listFolder:をスレッドの中で実行していますが、Sync APIにおいて、ファイル情報を取得するAPIは、メインスレッドで実行してはいけません(メインスレッドで実行すると警告がログに出ます)。Sync APIはDropboxサーバから取得したファイル情報をキャッシュしているのですが、まだキャッシュが作られていない場合はサーバへ取得にいき、それが終わるまでメソッドが終了しません。メインスレッドで実行するとアプリがフリーズしたように見えてしまいます。スレッドを生成するのはこれを避けるためです。

listFolder:で扱うようなファイルのディレクトリ情報がキャッシュされたかどうかはDBFilesystemのcompletedFirstSyncプロパティでみることができますので、作成するupdateData:ではこのプロパティがNOならばMBProgressHUDを使用して、待ちインジケータを表示します。

待ちインジケータ
待ちインジケータ

ちなみに、このようにサーバへ取得にいって取得完了を待つ動作をするのは、キャッシュが作成される最初の1回のみです(listFolder:で取得するディレクトリ情報と、各ファイルの中身は別にキャッシュが作成されるようで、listFolder:の後ファイルを個別に開く際にはそれぞれのファイルごとに取得完了待ちが発生します)。

一度ローカルにキャッシュが作られると、それ以降はネットワークがオンラインであってもオフラインであっても、APIはローカルのキャッシュを見に行くようです。いちいち待たずにすぐメソッドが完了します。ローカルのキャッシュはDropboxのサーバに最新のものがあれば、APIが自動で取得にいきます。

なお、このサンプルではエラー時の処理を省略しています。実際にリリースするアプリを作成される場合は、メソッドの戻り値やNSErrorの値を見てエラーの処理を行ってください。

- (void)updateData
{
    // completedFirstSyncがfalseの間はlistFolder:が待ちになる。これはログイン後の1回だけ発生します。
    // listFolder:をメインスレッドで実行すると警告が出ます

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 待ちが発生する場合はprogress dialogを表示(メインスレッド)
        if ([DBFilesystem sharedFilesystem] && ![[DBFilesystem sharedFilesystem] completedFirstSync]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [MBProgressHUD showHUDAddedTo:self.view animated:YES];
            });
        }

        // listを読み込み
        NSError *error;
        DBPath *path = [DBPath root];
        NSArray *dbArray = [[DBFilesystem sharedFilesystem] listFolder:path error:&error];
        NSMutableArray *tmpArray = [[NSMutableArray alloc]initWithCapacity:[dbArray count]];
        for (DBFileInfo *info in dbArray) {
            [tmpArray addObject:info.path.name];
        }
        self.filesArray = [NSArray arrayWithArray:tmpArray];
        NSLog(@"files:%@",[self.filesArray description]);

        // progress dialogを非表示
        dispatch_async(dispatch_get_main_queue(), ^{
            [MBProgressHUD hideHUDForView:self.view animated:YES];
        });

        // tableViewを更新
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
        });
    });
}

viewDidLoadを変更して、viewDidAppear:を追加しました。ファイルを追加するための+ボタンもここで作成しています。そしてviewDidLoadで+ボタンを右上に表示します。Linkボタンは左上に移動しましょう。

それぞれ[self updateData]を呼び、ファイルの一覧を再取得します。viewDidAppear:でupdateDataを呼ぶのは、Link後にテーブルを更新するためです。

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem *linkButton =
        [[UIBarButtonItem alloc] initWithTitle:@"Link"
                                         style:UIBarButtonItemStyleBordered
                                        target:self
                                        action:@selector(linkButtonTapped:)];
    self.navigationItem.leftBarButtonItem = linkButton;

    UIBarButtonItem *addButton =
    [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                                                  target:self
                                                  action:@selector(addButtonTapped:)];
    self.navigationItem.rightBarButtonItem = addButton;

    [self updateData];
}

- (void)viewDidAppear:(BOOL)animated {
    [self updateData];
}

UITableView用のDelegateを実装します。以下のメソッドを書き換えましょう。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.filesArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
    }
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    cell.textLabel.text = [self.filesArray objectAtIndex:indexPath.row];

    return cell;
}

ファイルの追加

さて、ここまででファイルのリストを取得する処理は完了です。続けてファイルを追加する処理を記載します。

ファイルを追加する+ボタンはviewDidLoadで生成していますので、+ボタンを押された際の処理をaddButtonTapped:に記述します。ここではファイルの一覧に存在しないユニークなファイル名newText(番号).txtという形式のファイル名でファイルを生成します。その後、再びファイルの一覧を更新します。

ファイルを生成する際には、DBFilesystemのcreateFile:error:とDBFileのwriteString:error:を使用します。それぞれファイルを生成するメソッドと、ファイルの中身を書き換えるメソッドです。

これらのメソッドは、キャッシュを自前で作るか既にキャッシュが存在するはずなので、メインスレッドで呼んで問題ありません(もちろんスレッドを作っても構いません)。

- (IBAction)addButtonTapped:(id)sender
{
    // create file
    [self createFile:[self uniqueNameStartWith:@"newText" endWith:@".txt" among:self.filesArray]];

    [self updateData];
}

#pragma mark -

- (NSString *)uniqueNameStartWith:(NSString*)prefix endWith:(NSString*)suffix among:(NSArray*)names
{
    for (int i=0; i </pre> &lt <pre> INT_MAX; i++) {
        NSString *uniqueName = (i==0) ? [NSString stringWithFormat:@"%@%@",prefix,suffix] :
        [NSString stringWithFormat:@"%@(%d)%@",prefix,i,suffix];
        if (![names containsObject:uniqueName]) {
            return uniqueName;
        }
    }
    return nil;
}

- (BOOL)createFile:(NSString*)filename
{
    NSError *error;
    DBPath *newPath = [[DBPath root] childPath:filename];
    DBFile *file = [[DBFilesystem sharedFilesystem] createFile:newPath error:nil];
    return [file writeString:@"" error:&error];
}

ファイルの削除

ファイルの削除にはdeletePath:error:を使います。ここでは簡単にするために[self updateData]の中でlistFolder:を呼んで削除後のファイル一覧を取得し、テーブルを再表示しています。もし、tableView:deleteRowsAtIndexPaths:を用いて削除された行を消すアニメーションを行う場合は、少し注意が必要です。ここではlistFolder:で削除後の一覧を取得していますが、ユーザがこのアプリ以外でファイルを既に削除している可能性があります(逆に追加されている可能性もあります)。件数が一致しないとtableView:deleteRowsAtIndexPaths:はエラーになるので、削除されたファイルを正しくdeleteRowsAtIndexPaths:に渡すようにしてください。

以下を追加します。

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [self deleteFile:[self.filesArray objectAtIndex:indexPath.row]];
        [self updateData];
    }
}

- (void)deleteFile:(NSString*)filename {
    //NSFileManager* fileManager = [NSFileManager defaultManager];
    //NSString *path = [self.currentDirectory stringByAppendingPathComponent:filename];
    NSError *error;
    //[fileManager removeItemAtPath:path error:&error];

    DBPath *path = [[DBPath root] childPath:filename];
    [[DBFilesystem sharedFilesystem] deletePath:path error:&error];
}


今回はDropbox Sync APIを使ってファイルの一覧を取得し、ファイルの追加/削除を行いました。次回は編集画面を作成し、ファイルを読み込みや保存を行うメソッドを追加します。

Sdk

記事をリクエストする

関連記事

コメント