Evernoteクリッピングの実装手順
今回、ATND暦 ver0.9.2でEvernoteへのクリッピング機能を追加しました。実は、以前SOICHAで実装したことがあるので今回は2度目になります。
当時はCocoa用のソースがiPhone用にカスタマイズされてなく改変しながら組み込んだのですが、最新バージョンではiPhone用にも使えるようになっていて、サンプルコードも用意されているので更に簡単になったように思います。
今回はEvernote API ver 1.19を利用したEvernoteクリッピングの実装手順について書きます。
APIキーの取得
EvernoteデベロッパーサイトよりAPI Keyを申請します。
http://www.evernote.com/about/developer/api/
「Evernote name」はEvernoteのアカウント名を入力します。APIキーはこのアカウント名になるので個人と切り離したい場合は別にEvernoteアカウントを取得してから行なったほうがいいかもしれません。
「Organization」は会社等の組織に属していない場合は自分の名前を入力します。
Application Typeは iPhoneアプリですので「Client Application」を選択します。
「Application Details」はAPIの使い道を記載しますが、適当に「I will develop my iphone application evernote integration. I will make an application.」くらいに書いておけばいいと思います。
後は、License Agreementに同意してSUBMITします。
暫くすると、登録したメールに Consumer Key と Consumer Secret が届くので保管しておいてください。
尚、この手順で登録したKeyはあくまでsandboxサーバ上でのみ有効で、本番サーバでは有効になっていません。
EvernoteデベロッパーサイトよりSDKをダウンロードします。
中に様々なプラットフォームのソースが梱包されていますが、iPhoneの場合はcocoa/src 以下があれば事足りますが、折角なのでサンプルソースがありますので動かしてみます。
Evernote.mファイルのヘッダ付近にある、consumerKey、consumerSecret、username、passwordを設定します。username/passwordはクリッピングする操作者のアカウント情報です。
実行して、Titleに適当に入力して「Create a new note」を押してみてください。成功した場合、サンドボックス上のEvernoteにクリッピングされたことが確認できます。
https://sandbox.evernote.com/ にアクセスして確認できます。
クリッピングに必要な骨組みのソースを抜き出してみました。
addNoteViewController#sendNoteEvernote
-(IBAction)sendNoteEvernote:(id)sender{ // Creating the Note Object EDAMNote * note = [[[EDAMNote alloc] init]autorelease]; // Setting initial values sent by the user note.title = @"test"; // Simple example ENML we are going to use as content for our note NSString * ENML = [[[NSString alloc] initWithString: @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">\n<en-note>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."]autorelease]; ENML = [NSString stringWithFormat:@"%@%@", ENML, @"</en-note>"]; NSLog(@"%@", ENML); // Adding the content & resources to the note [note setContent:ENML]; // Saving the note on the Evernote servers // Simple error management @try { [[Evernote sharedInstance] createNote:note]; } @catch (EDAMUserException * e) { NSString * errorMessage = [NSString stringWithFormat:@"Error saving note: error code %i", [e errorCode]]; UIAlertView *alertDone = [[UIAlertView alloc] initWithTitle: @"Evernote" message: errorMessage delegate: self cancelButtonTitle: @"Ok" otherButtonTitles: nil]; [alertDone show]; [alertDone release]; return; } // Alerting the user that the note was created UIAlertView *alertDone = [[UIAlertView alloc] initWithTitle: @"Evernote" message: @"Note was saved" delegate: self cancelButtonTitle: @"Ok" otherButtonTitles: nil]; [alertDone show]; [alertDone release]; }
Evernote#createNote:
- (void) createNote: (EDAMNote *) note { // Checking the connection [self connect]; // Calling a function in the API [noteStore createNote:authToken :note]; }
Evernote#connect
- (void) connect { if (authToken == nil) { // In the case we are not connected we don't have an authToken // Instantiate the Thrift objects NSURL * NSURLuserStoreUri = [[[NSURL alloc] initWithString: userStoreUri] autorelease]; THTTPClient *userStoreHttpClient = [[[THTTPClient alloc] initWithURL: NSURLuserStoreUri] autorelease]; TBinaryProtocol *userStoreProtocol = [[[TBinaryProtocol alloc] initWithTransport:userStoreHttpClient] autorelease]; EDAMUserStoreClient *userStore = [[[EDAMUserStoreClient alloc] initWithProtocol:userStoreProtocol] autorelease]; // Returned result from the Evernote servers after authentication EDAMAuthenticationResult* authResult =[userStore authenticate:username :password : consumerKey :consumerSecret]; // User object describing the account self.user = [authResult user]; // We are going to save the authentication token self.authToken = [authResult authenticationToken]; // and the shard id self.shardId = [user shardId]; // Creating the user's noteStore's URL noteStoreUri = [[[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@%@", noteStoreUriBase, shardId] ] autorelease]; // Initializing the NoteStore client THTTPClient *noteStoreHttpClient = [[[THTTPClient alloc] initWithURL:noteStoreUri userAgent: @"iosdemo" timeout:15000] autorelease]; TBinaryProtocol *noteStoreProtocol = [[[TBinaryProtocol alloc] initWithTransport:noteStoreHttpClient] autorelease]; noteStore = [[[EDAMNoteStoreClient alloc] initWithProtocol:noteStoreProtocol] retain]; } }
EvernoteのノートはENMLというXML形式で保持される仕組みとなっています。
クリッピングするXMLデータを事前に作成する必要があります。
ENMLの仕様はこちらに記載されています。
http://www.evernote.com/about/developer/api/evernote-api.htm#_Toc297053072
ここでは EDAMNote に titleとcontentを設定して EDAMNoteStoreClient#createNote:: でノートを送信しています。
サンプルでは、authTokenは送信時に認証していますが、実際にはユーザー名、パスワードを登録する画面が必要となりますのでそこで認証させ、authTokenを保持しておけばよいかと思います。
実際に組み込む時は、cryptoとapi、Evernote.h、Evernote.mを自分のプロジェクトにコピーして、固定値となっている箇所をNSUserDefaultsとかから引っ張る仕組みに組み替えます。
アクティベート
組込み後、sandbox上でテストが完了したらKey取得時にメールにURLがありますので、そこからアクティベート申請を行います。
http://www.evernote.com/about/developer/api/activate.php?code=[consumerKey]
申請して2日ほどでアクティベート完了のメールが届くので、NoteStoreとUserStoreのURLをsandboxからwwwに変更して本番サーバにてテストします。
//NSString * const userStoreUri = @"https://sandbox.evernote.com/edam/user"; //NSString * const noteStoreUriBase = @"https://sandbox.evernote.com/edam/note/"; NSString * const userStoreUri = @"https://www.evernote.com/edam/user"; NSString * const noteStoreUriBase = @"https://www.evernote.com/edam/note/";
そこまで難しい仕様(ENML)でなければ実装は認証画面を含め2、3日くらいで可能だと思います。
ENMLが厳格な仕様なのでWebのHTMLのBody内をそのままクリッピング使用とすると大抵ENMLバリデーションエラーとなります。
Evernote site memoryのjavascriptを上手く活用できれば、全てのHTMLでクリッピングできそうな気がしますが、まだ試していないので余力があればチャレンジしてみます。