iOS で Evernote API を使う(前編)

iPad から Evernote にアクセスするアプリを作ろうと思い、試してみました。


http://www.evernote.com/about/media/img/logos/evernote_logo_4c-lrg.gif

1. Evernote APIを理解する

a. ドキュメントに目を通す

Evernote API Overview | Evernote Corporation ページにある API Overview (PDF)を眺めると、 Evernoteのデータ構造、 APIの概略が解ります。

b. APIとサンプルコードをダウンロードして眺める

Evernote API Overview | Evernote Corporation ページある Download the API SDK から API, サンプルコードの入った zipファイルがダウンロードできます、現在のバージョンは1.17です。中にはいろいろな言語で書かれたサンプルがありませので、自分の好きな言語のサンプルを眺めてみます。私は当然Rubyですね。

c. サンプルを動かす為のAPI Keyを取得する

Evernote APIは OAuthを使ってクライアントソフトを認識するので、OAuth用のconsumer key, consumer secretを取得する必要があります。
Keyの取得は Evernote API Overview | Evernote Corporation ページのRequest an API Keyフォームを記述しSubmitボタンを押すと、consumer key , consumer secret が画面に表示されますのでメモしておきましょう。

d. Sandboxにテストデータを準備する

開発用にアクセスできるのは通常のEvernoteではなく、Sandbox です。 ここ でテスト用のアカウントを作成し、テスト用のコンテンツを格納して下さい。

e. サンプルコードを動かしてみる

b. で展開したサンプルコードに c. で取得した consumer key , consumer secret を書き込むと Sandbox の Evernoteがアクセスできます。

実行例:

% cd evernote-api-1.17/sample/ruby/client
% vi  EDAMTest.rb    ← consumer key , consumer secret を書き込む
% ruby EDAMTest.rb Sandboxのアカウント  Sandboxのパスワード
Unable to load thrift_native extension. Defaulting to pure Ruby libraries.
warning: peer certificate won't be verified in this SSL session
Is my EDAM protocol version up to date?  true
warning: peer certificate won't be verified in this SSL session
Authentication was successful for yuumi3
Authentication token = S=s1:U=5381:E=130f97c8d8a:C=130f9459f0e:P=37:A=yuumi3:H=0bdbe82e2e87bf54ac6121ff0e3246e1
warning: peer certificate won't be verified in this SSL session
Found 1 notebooks:
  * yuumi3 のノートブック

Creating a new note in the default notebook: yuumi3 のノートブック

warning: peer certificate won't be verified in this SSL session
Successfully created a new note with GUID: 64983248-7d3f-438e-bbb0-7eeb0409c4f4

2. iOS用ライブラリーの作成

いよいよ、iOS用ライブラリーを作りましょう。手順は Evernote APIを使うiPhoneアプリの作りかた - the time ship を参考にしました。ありがとございます!

  • Xcode4 で新規プロジェクト(Navigation-based Application)を作成します(Xcode3とはプロジェクトのディレクトリー構造が違いますね)。
  • Evernote APIを入れるフォルダー/グループを作成し evernote-api-1.17/src/cocoa/thrift, evernote-api-1.17/src/cocoa/edam を以下の様にコピーします
  • Build Settingsの Header Search Path に ./EvernoteTest/Library/thrift (Recusiveをチェック) を追加
  • ビルド。エラーが無ければ成功

3. iOS用テストコードの作成

EvenoteのサンプルコードにはiOSMac OS X用ありません。そこで Evernote APIを使うiPhoneアプリの作りかた - the time ship] のコードをベースに Evernoteに格納されているノート名の一覧をテーブルで表示するテストコードを作ってみました。

Evernoteアクセスの主要部分は以下のようなります、 APIの詳細は Thrift API Reference を参照して下さい。

  1. user(アカウント)情報の取得
  2. 認証用トークンの取得
  3. Note情報の取得
    • Noteの一覧は findNotes で行います、このメソッドは渡された検索条件(EDAMNoteFilterオブジェクト)にマッチするノートの一覧を取得します
    • 今回は全件の取得ですが、いろいろな条件が設定できるようです、詳細は API Overview Thrift API Reference を参照して下さい。
  • RootViewController.m の主要部分
#import "RootViewController.h"
#import "THTTPClient.h"
#import "TBinaryProtocol.h"
#import "UserStore.h"
#import "NoteStore.h"

