合わせ鏡風カメラアプリ「無限スコープ」を公開しました

Icon

久しぶりの新作です。新作っていう意味ではTweetMap以来じゃないですかね。

今回は完全に小ネタです。別に実用性とか、意義とかそういうことを考えてはいけません。面白いからOKという類いのものです。以下、紹介文からの引用です。

見慣れた風景に不思議な無限の奥行きを持たせてみませんか?

「無限スコープ」はiPhoneのカメラ越しに見た風景に合わせ鏡のような無限の奥行きを持たせるアプリです。その一瞬を切り取るだけでなく、過去の映像で無限の奥行きを持たせるのでリアルタイムに移ろう不思議な映像をディスプレイに映し出します。

で、こんなふうに映ります。鏡像反転するわけじゃないので「合わせ鏡【風】」です。

InfiniteScope

「撮る」ことより「見る」ことを重視しました。なので、カメラじゃなくて「スコープ」。リアルタイムに移ろう映像を楽しんでいただければと。

ただ、ずっと見続けると、酔うかもしれませんので注意してくださいね。

avail_on_app_store.png

もともとは、開発者内で話題の UIGetScreenImage のサンプルをいじっていたのですが、ふとしたところからこういう形になりました。えーと、構想からリリースまでほぼ1週間です。はい。

最近は審査も早くなりまして、新作だというのに1週間かからなかったですね。アイコンはいつものごとく、メタ・グラマー様にお世話になりました。いつもいつもありがとうございます。

  1. 1/21: 提出
  2. 1/25: ready for sale

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

appbankの中の人を京都で囲んで和む会に参加してきました

iPhoneアプリのレビューサイトとして有名なAppBankの中の人が京都に来られるということで、いつも大変お世話になっていることもありますし、一度話をしてみたいな、とそのオフ会に参加してきました。

いつのオフ会の雰囲気と違って、ほとんどが初対面の人ばかりで、普通のiPhoneユーザーのみなさんばかりでしたが、それはそれで面白かったです。拙作アプリの愛用ユーザーの方も結構おられまして大変励みになりました。

で、目的のAppBankさん。なんといいますか、ネット上で見ている印象とほとんど変わらない雰囲気で、月並みな言葉で言えば強いオーラを感じました。一次会ではそれほどお話できなかったのですが、二次会では同じテーブルでじっくりとお話を聞くことができました。

ちなみに、AppBankさんは二次会で離脱されましたが、最後に残った3人で三次会まで決行され、家に帰って寝たのは3時くらいでしたかね。地元京都での開催ってことで時間の余裕があったのでつい長居してしまいました。

全体として大変面白く有意義な時間を過ごすことができました。みなさんお疲れさまでした。

最後に、今回強く思ったのはもっといいものを作らないといけないってことです。なので新年早々にも書いたけれどもう一度。

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

ワンボタンの声の特番にゲストとして参加しました

Apple系Podcastとして有名なApple News Radio ワンボタンの声に参加させていただきました。

おそらくことの発端は、僕自身がワンボタンの声のアプリを作ろうとしたことによると思うのですが、その話の流れで「12月か1月くらいに収録を」みたいなことになりまして、みなさんのスケジュール調整の結果、ようやく先週末に収録がとりおこなわれました。

もちろん、podcastの収録は初めてでしたし、そもそもskypeでの複数人チャットもはじめてのことだったので、ちょっと勝手のつかめないままあっという間に時間が過ぎてしまった気がします。

で、配信されたのがこれです。

まず最初にプレビュー版を聴いた時、編集ってほんとにすごいなあ、と。いろんな雑談とか、話がそれてしまったりしていたのに、こんなにきれいになるものかとびっくりです。

で、個人的な感想、というか反省ですが、改めて自分の声を聞くと、やっぱり鼻声で、そして発声があまりよろしくないようで、だからちょっと聴きにくいですね。自分で聴いていても「え?なんていった?」と思うこともありましたし。あとは、編集のことも考えて、あまり発言タイミングがかぶらないようにすべきでした。これははじめてのことなので仕方ないというところですが。

肝心の中身ついては、あくまで一開発者の主観ですので「こういう考え方もあるんだな」という軽い気持ちで聴いていただければよいなと思います。自分で言うのもなんですが、偏っている気がしなくもないのでね。

ワンボタンの声のみなさん、山村さん、松尾さん、魚井先生、どうもおせわになりました。もしこれに懲りることなく、また機会がありましたらご一緒させていただければ幸いです。

MUGNET 1月度定例会に参加してきました

大阪のMacユーザグループ MUGNET の定例会に参加してきました。

前から一度はいってみたいと思っていたのと、最近作っている「ワンボタンの声 for iPhone」をワンボタンのメンバーの人に直に見ていただきたかったこともあり、ようやく日程調整がついていってきました。

といいつつ、最大の失敗を犯してしまいまして、前日にアプリのデモ動画を撮影したにも関わらず、それを持っていくのを忘れるという大失態。

これです↓

思っていたより枠が長くて、ちょっと準備不足だった感が否めないですが、それでも二次会にわたっていろんな人とお話が出来たのはよかったです。

次は月末のAUGMですかね。

3年ぶりの献血日記(61)

もう3年以上あいてしまいました。久しぶりの献血です。

子供が産まれてから、成分献血に必要な2時間を空けることが難しくなって、「30才までに100回行く!」というなんだかわからない個人的目標も達成されないままに3年が過ぎました。

諸般の事情により(一部省略)毎月15日は午前半休をいただくことが多いのですが、実際用事があるのは9時過ぎまでなので、それから昼までの間なら十分に時間がとれるのでは?と思ったのがついこの間の健康診断の時。

で、昨日の15日に行ってきました。久しぶりに阪急グランドの献血ルームへ。本当は西梅田のほうがキレイで好きなんですが、金曜は休みってことだったので、阪急のほうに行ってきました。

さすがに平日は空いています。でもそれなりにベッドも埋まっています。まったくガラガラっていうわけじゃないんですよね。いいことですけれど。

久しぶりな上に、一番新しい献血手帳を紛失していたので、昔の手帳を持っていきましたが、なんとかデータも残っていて一安心。前回の記録を見ると、上の子が生まれる1週間ほど前に行ったのが最後でした。ほんと、子供が出来てから行ってなかったってことですね。ちなみに今回で61回目でした。

献血手帳、最近は磁気カード + 暗証番号なんですね。マークシートがタッチパネル入力になった時もびっくりしましたが、着実にシステムは進化しているようです。

相も変わらず、採血のところで血管の太さを気に入ってもらえて、これまたいつの間にか導入されていた新型の機械で血小板を取ることになりました。ちと針が痛かったけれど、それ以外は特に問題なく、1時間が経過。

新しくなっていたこともあり、全然変わっていないこともあり、そんな中懐かしい気持ちで献血ルームをあとにしました。これからまた時間を見つけていけるとよいですね。

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

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

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

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