Cocoa と twitter と OAuth に関する覚書

いろいろと話題のOAuthですが、その意義とか仕組みとかはさておき、実際に実装してみようと思ったときに、ややこしいのはsignatureを作ってリクエストを生成するところなので、そのあたりをざっとメモっておきます。

あくまで個人的な覚書なんで、より詳しくは本家のサイトを参照してください。OAuth Core 1.0a


以下、NSString を ‘&’ や ‘=’ で連結しているかのような記述がありますが、単なる文字連結のイメージですのでご了承下さい。

はじめに

全体を通していえることですが、すべてのGET/POSTパラメータは key, value ともにURLエンコードされている必要があります。(実際 key のほうは不要であることが多いですが)

OAuthでのURLエンコードはRFC3986準拠ですので注意しましょう。CFURLを用いて例えばこんなふうにできます。

CFStringRef encoded = CFURLCreateStringByAddingPercentEscapes(
    kCFAllocatorDefault,
    (CFStringRef)str,
    nil,
    CFSTR(":/?=,!$&'()*+;[]@#"),
    kCFStringEncodingUTF8);

以下、http://twitter.com/statuses/user_timeline.xml?screen_name=itok_twit にアクセスすることを前提にメモっておきます。

標準パラメータ

OAuthの標準パラメータとして、以下のようなものがあります。(GETなりPOSTなりで渡す必要があります)

oauth_consumer_key
twitterでアプリを登録したときにもらえる値。そのまま使います
oauth_nonce
ユニークな文字列を作成。タイムスタンプでもよいけれど、より確実にUUIDを使うとか
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef nonce = CFUUIDCreateString(kCFAllocatorDefault, uuid);
CFRelease(uuid)
oauth_signature_method
twitter は HMAC-SHA1 固定
oauth_timestamp
タイムスタンプ。 time(NULL); の返り値でOK
oauth_version
1.0固定

signature

signature の元となる signature base は分解した link や normalized parameter を元に ‘&’ で結合します。

NSString* base = [HTTP method]&[my_urlencode_rfc3986(link)]
    &[my_urlencode_rfc3986(parameter)];

ここで link はいわゆる query をのぞいた部分。

NSString* link = @"http://twitter.com/statuses/user_timeline.xml";

normalized parameter は POST/GETパラメータ(標準パラメータ含む)を全部key=valueの形にして昇順ソートし、それを ‘&’ で結合して1つの文字列にしたものです。

NSString* parameter = [key1]=[val1]&...;
/* oauth_consumer_key=xxxx&oauth_nonce=xxxx&
   oauth_signature_method=HMAC-SHA1&oauth_timestamp=123456&
   oauth_version=1.0&screen_name=itok_twit */

次に HMAC で使う key も ‘&’ で結合。

NSString* hmacKey = [my_urlencode_rfc3986(consumer secret)]
    &[my_urlencode_rfc3986(token secret) *なければ空文字];

signature base と HMAC key から HMAC-SHA1 ハッシュを生成し、これを Base64 エンコーディングしてURLエンコードして signature とします。

NSString* digest = my_hmac_sha1(base, hmacKey);
NSString* base64 = my_base64_encode(digest);
NSString* signature = my_urlencode_rfc3986(base64);

パラメータに oauth_signature=signature を追加して、最終的にアクセスするURLの出来上がり。

備考

  • POSTは ContentType: application/x-www-form-urlencoded で行う
  • より詳しい例は本家の Appendix 参照

と、ざーっと書き連ねてみました。手元で動いている現状のソースから引っ張ってきたので多分大丈夫と思いますが、なんかおかしかったら教えてください(いつでも弱気)

iPhoneとTwitter OAuth認証の流れについて

twitterヘの認証方式は2つありまして、1つはお馴染みBasic認証。もう1つがOAuthです。で、なにやら、今後はOAuthしか受け付けなくなるとかいう話があって、ちょっと盛り上がっているtwitterクライアント界隈。

OAuthの細かい話はさておき、twitterのOAuth認証(access_token取得)の手順には2通りがあります。(太字はiPhone上での主な挙動)

サーバ型

  1. アプリごとの key から request_token 取得
  2. request_token を用いて twitter のサイトを表示
    → MobileSafari起動
  3. ユーザにログインしてもらう
  4. 設定していた callback URL が呼び出される
    → サーバ側で元アプリの URL scheme にリダイレクトし、元アプリを起動
  5. callback に渡された oauth_token から access_token を取得
    → アプリ起動時に渡された URL から access_token を取得

クライアントアプリ型

  1. アプリごとの key から request_token 取得
  2. request_token を用いて twitter のサイトを表示
    → 内蔵ブラウザで表示
  3. ユーザにログインしてもらう
  4. サイトに表示されたPINをアプリに入力してもらう
    → ユーザがブラウザ外のフィールドに別途入力
  5. 入力された PIN から access_token を取得