#define USERNAM         @"XXXXXX"  // Sandboxのアカウント
#define PASSWORD        @"XXXXXX"  // Sandboxのパスワード
#define CONSUMER_KEY    @"XXXXX"   // 取得したconsumer key
#define CONSUMER_SECRET @"XXXXXX"  // 取得したconsumer secret

@implementation RootViewController
@synthesize noteTitles;

- (void) getEvernoteTitles {
    NSURL *userStoreURL = [NSURL URLWithString:@"https://sandbox.evernote.com/edam/user"];
    NSString *noteStoreURLBase = @"http://sandbox.evernote.com/edam/note/";    

    // user情報を扱うオブジェクトの作成
    THTTPClient *userStoreHTTPClient = [[[THTTPClient alloc] initWithURL:userStoreURL] autorelease];
    TBinaryProtocol *userStoreProtocol = [[[TBinaryProtocol alloc] initWithTransport:userStoreHTTPClient] autorelease];
    EDAMUserStoreClient *userStore = [[[EDAMUserStoreClient alloc] initWithProtocol:userStoreProtocol] autorelease];
	
    // 通信プロトコルのバージョンのチェック
    BOOL versionOK = [userStore checkVersion:@"EDMATest" :[EDAMUserStoreConstants EDAM_VERSION_MAJOR] :[EDAMUserStoreConstants EDAM_VERSION_MINOR]];
    if(!versionOK) {
        NSLog(@"checkVersion error");
        return;
    }
    @try {
	// 認証用トークンの作成
        EDAMAuthenticationResult *authResult = [userStore authenticate:USERNAM :PASSWORD :CONSUMER_KEY :CONSUMER_SECRET];
        EDAMUser *user = [authResult user];
        NSString *authToken = [authResult authenticationToken];
        
	// Note情報を扱うオブジェクトの作成
        NSURL *noteStoreURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", noteStoreURLBase, [user shardId]]];
        THTTPClient *noteStoreHTTPClient = [[[THTTPClient alloc] initWithURL:noteStoreURL] autorelease];
        TBinaryProtocol *noteStoreProtocol = [[[TBinaryProtocol alloc] initWithTransport:noteStoreHTTPClient] autorelease];
        EDAMNoteStoreClient *noteStore = [[[EDAMNoteStoreClient alloc] initWithProtocol:noteStoreProtocol] autorelease];
        
	// Notebookの一覧取得
        NSArray *notebooks = [noteStore listNotebooks:authToken];
        for(int i = 0; i < [notebooks count]; i++) {
            EDAMNotebook *notebook = (EDAMNotebook*)[notebooks objectAtIndex:i];
            NSLog(@"%@", [notebook name]);
        }
        // Note検索用フィルターの作成
        EDAMNoteFilter *allFileter = [[[EDAMNoteFilter alloc] initWithOrder:0 ascending:YES words:nil notebookGuid:nil tagGuids:nil timeZone:nil
                                                                   inactive:NO] autorelease];
	// Noteの一覧取得
        EDAMNoteList *list = [noteStore findNotes:authToken :allFileter :0 :1000];
        NSArray *notes = [list notes];
        NSLog(@"notes count %d", [notes count]);
        
        [noteTitles removeAllObjects]; 
        for (EDAMNote *note in notes) {
	    // Note名の取得
            NSLog(@"-- %@", [note title]);
            [noteTitles addObject:[note title]];
        }
        
        [self.tableView reloadData];
    }
    @catch (NSException * e) {
        NSLog(@"Caught %@: %@", [e name], [e reason]);
    }
    @finally {
    }
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;    
}

   ・・・・・・
  • RootViewController.h
#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {
    NSMutableArray *noteTitles;
}
@property(nonatomic, retain) NSMutableArray *noteTitles;

@end
  • RootViewController.m その他
    ・・・・・・
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.noteTitles = [NSMutableArray array];
}

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

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [self performSelector:@selector(getEvernoteTitles) withObject:nil afterDelay:0.2];
    
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    cell.textLabel.text = [noteTitles objectAtIndex:[indexPath row]];
    return cell;
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    self.noteTitles = nil;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.noteTitles = nil;
}

- (void)dealloc
{
    [noteTitles release];
    [super dealloc];
}

@end