Paradigm Shift Design

ISHITOYA Kentaro's blog.

Evernote APIのラッパ、EVNConnectを作った

ヒーホー。


Evernote APIの自家製ラッパを作りました。
githubにおいてあります。kent013/EVNConnect · GitHub


- 注1:thriftで生成されたevernote-apiの実装の都合上、リクエストそのものは同期です。(追記:非同期もサポートしました)

せっかちなひとむけ

  1. kent013/EVNConnect · GitHubをgit clone/ダウンロード
  2. ENVConnect/ENVConnectをコピー
  3. ライブラリの設定をする。
    • oauthconsumer/KissXMLはソースをコピーするだけじゃ駄目なのでドキュメント読んで。
  4. URL schemeの設定をする
  5. コード書く

はじめに

自分的に次はEvernoteだったので、とりあえずEvernote APIとか読んでたんですが、API呼び出しに必要なauthTokenの取得コードが

EDAMAuthenticationResult* authResult =
  [userStore authenticate:username :password : consumerKey :consumerSecret];

となってて、usernameとpasswordを入力に与えないといけないみたいで。
正直、UI考えたりするのが面倒くさいので、ううーっとなっていました。


で、いろいろ調べてたら(OFFLINE) Community Messageという投稿がみつかりました。


Julien Boedecさんという方が、OAuthが終わったときに取得できるshardIdとauth_tokenを使えばuserstore初期化する必要ないよ。アクセス先の、shardIdとauthTokenさえ分かればnoteStore使えるよ。


的な事を言ってたので、おおお、とおもい、やってみた次第です。

機能

1, OAuth認証
OAuthConsumerを使いました。
私はiPhoneでしか使わないし、マルチタスクサポートしてないiOSは対象外なので、ダイアログは実装してません。

[evernote_ login]

と書くと、Safariに切り替わって認証が始まります。


2, Request Delegate

EvernoteのAPI実装はThriftを使っているんですが、それに実装されているTHTTPClientは同期的なリクエストをしていました。

NSURLConnectionのsendSyncronousRequestはdelegateを受け取ってくれなくて、アップロードの進捗とかを外部から知る事ができません。


なので、THTTPClientを継承したクラスでasynchronousなリクエストを送り、delegateを設定できるクラスを作りました。ただし、EDAMNoteStoreの実装がsynchronousなリクエストを前提としているので、アップロードが完了するまで処理をブロックするような実装をしました。なので、UIの描画などがおかしくならない様に呼び出し側できちんと実装してあげる必要があります。


追記:結局,自分が利用する用途ではNSOperation中で上記実装を動かそうと思っていたのですが,どうもうまく行かないので,sendSynchronousRequestを送るラッパメソッドと,非同期な普通にNSURLConnectionを使った実装を用意しました.が,正直createNoteしか私は使わないので,ラッパはそれしか実装していません.


ただし,非同期に動作させるための実装はEvernoteNoteStoreClient.mに書いてあるので,必要ならば比較的簡単に実装できるかと思います.

@protocol EvernoteRequestDelegate <NSObject>
@optional
- (void)requestLoading:(EvernoteRequest*)request;
- (void)request:(EvernoteRequest*)request didReceiveResponse:(NSURLResponse*)response;
- (void)request:(EvernoteRequest*)request didFailWithError:(NSError*)error;
- (void)request:(EvernoteRequest*)request didLoad:(id)result;
- (void)request:(EvernoteRequest*)request didLoadRawResponse:(NSData*)data;
- (void)request:(EvernoteRequest*)client 
    didSendBodyData:(NSInteger)bytesWritten
    totalBytesWritten:(NSInteger)totalBytesWritten 
    totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
@end

のようなNSURLConnectionDelegate/NSURLConnectionDataDelegate的なdelegateを用意しています。


3, EDAMNoteStoreのラッパ

適当にWrapしましたが、私が必要な物しか実装してない上に、未テストなものが多いので、必要なら実装/デバッグしてください。現状サポートしているAPIEvernoteRequestにあります。

使いかた

  1. EVNConnect/EVNConnect以下のソースをコピー
  2. ライブラリの設定
  3. URL schemeの設定をする
  4. コードを書く


基本的にはEvernoteクラスのインスタンスを作って、ログインして、Request取得して実行って感じです。

#import "EVNConnect.h"
@interface MainViewController : UIViewController<EvernoteSessionDelegate, EvernoteRequestDelegate>{
    __strong Evernote *evernote_;
}
@property (nonatomic, readonly) Evernote *evernote;
@end

みたいに宣言しておいて、

evernote_ =
        [[Evernote alloc] initWithAuthType:EvernoteAuthTypeOAuthConsumer
                               consumerKey:EVERNOTE_CONSUMER_KEY
                            consumerSecret:EVERNOTE_CONSUMER_SECRET
                            callbackScheme:@"evnconnecttest://authorize"
                                useSandBox:YES
                               andDelegate:self];

とかしてインスタンスを作ります。consumerKey/consumerSecretはEvernoteで申請した奴で、callbackSchemaはProjectのInfoのURL Typeで指定した奴です。
そしたら、

[evernote_ loadCredential];

とすると保存されたauthTokenとshardIdなどが読み出されます。
次に、

[evernote login]

とすると、loadCredentialで読み出された情報が無効なとき、safariに遷移して認証が行われるはずです。


ここまでで、とりあえず認証は終わりですがcredentialをsaveしたりclearしたりするために、EvernoteSessionDelegateをimplementsして

-(void)evernoteDidLogin{
    [evernote_ saveCredential];
}
- (void)evernoteDidLogout{
    [evernote_ clearCredential];
}
- (void)evernoteDidNotLogin{
    [evernote_ clearCredential];
}

などとしておくと良いでしょう。


最後に、同期的なリクエストを送る場合には,

EDAMNotebook *notebook = [evernote_ notebookNamed:@"test"];

非同期なメッセージを送る場合には,

EDAMResource *resource1 = 
[evernote_ createResourceFromUIImage:[UIImage imageNamed:@"sample1.jpg"]];
    
[evernote_ createNoteInNotebook:notebook 
                          title:@"testnote" 
                        content:@"testnotemogemoge" 
                           tags:[NSArray arrayWithObjects:@"Photo", @"Bear", nil]
                      resources:[NSArray arrayWithObject:resource1]
                    andDelegate:self];

とすればいいです.

現状サポートしているAPIEvernote.hを見てください。

以上〜。