まあ、セキュリティ云々の話は別として、どっちもどっちだと思うわけです。サーバ型ではアプリをいったん離れなければなりませんし、クライアントではPINを入力するという手間がかかります。

じゃあ、アプリを離れることなしに、サーバ型の方式は使えないのかと思って試してみました。

やってみたこと

UIWebView の delegate でアクセス予定の URL を取得できます。ここで、指定していた callback URL にアクセスしようとしていたら、そこから oauth_token パラメータを取り出せるのではないかと考えました。

結果

予想通り、UIWebView の delegate で (4) の callback 呼び出しをとらえて、そこから oauth_token を取得することができました。これだと操作がアプリ内蔵の UIWebView だけでとじているので、アプリの切替もPINの入力も要りません。極端な話、callback URL 先の実体すら不要です(存在しないURLを指定していても大丈夫のようです←実際にアクセスする前に request を止めるので当然といえば当然ですが)

delegate 内はこんな感じで。

- (BOOL)webView:(UIWebView *)webView
    shouldStartLoadWithRequest:(NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType {
    if ([[request.URL host] isEqualToString:@"callback.example.com"]) {
        NSString* query = [request.URL query];
        NSArray* arr = [query componentsSeparatedByString:@"="];
        if ([arr count] > 1 && 
            [[arr objectAtIndex:0] isEqualToString:@"oauth_token"]) {
            NSString* token = [arr objectAtIndex:1];
            // do something
        }
        return NO;
    }
    return YES;
}

OAuthの運用上、あるいはセキュリティ上の問題があるかもしれませんが、それらをさておいたとして純粋にユーザ視点にたてば、これが一番スマートなのではないでしょうか、と。

あとは、twitterのログインページがiPhoneに最適化されれば問題ないんですけれど・・・

***一応、手元で開発中にアプリでは問題なく動いているんですが、なんかおかしかったら教えてくださいませ。

iPhoneアプリ内でYouTube動画を再生する

たまには開発の小ネタでも。

ご存知のようにiPhoneでFlashは再生できません。なので、基本Flashベースで動いているYouTubeの動画なんかも再生できません。(もちろん、標準のYouTubeアプリは別ですが)

動画再生専門のアプリならともかく、ちょっとしたYouTube動画を再生したいと思った場合には、標準のYouTubeアプリをURLスキームで起動するのが手っ取り早いわけですが、それだと自分のアプリに戻ってこれなくなります。

そこで、YouTube動画をアプリ内で再生する方法。

YouTubeはFlashベースではありますが、MP4ファイルが取り出せることもよく知られている事実です。もちろん簡単には取り出せないのですが、

この辺りを参考にしますと、get_video_info.php で取得した token を用いて fmt=18 を指定すればmp4で取得できそうです。

余談ながら、最初はサーバでこのtokenを取得してアプリからの要求をリダイレクトさせようと思ったのですが、tokenはアクセス元によって異なるようなので、DL元(今回の場合はiPhoneアプリ)でtokenを取得しないと行けないようです。

というわけで、NSURLConnection あたりを用いて token を取得し、それをそのまま MPMoviePlayerController に渡せば無事に YouTube の動画再生ができました。(コンソールには Warning: MPMoviePlayerController may not support file of type php のようにPHPファイルは再生できないといった警告がでますが、YouTube側のリダイレクトの話なので特に問題なさそうです)

任意の video_id を渡して MPMoviePlayerController で再生するサンプルコードを GitHub においてますので、参考にしてください。

地デジ化

去年の8月に6年使っていたブラウン管のテレビ(28インチ)が壊れて約5ヶ月。余っていた14インチ液晶でその場をしのいでいたわけですが、今年の初売りでついにデジタルテレビを購入。今日、それがようやくとどきました。

new_tv

28 => 37インチならそれほどにインパクトもないかもしれませんが、なにしろ最近はずっと14インチだったのでその比2.6倍。これはほんとにでかくなりました。

作り付けのテレビ台にも無事におさまりまして、ほっと一安心。これでようやく画面に映った文字が読めます。

余談ながら、1年前の地デジテレビと違って、いろいろとパフォーマンスとかUIとか改善されているもんなんですね。アナログ => デジタルの移行は動作のもたつきが気になる印象があったのですが、それほどでもなかったのでちょっとびっくりしました。

そういや、偶然ながら冬季オリンピックにも間に合ってます。これで家族でフィギュアでもみますかね。

2010年に読んだ本(1)「地名の世界地図」

読んでいる間に年を越えてしまいました。

4166601474 地名の世界地図 (文春新書)
文藝春秋 2000-12

by G-Tools

地名がどうやってできてきたのかという本。もちろん歴史的に複雑なものもあるけれど、単にその場所の属性だったりとかして結構面白かったです。

一番「へえー」と思ったのは、「ブラジルで生えるからブラジルウッド」なのではなく「ブラジルウッドが生えているからブラジル」だということ。当然「ブラジル」のほうが有名なので、どうしても前者だと思いがちですが、実は違ったんですね、と。

さて、今年はどれだけの本を読むのかなあ、と。そういう意味も込めて、連番をつけてみました。

初売りとお年玉

正月休みも今日まで。1日は親戚で集まり、2日は仲間内で集まり、で3日は初売りへ、と。

今年の狙いはテレビです。去年の8月に28インチブラウン管がお亡くなりになってから、14インチのアナログ液晶でその場をしのいでいたわけですが、なにしろ小さすぎて家族内で「字が読めない」との苦情多数。もともとテレビはそんなに見るほうじゃないですが、いくらなんでもしんどいよね、と。

選択肢としてはいろいろあったんです。それこそiMac27インチをテレビとして使うとか、27インチ液晶ディスプレイにBDレコーダーを接続するとか、っていう本流じゃないものもありました。

そんな中、初売りでテレビが安くなっていたら真剣に考えてもいいかもね、と思っていたんですが、見事に予想(期待)的中。近くの家電量販店で価格.com最安値と同等の値段ででておりました。送料無料だし、壊れたテレビも一緒に回収してくれるし(こっちはもちろん別料金がかかりますが、それでも個別に頼むよりは安い)。

もともと1日の夕方に見にいったんですが、その日の分(10台)が全部売り切れだったそうで、今日の朝一で再挑戦。開店直後に行った時点ですでに2台売れているという売れ筋商品を無事にGETしてきました。

ちなみにこれの37インチモデル。場所の都合で37インチMAXだったのと、液晶表面が若干固めで「多少の衝撃なら耐えられるかも」とのことで子供のいる家庭としては妥当な選択ではなかったかと。フルHDですしね。

そこまで急ぎじゃなかったので、納品は来週末にしてもらいました。私がいないと配線もままならないでしょうし・・・(コンセントが壁の中を通さないといけないとかそういう事情で)

楽しみですが、これでDVDを見てしまったらますますBDが欲しくなるんだろうな。こっちは来年の初売り狙いか?

で、ほかにも百貨店でいろいろ初売り(主に子供用品)を物色して、帰ってきたら嬉しいお年玉が。

m_rose_postcard

野間美由紀先生からの年賀状です。しかもサイン入り。twitter上でいろいろとお世話になっている野間先生のブログにて募集されていたので、申し込んでみましたら見事送っていただきました。ありがとうございます。これは会社に飾ろうかしら。

そんなこんなで、明日からまたがんばっていきましょう!

2010年の目標

さて、今年は正月休みもしっかりありますし、ちゃんと目標を書いておきましょう。

iPhone関連

これは昨年末からずっと考えていたことなのですが、日曜プログラマの自分としては初の試みとして有料アプリを出す方向で検討したいと思っています。

元来「自分が欲しくて作っただけなので、もしよかったら使ってみてください」というスタンスで作り続けていたわけですが、その一方で「無料だし、細かいことは気にしないで、、、」という逃げ道を用意していたのも事実です。

開発費を回収したいとかそういうことでは一切なく、純粋に挑戦してみたいなと思いましてね。既存の無料アプリでいうと、アプリ5本の合計で20万DLをこえる感じなのですが、さてそれが有料の世界でどのくらい通じるものかと。

まあ、もちろん、仕事でもやってますから、有料の世界が厳しいのは十分に承知しています。2桁DL数が落ちることも知っています。

ですが、「アプリの価値」という視点で考えた時に、はたして有料で「価値」を認めてもらえるものを自分が作り出すことができるのか、と。そこに挑戦してみたいんですよね。(あ、もちろん、有料でしか作らない、というわけじゃないです)

すでに、有料・無料関係なく、3本ほどのアプリの構想があります。それぞれがそこそこ動き出しています。みなさんによいものを届けられたらなあ、とがんばりますのでよろしくお願いします。

これについては、また別エントリを書くかもしれません。

Mac関連

なんだかんだいって、iPhone開発の合間にMac系アプリの開発をしていることは否めないのですが、既存のアプリもそこそこユーザさんがおられて、要望もたくさんいただいていますし、特に常用している Hummings はもうちょっとがんばっていきたいですよね。

もう一つだけアプリの構想もあるんですけれど、こちらは余裕があるかどうかの勝負なんですけれど、、、どうなることやら。

その他

30をこえて、いい加減に腹まわりの肉が気になってきました。「どんだけ食べても太らない」という時代は終わったようです。何度か挑戦しては半端になっている運動をもう一度見直してみたいと思っているところです。「思っている」だけなので、なんだか弱い感じですが、でもまあなんとかしないとなあ、、、と。

あとは、もう少し音楽との関わりを戻したいですね。演奏者としてではなくても、日常にあふれていたはずの音楽が、最近はそうでもなくなってきていたので。いろいろと久しぶりに聴きたい曲も出てきましたし、時間を見つけて音の洪水に浸ってみたいなあ、と。

ほかにもいろいろとある気がしますが、欲張っても仕方ないので、とりあえずこんなところで。

最後にいつものやつを。

まだまだ行ける。もっと上に行ける。

今年もよろしくお願いします。

2009年終わり

2009年ももう終わりです。今年もあっという間の1年でした。

毎年恒例で、年初の目標を確認、、、と思ってみてみたら、そんな余裕は全くなかったみたい。まあ、だいたい、2008-2009の年末年始はほぼ休みなしでしたから。

なので、目標と照らし合わせることはできないですが、今年のまとめを軽く。

転職した

まず、なんといってもこれが一番の出来事です。もうすぐ丸一年になるわけですが、一年目の挑戦はなんとかなったようで、無事に同じ職のまま年を越すことができました。これも会社と家族のおかげです、ほんと。

残業無しの1日8時間勤務というのは、京都大阪間の通勤時間を考えても、家庭に優しく、飲み会でもない限り毎日家族の起きている時間に帰れるというのは精神的にもかなりよかったです。むやみやたらに時間をかけたらいいってもんじゃないってことですよね、きっと。

開発者としての自分は、確かに去年よりはパワーアップしたと思います。だけど、まだまだですよね。先日、仕事納めの日に話をしていましたけれど、まだまだスタートラインを少し過ぎた程度。もっと質の良いものを、もっと効率良く、まだまだ上に行けるはずですから。

iPhone関連

ようやくiPhone開発者として定着しつつあると思いますが、まあ、たくさんアプリを作らせてもらいました。会社ではこんな感じ。個人でも3つ新規リリースしました。もちろん、数がすべてじゃないですが、よくがんばったな、ということで。

Mac関連

iPhone開発者であるとともに、Mac開発者でもありたいというのが理想です。そんなわけでいくつかのアプリを新規にリリースし、それなりに使っていただけるようでなによりです。

その他

開発全般的な話として、以前より表に出る機会を増やすことにしました。できるだけ積極的にイベントやオフ会(飲み会)に参加し、開発者やユーザさんと接点ともつ。こうすることで、自分の作るもののイメージを膨らませたり、新しいアイディアをいただいたり、単純に自分自身の宣伝にもなったり、といい方向に向かっている気がします。twitterにも助けてもらった感じで、これは来年も積極的にやっていこうと思います。ネットでもリアルでも人脈は大事だな、と。

そうそう、結婚前から使っていたG5を手放し、MacBook Pro 13inchに乗り換えました。PPCではiPhoneの開発もSnowLeopardの開発もままならないのでね。ノートを手にしたことで、フットワークが軽くなった気がします。

プライベートとしては、そんなに大きな出来事もなかったので、まとめとしてはどうしても開発優先な話になってしまいますが、そこはそれ、挑戦の一年でしたから。

最後になりましたが、今年一年どうもありがとうございました。来年もよろしくお願いします。

シンプルな天気予報iPhoneアプリ「そら案内」v1.5 が公開されました

Sora-Annai

今度こそ、年内最後のリリースです。

avail_on_app_store.png

変更点

  • 現在地予報で現在地検索履歴を管理する機能を追加

いろんな場所で現在地予報を使った場合に、そのGPS→住所の検索履歴を残すようにしました。よく行く場所の現在地予報を手元でも確認できるようになります。例えば、自宅で会社付近の予報をチェックしたり。

sora-annai_v150_1sora-annai_v150_2

年明けリリースだと思っていたのですが、あっという間に審査に通ってしまいました。この時期に提出されたアプリが少ないようにもみえませんので、審査の方法が変わったのかな?

  1. 12/28: 提出
  2. 12/31: ready for sale

みなさん是非お使いくださいませ。

シンプルな月齢表示アプリ Diana v1.0.1 が公開されました

Icon

Diana(ディアナ)は「お、今日の月はキレイだね」と思った時にさらっと月齢なんかを調べられるアプリです。

avail_on_app_store.png

変更点

  • 日付フォーマットがおかしくなる問題を修正

今回はちょっとした修正です。ほとんどの人には関係ないかもしれません。

これの審査はものすごく早かったです。審査のクリスマス休暇が終わった直後に提出したのですが、ほぼ1日で通過しましたね。

  1. 12/29: 提出
  2. 12/30: ready for sale

アップデートされる方も、新規の方も是非どうぞ。