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

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ですかね。

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 においてますので、参考にしてください。

シンプルな天気予報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

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

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

Icon

実に1年ぶりのアップデートで、ようやくv1.0になりました。

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

avail_on_app_store.png

変更点

  • パフォーマンスの改善
  • GUI(リソース)の変更
  • 手入力での経緯度設定の不具合を修正
  • タイムゾーンを経緯度から自動設定できる機能を追加

もう1年前のソースなので、あまりのひどさに絶句していたわけですが、そんな中で機能追加とバグ修正を行っています。ソース整理のついでにパフォーマンスもかなり改善しているかと。

Diana_v100

またメタ・グラマー様のご好意によりアプリアイコンが新しくなりました。本当にありがとうございました。

で、今回の審査はさらに早かった感じです。年内は無理かなと思っていたのですがね。

  1. 12/18: 提出
  2. 12/23: ready for sale

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