2008年12月28日日曜日

自分のiPhoneアプリにroute-meを組み込んでみる

  • http://code.google.com/p/route-me/wiki/EmbeddingGuidev2 ただし一部間違いなど不正確なところがある
  • route-meはフレームワークになっていない(ただのXcodeプロジェクト)なので、自分のアプリに組み込むためにはXcodeプロジェクトを参照する設定を行わなければならない
  • MapViewを組み込む際にはInterface Builderを用いるよりもソースコードから直接Viewを作る方が楽

前回に引き続きMap Viewが使いたいということで、今回はいよいよ実際にroute-meを利用して自分のアプリ上でMap Viewを表示してみたいと思います。
参考にしたのは以下のページ。
EmbeddingGuidev2
http://code.google.com/p/route-me/wiki/EmbeddingGuidev2

○まずは何はなくともプロジェクトをダウンロードする
一週間に何度も更新が入るようなプロジェクトですので、常に最新のソースを利用したい方は、パッケージを落としてくるよりSubversionを利用してチェックアウトするのがおすすめです。
ということでコマンドプロンプトから以下のコマンドを実行。
$ svn checkout http://route-me.googlecode.com/svn/trunk/Proj4
$ svn checkout http://route-me.googlecode.com/svn/trunk/MapView

パッケージからダウンロードする場合でも、Subversionからチェックアウトする場合でも重要なことは、このProj4というプロジェクトディレクトリと、MapViewというプロジェクトディレクトリを同じディレクトリに配置する必要があるということです。例えばこんな感じです。
$ ls -l
total 0
drwxr-xr-x 12 akisute staff 408 12 25 22:06 MapView/
drwxr-xr-x 168 akisute staff 5712 12 25 22:04 Proj4/
drwxr-xr-x 13 akisute staff 442 12 25 22:07 ThisisMyProject/

これはMapViewがProj4プロジェクトを参照しているからのようです。
自分のプロジェクトについては何処に置いても大丈夫ですが、同じ場所においておいた方が後からプロジェクトの参照がしやすくていいかもしれません。

ちなみに、私たちユーザーが使うのはMapViewプロジェクトだけです。Proj4は内部的に利用されるだけですので、私たちが直接呼び出すことはありません。

○プロジェクトの参照を自分のプロジェクトに追加する
次は配置したMapViewプロジェクトへの参照を自分のプロジェクトに追加します。


プロジェクトに追加を選択して、先ほど配置したMapViewプロジェクトの中の、MapView.xcodeprojを選択します。
このとき、プロジェクトのコピーは作成してはいけません。「ディスティネーショングループのフォルダに項目をコピーする」というチェックボックスを選択しないようにしてください。


追加が成功するとこんな感じになります。
"Xcode Project Management Guide"の37ページ目(Referencing Other Projects)というところに書いてある方法なんだそうです。

・ビルドターゲットにファイルを追加
libMapView.aというファイルの横にある小さなチェックボックスをチェックします。


こんな風に。MapView.appはチェックしなくてよいです。
これでlibMapView.aがビルドターゲットに追加されます。

・ビルドターゲットの設定
ビルドターゲットの設定を開きます。プロジェクトメニューから選択するか、ターゲットを直接ダブルクリックします。


では直接依存関係というところに、先ほどのMapViewを追加します。+ボタンを押して追加してください。


このMapViewというのを追加します。
それから、リンク済みライブラリというやつをいくつか追加する必要があります。
QuartzCore.frameworkと、
libsqlite3.dylibというライブラリを追加してください。
原文では4つ追加するように指示されていますが、この2つだけで問題ないようです。

・ヘッダ検索パスを変更する
引き続きターゲットの設定ウィンドウから、「ビルド」タブを選択し、
「ヘッダ検索パス」という項目を探します。


このように、MapViewプロジェクトへのパスを設定します。
ここでは早退パスで指定していますが絶対パスでも問題ないと思います。
再起的チェックボックスを忘れずに。

・プロパティタブの識別子を設定
プロパティタブの中の、識別子という項目を設定します。
これはMapViewに限らずiPhoneアプリを実機にインストールする際に必ず必要になる設定なのですが、一応忘れずにということで。

・ここらで一度ビルドしてみる
Command + Bを押下して一度ビルドしてみます。ここまでの設定が間違いなければビルドに成功します。
「警告がいくつか出るかもしれないが無視してくれ」と原文には書いてましたけど、私の場合には警告は一つも出ませんでした。ラッキーです。


ビルドにどうしても成功しない場合には、こちらの画像を参考にしてみてください。ライブラリがきちんと追加されていなかったりしませんか?

・リソースの追加
MapViewプロジェクトの中にある画像ファイルを、
自分のプロジェクトにコピーしてきます。そうしないとマーカーの画像がでないんだとか。


こんな風に加えてみました。別に自分のプロジェクトの中なら何処に加えておいても問題ないとは思います。小さなチェックボックスをチェックして、ビルドターゲットに追加するのも忘れずに。

○いよいよマップを自分のプロジェクトに配置
マップを自分のプロジェクトに配置する方法には、
1:
2:
この二つがあります。1の場合は、Interface Builderを起動して、Viewを配置し、Viewのクラス名をRMMapViewに変更すれば基本的にはOKですが、Interface Builder上で追加しただけでは動かない(gccがコンパイル時にリファレンスを削除してしまうらしいです)のでちょっとしたハックをコード上で行う必要があります。
ViewControllerに以下のようなコードを追加してください。
- (void)viewDidLoad {
[super viewDidLoad];
[RMMapView class]; //この行がハック
}
これでInterface Builderから追加したRMMapViewが動作します。

2の場合は、以下のようなコードを書きます。


画像ですみません。

○そしていよいよアプリケーションを実行


無事に出ました!
(地図の一番下に空白があるように見えるのは私の設定ミスで、普通に先ほどまでの記述に従って作ればきちんと全面が地図になると思います)
デフォルトの設定だと、オーストラリアのキャンベラ近郊のど田舎が最大倍率で表示されるようです。

○次回予告
一応地図は出ましたが、このままでは役に立たないので、
次回は初期表示位置の設定、初期倍率の設定、スクロール範囲の制御(日本の外は見れなくする)、マーカーの配置、クリックイベントの取得のやり方などを調べてみようと思います。

2008年12月27日土曜日

iPhoneでMap Viewを使いたいので、ライブラリを探してみました

  • iPhone Google Maps Component
  • route-me
  • TouchMap
  • 個人的にはroute-meがおすすめ、ただしMicrosoft Virtual Earthを使うことになる
  • ストリートビューが欲しい、またはどうしてもGoogle Mapで実装したいという人はAppleがCocoa Touchに組み込んでくれるのを期待しつつ待つしかない

iPhoneで開発をしている人なら、誰しも一度はこう思うでしょう。
「標準のGoogle Mapアプリみたいに、地図を使ったアプリが作りたい」と!
GPS・加速度センサー・タッチ操作に強力な通信機能、おまけに3D描画もできると、
こんなにすてきな機能と地図がくみ合わさったら、その可能性は無限大に違いありません。

が。しかし、なんということでしょう。
皆様ご存知の通り、Cocoa Touch Frameworkに地図機能は存在しないのです!
ライバルのAndroidにはあんなにすてきな地図機能があって、自由に使えるというのに!

さてさて前置きが長くなってしまいましたが、要するに、

「iPhoneでMap Viewが使いたいんだけどどうすりゃいいの」

ということです。
調べてみたところ、以下の3つのオープンソースライブラリが見つかりました。

○iPhone Google Maps Component
http://code.google.com/p/iphone-google-maps-component/
その名の通り、Google Mapを利用したMap Viewライブラリです。
実装にはiPhoneのUIWebViewを利用しており、JavaScriptを用いてGoogle Mapにアクセスし、
描画を行っているようです。
画面上に描画点(プロットとか画像とか)を自由に配置することができます。

○route-me
http://code.google.com/p/route-me/
こちらはObjective-Cネイティブ実装のMap Viewライブラリです。
ネイティブ実装であるため、きわめて軽快で高速な動作が特徴です。
ただし地図の提供元がOpenStreetMapか、Microsoft Virtual Earthに限られてしまいます。

○TouchMap
http://toxicsoftware.com/touchmap/
全く未知数です。
Objective-CまたはC言語による実装で、
地図の提供元がMicrosoft Virtual Earthであるということ以外何もわかりません。

いずれもGoogle Map StreetViewには対応していません。

さて、この中のどれを選ぼうか・・・というところですが
まずiPhone Google Maps Componentは真っ先に除外されます。
JavaScriptで実装されているため実機では重すぎて使い物になりません。唯一のGoogle Mapsを用いた実装だけに非常に残念です。
残る二つからroute-meをとるか、それともTouchMapをとるか悩みましたが、
TouchMapのほうがドキュメントが少なく、また開発頻度が悪い(10月から一度も更新されていない)ため、
現在最も勢いのありそうなroute-meを今回採用することにしました。


次回はroute-meのページに用意されているインストールの手引きを見ながら、
実際に自分のアプリの中で地図を動かしてみようと思います。
http://code.google.com/p/route-me/wiki/EmbeddingGuidev2

2008年12月25日木曜日

CS193P Cocoa Programming - AssignmentPresence3まで完了


最近ご無沙汰気味でしたが、
なんとかPresenceアプリ作成の宿題3まで完了させることができました。

リロードボタンと投稿ボタンがつきました。



詳細画面を見るとこんな感じです。ちょっと寂しい。




投稿ボタンを押すとこうなります。
UITextFieldではなくてUITextViewを使った方がよかった気がします。まぁいいや。


ソースコードなどは以下に公開しております。
http://github.com/akisute/akisute_cs193p/tree/master


残るはPresence4だけなのですが、Presence4の内容は正直それほど難しくない(TabBarを使うぐらい)ので、
ここらですっ飛ばして今興味があるroute-meという地図ライブラリの解析でもやってみようと思います。

2008年12月21日日曜日

Cocoa(iPhone)で、日本語を含むURLを開く方法

  • 基本的にはCore FoundationのC関数を利用する
    CFURLCreateStringByAddingPercentEscapes()
  • ただし、一部問題のあるケースがある
    URL中に&を含む場合などは正しく作れないので別の方法が必要

プログラマをやっていると、だんだんと日本語が嫌いになってきます。
いや、嫌いというのはおかしいのですが、とにかく英語以外の言語はトラブルが多いです。
コンピューターというのはつくづく英語を処理するためだけに作られているのだと思います。
(だからこそ、プログラムの「国際化」で飯を食える人がいる訳ですけど!)

すみません、前置きが愚痴っぽくなってしまいました。
iPhoneプログラミングにおいてももちろん、日本語を使う際に罠があります。
たとえばNSURLRequestなどがそうです。
日本語の含まれるURLを開こうとすると、うまく解釈してくれないんです。
(といいますか日本語が含まれるURLはURLエンコードしなくちゃいけないんです。・・・当たり前ですね)

例えばこんな感じです:
http://s3.amazonaws.com/twitter_production/profile_images/65140989/るーみゃ_normal.jpeg


自分の画像だけ出ないぞ!

そこでURLエンコーディングを行う関数を探してみたところ、ありました。
Cocoaで日本語のGETを飛ばそう!

早速真似してやってみました。
CFURLCreateStringByAddingPercentEscapes()関数を使うのがポイントらしいです。
            NSString *originalUrl = [userDict objectForKey:@"profile_image_url"];
           NSString *encodedUrl = (NSString *) CFURLCreateStringByAddingPercentEscapes
           (NULL, (CFStringRef) originalUrl, NULL, NULL, kCFStringEncodingUTF8);
           person.profile_image_url = encodedUrl;

たったのこれだけでうまくいきました!



ところが、有頂天になってTwitter上で騒いでたら、
@psychsさんから突っ込みが。
JavaScriptでいうencodeURI相当なんで、
日本を適当にエスケープしたいときにはいいんだけど、
たとえばパラメタに「&」を入れたい場合とかは、
ちゃんとencodeURIComponent 相当のやつを使って組み立てなきゃだめだよ

なんと具体的なアドバイス!(ありがとうございます!)ちょっと調べてみました。
javascript: escape(), encodeURI(), encodeURIComponent() 比較

細かいところはわかりませんが、とにかく一部の文字がエンコーディングされないみたいなのです。
深刻なのは?とか=とかでしょうか。気をつけないとトラブルに巻き込まれそうですね。

ちなみにiPhoneでencodeURIComponent相当のエンコーディングを行うためにはどうすればよいのかですが、
リファレンスを引いてみても特にそれらしきものは見当たりませんでした。
ひょっとしたら自分でやるしかないかもしれません。

■2008/12/29 22:00追記
Twitter上で、@norio_nomuraさんにencodeURIComponent相当のエンコードを行う方法を教えていただきました!ありがとうございました!
http://twitter.com/norio_nomura/status/1083641557
CFURLCreateStringByAddingPercentEscapes(NULL, string, NULL, CFSTR (";,/?:@&=+$#"), kCFStringEncodingUTF8);

最大のポイントは、第4引数のCFSTRです。ここで指定した文字列はエンコードされずにそのまま残るようです。
第4引数をNULLのまま使うと標準のencodeURL相当になり、
この例のように指定すると、encodeURLComponent相当のエンコードが可能になります!

iPhoneアプリをローカライズ(国際化対応)してみた

  • まずはNSLocalizedString()を利用してアプリを作る
  • 次にgenstringsコマンドを利用してLocalizable.stringファイルを自動生成する
  • Xcodeに取り込む
  • Xcodeからローカライズの設定を行う
  • 最後にかっこよく翻訳を行う
  • info.plistファイルの中身をローカライズしたい(たとえばアプリ名など)ときは、
  • InfoPlist.stringsファイルを手で作って、ローカライズ設定をして、翻訳すると良い
  • Localizable.stringは基本UTF-16に統一しておくこと
  • InfoPlist.stringsはUTF-16でないと動かない。Localizable.stringsはUTF-8でいいらしい(未確認)

iPhoneアプリを作ったならば、
「一人でも多くの人に使ってもらいたい」
「1本でも多く売りたい」
というのが心情というものです。
日本ではヒットしないアプリが海外では大ブレイクということも考えられますし、
iPhoneアプリの市場は日本国内よりも海外のほうが圧倒的に大きいです。
従って、iPhoneでアプリを作るなら、国際化対応は必須であるといえましょう!

ということで、今日はそんな大事な大事な国際化のやり方を勉強してみました。
Cocoa Frameworkは元々非常に国際化がしやすい作りになっていますので、
iPhoneアプリでもその仕組みの恩恵を受けることが出来ます!




国際化の第1歩はアプリ作成からです。まずはアプリを作らないと始まりません。
このとき、普段は画面に表示する文字列を
self.navigationItem.title = @"Title";

こんな感じで指定していると思いますが、
これをNSLocalizedString()関数を利用して
self.navigationItem.title = NSLocalizedString(@"Title",
@"Title message for the main view");

こんな風に書き直します。
第1引数はメッセージキー(普通は英語の文字列をそのまま使えばいいと思います)、
第2引数はコメントです。コメントは面倒でしたらnilでも大丈夫ですが、あると翻訳時に大変便利です。


アプリ側の準備が出来たら、次は翻訳文字列ファイル(Localizable.strings)を生成します。
手で作っても良いのですが、genstringsという大変便利なコマンドが最初から用意されているので、
こちらを利用するのが良いと思います。
使い方はこんな感じです。
genstrings [オプション] ファイル名

たとえば
genstrings *.m

コレを実行すると、カレントディレクトリにあるすべての拡張子がmのファイルから
自動的にLocalizable.stringsをつくってカレントディレクトリに保存してくれます。
生成されたLocalizable.stringsはUTF-16でエンコードされています。
(BOMのありなし、およびエンディアンはリトルかビッグかについては不明ですが、
とにかくUTF-16としてMac, Xcode, およびiPhoneが正しく認識してくれるのは間違いないです)

これだけだとプロジェクト内のすべてのソースをまとめてファイルにしてくれないので、
実際の開発時にはfindコマンドを併用して、以下のように実行するのがおすすめです。
genstrings -a $(find . -name "*.m") 

このコマンド入力によって!
カレントディレクトリ以下のすべてのの拡張子がmのファイルを対象にしてLocalizable.stringsを生成してくれる
ソースコードが更新されていた場合、以前に作成したLocalizable.stringsの内容に新しく追加された文字列が追加される
(-aオプションがないとファイルは上書き保存されてしまいます)


Localizable.stringsが生成されたら、Xcodeに取り込みます。
単純にファイルをXcodeプロジェクトに対してドラッグ&ドロップすれば大丈夫です。
このとき、必ず文字コードを「UTF-16」にしてください。
取り込み時にUTF-16にするのを忘れていたら、あとからファイルのエンコードをUTF-16にする必要があります。


取り込んだら、ローカライズの設定を行います。


まずは対象のファイルを選択して右クリック、「情報を見る」ウィンドウを開きます。
「ファイルをローカライズ可能にする」をクリック。


再度、「情報を見る」ウィンドウを開き直し、
「ローカリゼーションの追加」をクリック。プルダウンからJapaneseを選択。
ドイツ語とかフランス語もプルダウンから簡単に選択できます。
その他の言語の場合は自分で調べる必要がありそうです。


成功するとこんな感じになります。

解除するときは「情報を見る」ウィンドウの一般タブから、
「すべてのローカライズを取り除く」をクリックするだけです。

以上、とても簡単です。
ただしこのローカライズの設定時に文字コードの指定がUTF-16ではなくなることがあるので、
もう一度設定したファイルの文字コードをチェックしておくと安心です。


これで準備は出来たので、あとはLocalizable.stringsを翻訳するだけです。
翻訳したらビルドしてiPhoneにインストールすれば完成!


Localizable.stringsでは翻訳できない箇所、たとえばアプリ名を翻訳するときなど、
Info.plistの内容を翻訳したいときは、InfoPlist.stringsと言うファイルをXcode上で作って、
ローカライズ設定を行い、翻訳してビルドすればうまくいくと思います。


Xibファイルについてもローカライズ設定が可能なようなのですが、
Xib自体を翻訳するより、ソースコード中からラベルやタイトル名を変更するようにして、
ソースコード自体をLocalizable.stringsで翻訳するようにしたほうが変更に強くて楽かと思います。




トラブルシューティング
以上の内容に従っても翻訳されない!というときは、
まずはLocalizable.stringsが本当にUTF-16になっているか確認してみましょう。
InfoPlist.stringsはUTF-16でないと動きません。
Localizable.stringsについてはUTF-8でも動くそうなのですが、私の場合駄目だったので、
やはりUTF-16をオススメしておきます。
ただし、いずれの場合でも、UTF-8とUTF-16が混在すると確実に動きません。

それでも駄目な場合は、Finderでプロジェクトのディレクトリを開き、
buildディレクトリの中身を丸ごと消してから再度ビルドするとうまくいくと思います。
(以前のビルド結果が残って居るとうまくいかないみたいです)


それでは素敵な翻訳ライフを!

2008年12月20日土曜日

NSURLConnection使用中にbad server certificationエラーが出たときの対処法

  • 1203, NSURLErrorDomain, bad server certificate
  • このエラーはSSL使用時に証明書の内容が不正なときに発生する
  • 要するに自己認証証明書(オレオレ証明書)警告
  • 標準APIにはこのオレオレ証明書警告をオフにする機能がない
  • NSURLRequestのallowsAnyHTTPSCertificateForHost:をオーバーライドすることで回避可能

皆さんも、自宅に自分用のサーバーをお持ちだったり、会社のサーバーに業務でアクセスしたりということがあると思います。
こういうちょっとしたサーバーでは、たいていの場合正式な認証局が発行したものではない、自己認証による証明書、
いわゆる「オレオレ証明書」によるSSH認証が行われています。

便利だし、正式な認証局に頼むとお金が必要になったりでついついやってしまいますよね。
ブラウザからアクセスすると警告が出ますが、無視してしまえばなんてことはありません。

ところが。
iPhoneのアプリからこうした「オレオレ証明書」を利用しているサーバーに対して、
NSURLConnectクラスを用いてアクセスしようとすると
1203, NSURLErrorDomain, bad server certificate

というエラー(NSErrorのインスタンス)が発生し、処理が中断されてしまいます。
Appleさんちょっと厳しいですって。
(たとえば、Pythonでliburlを利用してアクセスしたときはたとえオレオレ証明書でも一切怒られません)
しかも困ったことに、このエラーを回避する方法が標準APIに用意されていません。
対策はただひとつ、「オレオレ証明書なんて使うな、Verisignにお金払え」ということらしいです。

オレオレ証明書なんて許さないぞという決意は大変良く分かりますが、
自宅のサーバーならともかく、会社のサーバーでは自分が勝手に証明書取るわけにもいかず。
困りました。

そこで先人たちがNSURLRequestクラスのprivateなメソッドを利用する回避手段を編み出してくださいました。
http://lists.apple.com/archives/Macnetworkprog/2006/Nov/msg00020.html
http://www.phapper.com/Default.aspx?g=posts&m=8


この方法に従って、NSURLConnectionクラス(およびNSURLRequestクラス)を利用する箇所で、以下のようなクラスカテゴリ実装を行います。
@implementation NSURLRequest(NSHTTPURLRequest)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
 return YES; // Or whatever logic
}
@end

これで全てのオレオレ証明書の認証を回避することが出来ます。
もし特定のホストのみを回避したいのであれば、以下のように適当なロジックを組んでやればいいと思います。
@implementation NSURLRequest(NSHTTPURLRequest)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host
{
 return [@"ba-tyan.oreore.com" isEqual:host];
}
@end

iPhoneではAdHocによって100人までは自由にアプリを配布することが出来ますので、
App Storeを利用しない、自分専用・内輪向けのハックアプリなんかをこうして作っても面白いですね。
でも、せっかくiPhoneで開発するならApp Storeに挑戦しなければ損!もちろん、そのうち挑戦しますよ?

2008年12月18日木曜日

ようやくiPhoneアプリのパッケージ構成/クラス設計のコツがわかってきました

  • 良いクラス構造を学ぶためには、複数の偉い人のソースを読むのが一番良い

オブジェクト指向言語に限らず、どのようなプログラムを組むにしても、
クラス分割やモジュールの分割は再利用性・保守性を高める上で重要な点だと思います。
もちろん、iPhoneのアプリ開発だってそうです。

Objective-CでCocoa Touchフレームワークを使い始めておよそ1ヶ月、
ようやくこのフレームワークの作法やクラス分割のコツなどがつかめてきました。
まだまだ間違っているところが多数あるような気がしますが、現段階での自分のクラス分割法を晒してみます。

まずはグループ(Javaで言うパッケージ)の分け方から。
私はこんな感じで分けることにしました。ほとんど偉い人のパクりです。
  • Classes
    • Libraries
      各種ライブラリを配置する
      • JSON
      • ImageStore
      • その他自分が作ったアプリのロジックもここに置く
    • Controllers
      View Controllerの類を配置する
      • UIApplicationDelegate
      • UIViewController
    • Views
      カスタムビューを配置する
      • UITableViewCellのサブクラス
      • UIViewのサブクラス

悩ましいのはカスタムビューのライブラリを利用するときなのですが、
一応、Librariesに追加しようと考えています。

自分の書いたロジックはLogicsのようなグループを作成してLibrariesと明確に分けたほうが良いかもしれませんが、
後々自分の書いたロジックも可能な限り使いまわせるようにしたいので、Librariesに入れるようにしています。



クラスの種類を大別すると以下のような感じに。

●View
ビュー。画面。そのまんまですね。

●Controller
UIApplicationDelegateとUIViewControllerがここに属します。データ受け取って画面を書き換えるのがお仕事です。

●Logic
以前はControllerとDataSourceを直接つないでいたのですが、どうしてもうまくいかないので、ControllerとDataSourceの間に1つ層を設けるようにしました。
こいつのお仕事はDataSourceから非同期に受け取ったデータを貯蓄しておき、Controllerにデータを取得するためのインターフェースを提供することです。
データを貯蓄しておけるので、必要なときだけDataSourceからデータを取ってくることができます。
ん?ここまで書いていて思ったけど、LogicというよりLightWeightパターンやProxyパターンに近い?もっといい名前を付けてあげようかな。

●DataSource (Data Access Object, DAO)
実際にデータを取ってくるクラス。HTTPアクセスやらファイルアクセス、SQLiteへのアクセスなどいろいろ手段はありますけど、
いずれの場合でも必ず非同期でデータを取ってくるというところがキモだと思います。

クラス間の結合方法ですが、ControllerとLogic、LogicとDataSourceの間は、Delegateおよびプロパティを使って結んでいます。
Controller -> Logic, Logic -> DataSourceの呼び出しはプロパティからたどってアクセス。
DataSourceは非同期処理を行うので、DataSourceからの返り値はDelegateをたどってControllerまで伝播される仕組みです。
この仕組みは本当に良くできていると思います。非同期処理なのにとても簡単。

現在の課題としては、出来る限り複数のControllerから同一のLogicに対するアクセスを避けるようにしたいのですが、
そのための具体的な方法が良く思いつきません。
Tab barとか作って大規模なアプリになってくると難しそうですねー・・・
delegateだけではなくてCocoaに標準で備わっている通知機能も使っていく必要があるかもしれないと思ってます。

2008年12月17日水曜日

CS193P 11日目 非同期処理をやってみる

  • 非同期処理を行う方法はいくつかある
  • URLフェッチ処理ならば、NSURLConnectionクラスをつかっておけば一発
  • さらに簡単にURLフェッチ処理を行いたいのであればこのライブラリをおすすめ
  • URLフェッチ以外の処理を行うならば、NSThreadを使うか、NSOperationとNSOperationQueueを併用する
  • NSThreadは従来どおり、本当にスレッド処理を記述する必要があるため非常に大変
  • 対するNSOperationはインスタンスをつくってキューにぶち込んだら後は勝手にやってくれる、楽
  • UIViewやUIViewControllerに対する処理(要するに画面に対する処理)は、必ずメインスレッドから呼び出す必要がある
  • スレッドセーフではないため
  • 要するに[object performSelectorOnMainThread:withObject:waitUntilDone:modes:]を使えば解決する

いきなり日付が飛んで11日目です。
このあたりからは課題1つにつき3日分ぐらいのの講義内容が含まれていて、難易度がどんどん高くなってきました。
母さん、おいらスタンフォード大学の学生にはなれそうもないよ。



さて、今回の内容は非同期処理です。
現在の課題ではTwitterのタイムラインをJSON形式で取得して表示を行っているのですが、
メインスレッド(プログラムのメインループが走っているスレッド)の上から直接URLに対してHTTPアクセスを行っているため、
処理が返ってくるまでメインスレッドがブロックされ、結果フリーズしたように見えるという問題がありました。
これを非同期処理にしてブロックしないようにしましょうね、と言うのが今回の課題の内容。

NSURLConnectionと言うクラスを使えばURLのフェッチを自動的に非同期で行ってくれるのですが、
ご丁寧に「NSThreadかNSOperationで処理してね」とご忠告が。
Threadはどうにも使いこなせる気がしないので、ここはより簡単なNSOperationを使おうと思います。
(ゲームなどではおそらくNSThreadを使うことになるんだと思いますが)


NSOperationというクラスを継承して、
mainメソッドをオーバーライドして処理を記述し、
NSOperationQueueに追加すると自動的にThreadを裏で立ち上げて並列処理を行ってくれます。
処理が完了したらKVOという機能を使ってNSOperationから通知を受け取るらしいです。
しかしこのKVOと言う概念がイマイチ理解できないので後回しにして、
より簡単なNSInvocationOperationというクラスを使うことにしました。

使い方はこんな感じです。
NSInvocationOperation *op = [[NSInvocationOperation alloc]
     initWithTarget:self
     selector:@selector(reloadPerson:)
     object:person];
 [self.operationQueue addOperation:op];
 [op release];

これだけで自動的に並列処理をしてくれるんだから凄いと思います。
ということで、今回の課題では以下のように並列構成をしてみました。

スレッド1:メインスレッド
スレッド2:TwitterからTimelineを取得するためのスレッド(NSInvocationOperation + NSOperationQueue)
スレッド3:画像を取得するための並列処理(ImageStoreを利用、内部的にはNSURLConnection)

ところがこれがうまくいきません。
1と2だけを並列処理させたときはうまくいき、1と3だけのときもうまくいくのですが、
1と2と3と並列で動かすとエラーになります。
ああもう!だから並列処理なんて嫌いだ!

デバッガで調査してみるとSocketの取得のあたり?でとまっている感じがしたので、
スレッド2かスレッド3がソケットを捕まえてロックしているのではないかと考え、
使ったらすぐreleaseするようにソースを変えてみたのですが、効果なし。

Google先生にご相談したところ、それらしい回答が。
【iPhone】スレッド中で[UITableView reloadData]を使ってはいけない
なるほど!自分のソースを見直すと、確かにスレッド2の処理の中でUITableViewに対してreloadDataを呼び出しています。
さっそくご指摘のあったとおりにソースを書き直してみました。
if ([delegate respondsToSelector:@selector(mPersonDataSourceDidFinishLoadOfPerson:)])
{
 [delegate performSelectorOnMainThread:@selector(mPersonDataSourceDidFinishLoadOfPerson:) withObject:person waitUntilDone:YES];
}

今度は一発で成功!

2008年12月14日日曜日

第2回 Google App Engine Hackathon

  • 今回で第2回目
  • 主催は@tmatsuoさん
  • 4チーム(内部でさらに分かれるため合計6チーム)に分かれて作業をし、最後に発表する形式
  • 1回目よりも運営が格段にスムーズで素晴らしい時間を過ごせた
  • GDataの認証はすさまじく難しい。またGDataアクセス用のライブラリがあるが、これまた非常に機能が多く難しい
  • Google App Engine Oilは凄く良い、余計なことをしないし必要な者は全部作ってくれる
  • gaeogenはバージョン0.21の地点ではバグがあるためまともに機能しない
  • DropBoxを使ったグループ開発はすさまじいスピード感がある

http://groups.google.co.jp/group/google-app-engine-japan?hl=ja
ちょっと遅くなりましたが、2008年12月13日に開催されました第2回 Google App Engine Hackathonについてご報告です。

主催は前回に引き続きGoogleの認定エキスパートである@tmatsuoさん。
前回は好き勝手にGAEで物を作るだけという感じだったのですが、今回は趣向を変えて、
  • チュートリアル:RSS Reader作成
  • チュートリアル:Twitterもどき作成
  • GDataを用いたマッシュアップ:Bloggerなどとマッシュアップする組
  • GDataを用いたマッシュアップ:Picasaなどとマッシュアップする組
  • フレームワーク
  • データモデル
以上の6組に分かれてそれぞれ活動し、結果を最後に発表すると言う形式で行われました。
目標がはっきりしていたおかげで、最後にはものすごいアプリが次々飛び出しました。大成功だったと思います!
運営の皆さんありがとうございました!
前回に引き続きお弁当おいしかったです =)


詳細につきましてはHiiroさんのブログをご参照ください。
Hiiro_memo: GAEハッカソン参加感想&まとめ:Google凄いが周辺人も凄い


私はマッシュアップ班に参加いたしました。
ちなみに隣の席が世界に名だたるおっぱいプログラマー@technohippyさんでした。びっくりです。
Da変態な人かと思っていたら、意外と普通の人でした。Vistaに泣かされていましたけど。

今回のマッシュアップ班の開発の際には、DropBoxに全員のプロジェクトファイルを配置して、いつでもみんなのファイルを参照できるようにして作業しました。
コレが凄くいいです。誰かが一人難所をクリアできれば、すぐにみんながそのソースを参照して動かせるように出来たため、作業効率が格段に良かったです。
(共有する人はコミット作業すら必要ありません。全部自動的に同期してくれるから)

さっそくDropBoxが大のお気に入りになりました。Windows機にはあまり導入したくなかったのですが、
こんなに便利なら使うしかないですね。


また、噂のGoogle App Engine Oil(GAEO)も試してみました。
gaeo testproject

とすると、testprojectを作成してくれるのですが、コレが素晴らしい!
最初から絶対に必要になるjs, img, cssフォルダを作成してくれたり、faviconの設定もしてくれたり、かゆいところに手が届く感じです。
RequestHandlerの実装も非常に簡単になっていて、
class WelcomeHandler:
  def index(self):
      #get All models from DataStore
      models = AModel.all().fetch()
      #set models to self object to use it when rendering the template
      self.models = models

たったのこれだけで、テンプレートのレンダリングまで全部自動でやってくれます。

本当はgaeogenというrailsのscaffoldのような機能があるのですが、こちらは0.21の段階ではバグがあるようでまともに機能しませんでした。
(ソースを参照したところ、argvに対してgetメソッドを呼び出したりしていました。
Pythonのargvはlist型だからdict型にしか使えないgetメソッドは利用できないです)
でもgaeoだけでも十分すぎるほど便利ですから導入する価値は有りだと思います。

2008年12月6日土曜日

CS193P Cocoa Programming - ソースコードをgithubに公開いたしました

gitの練習もかねてここまでの成果ソースコードを公開してみました。
http://github.com/akisute/akisute_cs193p/tree/master

特に見所はありませんが、一応自分の勉強用として。

githubに自分のリポジトリを作ってコミットしてみる

  • gitとは分散リポジトリ、分散リポジトリについてはこちらのページを参照
  • 要するにローカル上のリポジトリ+github上のマスターリポジトリを用意してくれる感じらしい
  • 手元のリポジトリにコミットするのがcommit、リモート(他人の)リポジトリにコミットするのがpush
  • githubへのpushには公開鍵を利用したSSHによる認証が必要
  • 自分のマシンで公開鍵と秘密鍵を作成し、公開鍵をgithubに追加。秘密鍵をssh-agentとやらを使ってローカルマシンにインストールする

私の周りのTwitter界隈でgithubなる良く分からないシロモノが大流行しているので、私も便乗してみることにしました。
バージョン管理システムの経験はCVSとSubversionのみ、しかも両方ともEclipseから使ったことがあるだけでコマンド操作なんて全く分からないど素人ですが、なに、使ってみれば分かるさ!

まずはgitをMacBook Airにインストール。MacPortを使えば一発です。
sudo port install git-core

これだけなのですが、依存関係がひっじょーーーに多いのでインストールに1時間ぐらい待たされました。

次、githubに自分のアカウントを作ります。これは説明不要。

次、githubに自分用の楽しい楽しいリポジトリを作ります。これも説明不要。分かりやすいです。


できました。名前はakisute_cs193p。そのまんま。

次、画面に表示された念仏を、そのまま何も考えずに実行します。
git config --global user.name "akisute"
git config --global user.email "this_is_my_boomstick@gmail.com"
cd ~/Documents/Xcode/
mkdir akisute_cs193p
cd akisute_cs193p
git init
touch README
git add README
git commit -m 'first commit'
git push origin master

と、最後のコマンドを入力したところでなにやら訳の分からないエラーが出て停止。困りました。
とりあえず何をやっているのかを少しずつ把握していくことにします。

まず最初の2行でコンフィグ。名前とメアドを決めてね、ということらしいので、適当に入力。
次、自分のXcodeプロジェクトがある場所に移動して、リポジトリ用のディレクトリを作って、
git initコマンドを実行してその場にリポジトリを作りました。その場に、と言うのがポイント。
これでgithub上とローカル上に2個の同じリポジトリが完成したことになります。
touchコマンドでREADMEファイルを作成。
READMEファイルがあると、github上でREADMEファイルの中身が表示されるみたいです。
Licenseとか表示するのに便利そうですね。
次のgit add READMEで作ったファイルをgit様の管理下におきました。
git addはファイルを管理下に置くときだけではなくて、コミットの前にも実行する必要がある?みたいです。
git add .(ピリオドを忘れずに)とするとカレントディレクトリ以下の全ての存在をgit様の管理下に置くのだハハハハーらしいです。便利ですね。
変更追加を管理下においたら、git commit -m 'first commit'でコミット。
どうやら-mは引数でコメントを入力するオプションみたいです。
-mオプションを指定しなければ、自動的にvimが立ち上がってコメントを求められます。
コメントなしのコミットは出来ません。

最後、問題のgit push origin master。一体全体何をしているのか分からなかったので、まず調査してみました。
http://github.com/guides/git-cheat-sheet
ここでgit pushの例を見てみると、リモートリポジトリoriginに対して、ブランチ名masterをコミットしているらしいです。
要するにここでは、github上に一番最初に用意したリポジトリに対して、既存のブランチmasterをコミットするということかな。
どうして自動的にgithubに対してpushしてくれるのかはわかりませんが、きっと最初の設定がそうなっているんでしょう。

では分かったところで、よく分からない理由でgit push出来ない問題を解決しましょう。まずは適当に検索・・・
git/github - TOBY SOFT wiki
ヽ( ・∀・)ノくまくまー(2008-06-02)
すると偉大な先人たちが既に答えを導いておられました。素晴らしい!
要するに、git pushの際にSSHの認証をしたいんだけど、
秘密鍵と公開鍵がないから認証できないよ、ということらしいです。

まずは鍵を作ります。以下、@ITからコピペ&一部改変。
@IT:sshでパスワードなしにログインするには
ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/akisute/.ssh/id_rsa): ←[Enter]キー
Enter passphrase (empty for no passphrase): ←パスワードを入力
Enter same passphrase again: ←同じパスワードを入力
Your identification has been saved in /Users/akisute/.ssh/id_rsa.
Your public key has been saved in /Users/akisute/.ssh/id_rsa.pub.

できました。
次、githubに戻って、accountページから公開鍵を登録します。
less ~/.ssh/id_rsa.pub

この結果を何も考えずコピーして以下の画面に貼り付ける。


できました。改行とか入っていないかだけは注意。改行したらダメらしいです。

@ITの例では、やれchmodしろだの鍵を作ったらどうのこうのしろだのとか書いてますが、無視。
要するにサーバー側に公開鍵を置いて、クライアント側に秘密鍵を置いておけばいいようです。
今回はgithubがサーバー様なので、githubに公開鍵を渡せばいいってことですね。

次。以下のコピペ呪文を詠唱します。何も考えずに力を抜いて楽にして。
eval `ssh-agent`
Agent pid 12345
ssh-add ~/.ssh/id_rsa
Enter passphrase for /home/.ssh/id_rsa: ←パスワードを入力
Bad passphrase, try again for /home/.ssh/id_rsa: ←同じパスワードを入力
Identity added: /home/.ssh/id_rsa (/home/.ssh/id_rsa)

できた。凡人凡妖怪の私には何が起こったのかすらわかりません。
注意点はただひとつ、`ssh-agent`の`は'じゃなくて`です。Shift+@。

このコマンド入力によってgithubへの認証が可能になる!らしいです。早速試してみます。
git push origin master

おお!今度は成功!
調子に乗っていろいろ追加してみます。
git add Presense
git commit -m 'first commit'
git push origin master

これで俺もgitマスターだぜ!と調子に乗っていたら、


出ました、Mac OS Xの恥部(と勝手に自分が思っている)、.DS_Storeです。
これはリポジトリに含めたくないですね。どうすればいいんでしょう?
答えは簡単、.gitignoreというファイルを作って、お守り代わりにリポジトリのルートに配置すればいいらしいです。
build
.DS_Store
*.o
*.ob
*.pbxuser
*.tmproj
*.model*
*.mode*
*.build

このお守りを置けば、git add時にここに書いてあるパターンにマッチするファイルはaddされないらしいのですが、
すでにaddされてしまったものについては自分で消すしかありません。
git rm .DS_Store
git commit -m 'removed DS_Store'
git push origin master

まぁ、ざっとこんなもんよ、なんてね。

CVSやSubversionと比べると、ソースコードを公開するのが非常に楽でいい感じです。
ネットワーク環境が無くてもローカルのリポジトリに対してコミットできると言うのも地味に嬉しいところ。
過去の変更履歴を見たりブランチを切ったりするコマンドについては、おいおい学んでいこうと思います。

2008年11月30日日曜日

DEMOsa Vol.5に参加してきました

DEMOsaについてはこちら
http://www.mosa.gr.jp/?p=2168

デザイナー系の人が自分のやった新しくて面白いことをライトニングトークみたいに発表する会でした。
スケジュールを見てもわかる通り、iPhoneネタが非常に多かったです。

最初の半分を遅刻して見逃してしまいましたが、以下感想を箇条書き。

■ポメラ
  • ノートPCが持ち込めない場所(会社とか会社とか会社)では大活躍できそうだ。言われてやっとその価値に気づいた。
  • でもポメラからPCにデータが移せない(USBメモリは全面禁止が当たり前なので)ので、結局意味がない気がしてきた。
  • 実際に触らせていただいたが、小さい。初代DSにそっくり。キーボードはいまいち。EeePCのどうしようもないキーボードに比べれば遥かによいが、MacBook Airのキーボードに慣れてしまうとやはりMacBook Airに軍配が上がる。

■Cookie
  • 絵文字だけでコミュニケーションする子供向けSNSのようなもの。
  • 実装はともかくとして、絵文字だけでコミュニケーションという発想は最高。これでイスラエル人ともヘブライ語を覚えずしてコミュニケーションできる訳だ。絶対にこれはiPhoneで実現するべき。

■iKotoの中の人
  • 日本文化に関するアプリばっかり作る理由は、文化を創るか。かっこいい。
  • 漫画とアニメ以外の文化は創らないと伝わらない。

■Afrous
  • ブラウザだけでマッシュアップが作れるというアプリ。らしい。
  • RSSもAPIもないただのWebページのDOMを解析してデータを抜き出して、youtubeやamazonと連携させていた。凄い!
  • たとえば日経新聞のトップページを切り出して強制的にRSS化することもできそうだ。

■RainbowNoteの中の人
  • 1万アプリものアプリケーションがあふれ変えるiTunes Storeの壁は高い、キーになるのはプロモーション
  • Moopf's App Store Review Scrapper itunesレビュー評価を見る
  • Mybefia 競合アプリとの人気の差を見る
  • Mobclix 国別の評価を見る

■BiND
  • 10分でWebページを作成していた。Webページをブロック単位に分割して、ブロックを追加したり編集したり。
  • 本当なら6時間かかる作業を10分でやったんだって!本当かどうかは信じがたいが、確かにページの編集速度は速かったしCSSをいっさい触ってなかった。
  • ロゴ画像の編集は本当に便利そうだった。

■MemoryTreeの中の人(宮田さん)
  • 空間にPostitみたいに画像を貼付ける!セカイカメラみたいだなぁ。もっともセカイカメラと違ってこちらは本当に実現されている(できることが限られているけど)。
  • iPhoneを振ってメモリーを投げ、メモリーを受け取るというアイディアがすばらしい!後から懇親会でお聞きしたところ一人の天才プログラマーとセットで1週間不眠不休でやって作ったそうな。
  • Motion Comics Playerなんて動画を電子書籍としてリリースしているけどほとんど売れてないらしい。それでもそこそこのApp Storeで上位だから電子書籍はよほど売れていないんだろうとのこと。
  • 以下、後から懇親会でお話ししたときのこと・・・
  • 貫禄がある人だった。ちょっと怖い。でもいい人。
  • 1万アプリのうちほとんどは有象無象、いいものを作ってプロモーションをきちんとやればいくらでも上位に入れるよ、とのこと。
  • ケータイなんかに比べてiPhoneを購入する層の嗜好は偏っているとのこと。いわゆる新しいものとかっこいいものが好きな人たち。
  • 実はこの宮田さん、世界で一番最初に着メロを作って大もうけした人なんだとか。なんてこった・・・
  • で、あまりにも着メロが大成功だったのでモバイルからは遠ざかっていたが、iPhoneでモバイルに復帰したそうな。それほどの可能性か!iPhone
■Niconの新製品は化け物か
  • http://www.nikon.co.jp/main/jpn/whatsnew/2008/1007_up_01.htm
  • 第一印象:どう見てもスカウターです
  • 第二印象:やりやがったNicon、やりやがった
  • 実際に体験しましたが、ヘッドフォン部分のフィット具合は凄くいいです。画質も凄い、目の前に画像イメージがわいてくる感じです。サイズは640*480らしいですが実際はもっと小さく見えます。
  • ディスプレイ位置の調整がかなり大変です。あと振動にも弱いです(大きくぶれて見えなくなるので、歩きながらはつらいと思います)。
  • しかしこれにGPSとアプリケーション作成プラットフォームをつけてiPhoneみたいにしたら面白そう
  • そうしたら私は絶対にライトセイバーアプリならぬスカウターアプリを作る。「戦闘力、たったの5か・・・ゴミめ・・・」

■ノルウェーの留学生の方とお話
  • 外人さんは未だにちょっと苦手かも
  • 彼曰く、留学している日本人とそうでない日本人は話す内容が全然違うらしい
  • 留学していない日本人は、日本の否定的なところを言われたときに鵜呑みにしてしまうが、留学している日本人は嫌な顔をする(そうではないという)らしい。う・・・

CS193P Cocoa Programming - 7日目 UINavigationViewControllerを使う

  • UIViewControllerのawakeFromNibは動作しないときがある。UIViewControllerの初期化には 必ずviewDidLoadを使うこと
  • UINavigationViewControllerはxibファイルに含めずに、ソースコード中で初期化したり管理する方がうまくいく
  • UINavigationViewControllerの上に表示されるバーを編集したいとき(ボタンを追加したりするとき)は、UINavigationViewControllerにpushされるUIViewControllerのnavigationItemプロパティを編集する。Interface Builderから操作できるかどうかはわからない
  • UIAlertViewがポップアップ表示、UIActionSheetが下からにょきっと出てくるボタンのリストを表示
  • ボタンにソースコード中からアクションを追加することが可能(UIButton, UIBarButtonなどのドキュメントを参照のこと)


さて、ナビゲーションがついてようやくiPhoneアプリらしくなってきました。
画面はナビゲーション時にUIViewControllerのインスタンスがどのように管理されているかをログに吐いてみたところです。viewDidDisappearの後にdeallocが毎回流されているのがわかります。
要するに、一つのviewを毎回毎回alloc->initしないでインスタンスを再利用してやろうと思うときはUINavigtionViewControllerにpushするだけではなくて、どこか別のところでUIViewControllerのインスタンスを保持しておく必要があるようです。2tchの作者さん曰くalloc->initは相当重い動作らしいので、できるかぎり使い回しができるようにしたいです。



ポップアップも出るようになりました。


※いい加減開発ペースと学習ペースをあげようと思っているので、しばらくの間はブログの更新がこんな感じで適当になりそうです。

2008年11月26日水曜日

CS193P Cocoa Programming - 6日目おまけ、多角形をくるくる回せるようにしてみた

  • CS193P(http://www.stanford.edu/class/cs193p/cgi-bin/index.php)のチュートリアルで作っているHelloPolyプロジェクトを自分なりにアレンジしてみた
  • 自分なりにアレンジしてみた=ニコニコ動画だと駄作フラグ
  • UIViewはhiddenプロパティをYESにした瞬間に消えてしまうので、アニメーションでフェードアウトさせたいときは、まずアニメーションだけ実行>アニメーション終了時のデリゲータ(- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag)で実際にUIViewをhiddenにする
  • タッチ動作を取得するには、UIView(正確にはUIResponder)のtouchBeganメソッドやtouchMovedメソッドをオーバーライドする
  • デフォルトではマルチタッチ不可能(最初の1タッチのみ感知する)、マルチタッチしたければ設定を変えること
  • UITouch.tapCountでタップした数を取得できる。これを使ってダブルタップを検出できるが、連打すると3とか4とか2より大きい数が取得されてしまうので注意
  • CGPointはクラスではなくて構造体、頭にCGがつくCore Graphics系はすべて純粋C言語であるところに注意!
  • CGPointなどを作成するときはCGPointMake()関数を使う
  • CGPointやCGRectなどをObjective-Cのクラスとして扱いたいときはNSValueクラスを使う、たとえば[NSValue valueWithCGPoint:(CGPoint)point]など
  • CGRectGetMidX()関数とか地味に超便利
  • C言語の変数の有効範囲について:http://www.cis1.c.dendai.ac.jp/c_master/C_14.htm
    CGPointMake()で作ったCGPointなどは自動変数なので、別のメソッドに渡すときはポインタ渡しではなくてそのまま値で渡す

ただ単にカリキュラムにそって進めていくだけでは面白くないので、この辺りでちょっとチャレンジングなことをしてみることにしました


まずは新しくサブビューを追加。


ON OFFスイッチでビューを出したり消したり。出したり消したりするときはアニメーションします。


UISliderを使って、線の太さを変えてみましたよ。
UISliderのvalueプロパティはdouble型なので注意です。ずっとNSString型だと思ってました。
それから、タップした向きに多角形を回転できるようにしました。赤線は中心からタップした点への線分です。
タップしてドラッグするとスムー(?)ズに回転しますよ。


破線への切り替えもできるようにしました。
UISegmentedControlを使っています。このUISegmentedControl、取得できる値が選択されているセグメントのインデックス番号(selectedSegmentIndex)だけなのでちょっと厄介です。HTMLのラジオボタンみたいに好きな値をセグメントごとに持たせられればいいのに。


今回作成したアプリのプロジェクトファイルを公開してみました。
面白いことをやっている点は何一つないのですが、まぁ一応。
http://sites.google.com/site/akisutesama/files/HelloPoly-06b.zip?attredirects=0

今後はgithubとかで公開できるようにします。

2008年11月22日土曜日

CS193P - ここまでの進捗をアップしてみた

ここまでのCS193Pの進捗をアップしてみました。

http://sites.google.com/site/akisutesama/files/HelloPoly-06.zip?attredirects=0

Xcode3.1用のプロジェクトファイルと、ソース一式が入っています。
Max OS Xで解凍してXcodeで読み込んでコンパイルできると思います。たいした内容ではありませんが一応。

CS193P Cocoa Programming - 6日目、NSUserDefaultsの使い方

  • アプリケーションが終了した後もデータを保持したい(例:現在開いているページなど)
  • NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  • あとはNSDictionaryとほとんど同じ(格納の仕方も取得の仕方も)
  • BOOLとかintをそのまま格納できるメソッドもあります
  • 格納するタイミングは、値がかわった瞬間かまたはアプリが終了する瞬間
  • 取得するタイミングは、アプリが立ち上がった瞬間かまたはawakeFromNibメソッド
今日はNSUserDefaultsとやらの使い方を学習。
NSUserDefaultsとはアプリケーションが終了した後もデータを保持することができる仕組みのようです。
本格的にデータを保存したいならSQLiteやファイルを使うんでしょうが、ちょっとしたデータ(例えば現在開いているページ)を保持したいときなどにSQLを書くのは煩雑です。そういうときに便利に使える仕組みらしいです。

○値を格納する
タイミングは二つ。値がかわった瞬間か・・・
//ボタン押し下げ時に値を減らす
- (IBAction)decrease {
int currentNum = polygonShape.numberOfSides;
currentNum--;
polygonShape.numberOfSides = currentNum;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:currentNum forKey:@"numberOfSides"];
[self updateInterface];
}
またはアプリケーションが終了するタイミングか。
//@implementation HelloPolyAppDelegate(アプリケーションのデリゲートの中で)
- (void)applicationWillTerminate:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:controller.model.intValue forKey:@"numberOfSides"];
}
パフォーマンスに応じてどちらか好きな方を選んでくださいとのことです。

○値を取得する
こちらもタイミングは二つ。アプリケーションが立ち上がったタイミングか・・・
//@implementation HelloPolyAppDelegate(アプリケーションのデリゲートの中で)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int storedNumberOfSides = [defaults integerForKey:@"numberOfSides"];
controller.model.intValue = storedNumberOfSides;
[window makeKeyAndVisible];
}

または対象のクラスのawakeFromNibメソッドの中か。
//ここではコントローラクラスの中で・・・
- (void)awakeFromNib {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int storedNumberOfSides = [defaults integerForKey:@"numberOfSides"];
self.model.intValue = storedNumberOfSides;
[self updateInterface];
}

この二つの違いは正直わかってません。複数のNibファイルを利用するときはawakeFromNibでないと駄目らしいですが・・・

CS193P Cocoa Programming - 5日目、いよいよView自作

  • Interface Builderを使ったらとにかくWrite Class Files...を押すのを忘れないように
  • 描画はJavaのSwingなどとほぼ同じ
  • drawRectメソッドをオーバーライドして描画する
ViewとかAnimationとか、いよいよ実践が近くなってきたようです。
だんだんと難易度も上がってきました。



まずは自作Viewを作成。
Interface BuilderからUIViewを引っ張ってきて画面上に配置。
クラス名を適当に(ここではPolygonView)に変更。
ControllerクラスやらModelクラスなど、必要に応じてOutletを追加します。
最後に(一番忘れやすいんだけれども)メニューからFile > Write Class Files... この書き出しを行わないとXcode上に実際のソースが出てきません。

書き出しを行ったらXcodeを開いて、以下の2つの仕事を行います。
・先ほど追加したPolygonViewのスーパークラスを定義する
・ControllerにIBOutlet PolygonView *polygonViewを追加する

新しいオブジェクトはWrite Class Filesで一発だと思うけれども、既存のクラス(例えば前回実装したController)なんかは、書き出ししちゃうと上書きしてしまいそう。怖いので今回はXcodeから書くことにしました。でもこれ非常に非効率的。hファイルだけ上書きしてほしいんですけど・・・



勇気を振り絞って上書きWrite Class Files...にチャレンジしたら、なんだかこんなFileMergeとかいうアプリが立ち上がって、きれいにマージすることができましたとさ。よかったよかった。


では早速Viewに描画処理を追加します。
描画を行うのはUIViewの-(void)drawRect:(CGRect)rectメソッドですので、こいつをオーバーライド。
あとは再描画したいタイミングでUIViewのsetNeedsDisplayをコールすれば適切なタイミングでシステムが再描画してくれるというしくみ。Swingに似てますね。



実際に描画してみました。簡単簡単!!



描画のやり方さえわかってしまえばこっちのものです。
ポリゴンの点の位置を計算するメソッドは例題の中で用意されていたので、それを丸コピーして点の位置に線を引くだけ。線の太さを調節する関数はCGContextSetLineWidthというのが見つかったのでそれを使うだけ。リファレンスが使いやすい!すてき!



カスタムビューの中にUILabelを追加して、ラベルにポリゴンの名称を表示できるようにしてみました。UILabel.textプロパティを書き換えるタイミングはカスタムビューのdrawRectの中で。ほかに良いタイミングが見当たらず。


ひとまずこんなところですかね。次は・・・
  • スライダーで線の太さを変える
  • 線の種類を何らかのスイッチで変える
  • ビューの上でフリックしたらポリゴンがくるくる回るようにする(アニメーション処理の勉強が必要)
これらを試してみたいですね。

iPhone OS 2.2にアップグレードに失敗、ずいぶんと酷い目にあいました

  • iPhone OS 2.2へのアップグレード時に途中でエラーが出て中断、そのままインストールが再開できず
  • ケーブルを接続し直したり再起動したりHomeボタン押しっぱなしでセーフモード起動してもiTunesが認識せず
  • 結局Macbook Airにつないでみたら一発でリカバリモードで認識してくれたので助かった
  • ということでiPhoneで本当に困ったときはMacにつなげ、Windowsは信用ならない
  • いつのまにやらiPhone上のi.softbank.jpメールアドレスの設定が非常に簡単になっている(メアドとパスを入れるだけですべてやってくれる)

iPhone OS 2.2が事前予想通りにリリースされたので、さっそくアップデートを試みました。
が・・・途中で原因不明のエラーが出てアップグレードに失敗してしまいました。
そのままiTunesが何度やってもiPhoneを認識しないと言う事態に陥り、あわや愛しのiPhoneが6万円の格好いい板になりはてるかと気が気ではありませんでした。
結局Macにつないでみると無事認識してくれたので、そのままiTunesから復元を試みて無事復活。やれやれです。
基本設定が全部吹っ飛んでしまいましたが、アプリのデータなどはすべてWebサービス上にあるため全く無傷でした。おかげさまでiPhoneが無事立ち上がってからは1時間もかからずにすべてのデータを復元できました。クラウドコンピューティングの強力さを改めて実感です。


メールアカウントを設定し直す際に気づいたのですが、いつのまにかi.softbank.jpメールアドレスの設定が極めて簡単になっています。
以前は設定時にSSL通信を使わないようにしたり、IMAPとSMTPサーバー名を自分で設定する必要があったのですが、今日試してみたらメールアドレスとパスワードを入れるだけで全部自動でやってくれました。

それからiPhone 2.2といえばなんと言っても顔文字。コレで日本語キーボードがQWERTY、10キー、そして顔文字と3つになってしまいました。どれだけ日本語入力に力を入れてるんだって感じです。


メールの設定が楽になったり、顔文字が使えたりワンセグを見れるようにしたりと精力的に対応してくださって本当に助かるのですが、
それでもやっぱり、アップデートを行っただけで二度と起動しなくなってMacにつながない限り復元できなくなるようでは、とてもじゃないですが普通の人にiPhoneをお勧めは出来ないですね-。
だってこんなのが100万台も日本で売れたら、きっとソフトバンクモバイルのカスタマーサポートの中の人がストレスと過労でみんな死んでしまいますよ?

2008年11月17日月曜日

CS193P Cocoa Programming - 現在4日目

  • メモリの管理についてお勉強
  • allocで領域確保(オーバーライドしない)
  • initで初期化(オーバーライドして使う)
  • 同一オブジェクトを参照するときはretain(オーバーライドしない)
  • コピーするときはcopy(オーバーライドしない、copyWithZoneをオーバーライドするべき)
  • 解放するときはrelease(オーバーライドしない)
  • 実際にメモリから削除されるのはdealloc(オーバーライドして使う)
  • NSAutoReleasePoolとautoreleaseメソッド・・・事前にautoreleaseしておいたオブジェクトは[NSAutoReleasePool releaseまたはdrain]呼び出し時に一斉に解放される

1日に二日分前進。iPhone発売から既に4ヶ月経過、既に無数の超優良アプリがわんさかと出回っています。果たして私は遅れを取り戻せるでしょうか。


これはNSURLから文字列を取得する方法を模索していたときです。
結局、absoluteStringかrelativeStringが正解ということがわかりました。


クラスの作成とメモリ管理。ようやくCらしくなってきたかな?
Objective-Cではガベージコレクションもできるらしいのですが、iPhone開発ではガベージコレクタをぶん回せるだけのリソースはないため昔ながらのメモリ管理でやる必要があります。



いろんなイニシャライザをためしてみました。
リストにオブジェクトを突っ込んだときは、突っ込んでいるNSArray自身もリリースしないと、個々のオブジェクトがリリースされません。おそらく内部でretainされてるんでしょうね。



introspection(Javaでいうところのrefrection)にチャレンジ。instanceof演算子みたいなのはなくて、すべてNSObjectのメソッドとして提供されています。java.lang.refrectionパッケージみたいな面倒さはないです。あとセレクタはSEL型とかいう専用の型があって便利。いわゆる関数へのポインタとして使えます。Javaの何が不満ってこの関数ポインタがないところですよ。



続けてもう一つの宿題のほう(Assignment2B)に突入。いよいよ実際に動作するiPhoneアプリを作ることになるのですが、その前に一つ宿題にバグ?があったのでご報告。
上の画像のカーソルで選択している部分(PolygonShape.h)、Assignment2Aで作成したときにはCocoa/Cocoa.hをインポートしていますが、2BではCocoaが使えない(iPhoneアプリになるため)ので、Foundation/Foundation.hに書き換える必要がありました。



で、これが完成品です。
SwingやWin32API開発並みの苦行を覚悟していたのですが、あまりに簡単で逆に拍子抜け。はまりそうな点といえば、プロジェクトに新しくクラスをインポートしたり作成した後はInterface Builderからアプリケーションに登録しなければならないというところぐらいでしょうか。あと、awakeFromNibはコントローラに実装しないとだめ(間違えてPolygonShapeに一生懸命実装して、ビルド直前にこの過ちに気づきました)。


今のところは楽勝。問題はOpen GL ESに手を出すあたりからでしょうかね。

2008年11月16日日曜日

User-Agentを偽装して、YahooBB! BBモバイルポイントにタダのりを試みた

  • 実験場所:都内某所のマクドナルド
  • iPhoneからの接続:良好。速度も申し分なし。ただし電波強度がいまいち。
  • MacBookからの接続:失敗。User-Agentを偽装しても、iPhoneエミュレータからアクセスしてもだめ。UAを正しくiPhone2.1に設定していなかったのが原因と思われる。再度調査する
  • 電波の発信場所はおそらくバックオフィス内、バックオフィスに近い場所の方が電波的に有利(場所によっては外にもアンテナがある?)
  • どうでもいいけどマックは煙たくて騒々しい、そのかわり安くて人がたくさんいて面白い
  • スクリーンショットとってくるの忘れたorz

遅ればせながら手持ちのiPhoneで実験してみました。駅構内とかでも使えるのかと思っていたら想像以上にBBモバイルポイントのアクセスポイントが少なく、結局マック以外まともな場所がないということがわかってちょっと残念。

○まずは店内の電波強度を試験してみる
WiFiFoFumというアプリを利用して店内の電波強度を測ってみました。
結果、店のカウンターの手前が最も強力(RSSI = 40程度)で、2階の禁煙席付近はRSSI = 20前後でした。常にWifiアンテナ表示が2本程度という感じです。


○iPhoneからつなげてみる

電波が弱いのでちょっと心配でしたが無事接続できました。速度も相当快適です。YouTubeの動画でも問題なく視聴できるはずです。


○禁断の果実(?)MacBook Airからつなげてみる
User-Agentによって接続デバイスを判定しているという噂があったので、噂を信じてSafariのユーザーエージェントをMobile Safari 1.1.3に変更していざMacBookから接続!

・・・だめでした。XcodeからiPhoneエミュレータを起動して試してもやっぱりだめ。くそう、そんなに甘くなかったか・・・

と思ったら、どうやら私の試した設定が悪かったみたいです!
Windows版SafariのUA追加方法 | iPhone 3G Wiki blog
これはWindowsでの設定方法ですけれども、要するに開発メニューから選択できるUAはiPhone OS 2.1ではなくて1.1.3(昔のiPhone)だったためうまくいかなかったと。
Mac版のSafariでは、以下のファイルの中身を適当に書き換えると、「開発」メニューからiPhone OS 2.1のUAを選択できるようになるみたいです。
/Applications/Safari.app/Contents/Resources/UserAgents.plist
に以下のUser-Agent文字列を見よう見まねで追加する:
Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_1 like Mac OS X; ja-jp) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F136 Safari/525.20
これでまた来週試して結果をご報告したいと思います。


○結論
もしこれでただ乗りに成功できれば、しばらくはマックが主な勉強場所になりそうです。

2008年11月10日月曜日

知っている人がメディアに露出すると有頂天になる小者=自分


何気なく@ITのフィードを眺めていたら、なんだかどこかで見た名前が・・・って!こないだのPython温泉にいたニコニコ大百科の中の人!!
うらやましいと思う反面、なんだか凄い悔しいです。自分もいつか日の目が当たる人間になれる時が来るんでしょうかね?
とにかく、勉強会にはえり好みせずどんどん参加することにしました。このカレンダーさえあれば勉強会の日程は分かるんだから、参加しなきゃ損ですよね。

2008年11月9日日曜日

CS193P Cocoa Programming - 1日目の宿題Bをやってみた

  • Objective-Cは、見た目とは裏腹に非常に簡単
  • Xcodeのリファレンスはきわめて優秀
  • Xcodeのコード補完はControl + , またはOption + Escだが使いづらいのでCommand + Spaceに変更する
  • Xcodeのコンソール表示(NSLogの内容を確認)はShift + Command + R
引き続き宿題B。こっちの宿題はiPhoneから完全に離れてObjective-Cの基礎基本について学ぶようになっています。
それにしてもこの宿題、問題の出し方がうまいです。全く調べないで解けるほど優しくないですが、何処を調べればよいかはきちんと示されています。リファレンスの引き方と自分で調べる力が養えるのがうれしいです(あたりまえなんですけど・・・自分の大学では調べてもさっぱりわからないか、調べ方がそもそもわからないか、調べる必要がまったくないか、というような宿題が多かったので)。

閑話休題。Objective-Cは何一つわかりませんが、とにかく見よう見まねでコードを書いてみます。


おおー動いた!最初このコンソールの出し方がわからず困りました。Shift + Control + Rでした。


調子に乗ってさらに数行記述。リファレンスを引いてNSString同士の結合およびフォーマットのやり方を調べてみました。+で結合できないのが少々面倒(確かC++だとできたはず)。


なれてきたらもう簡単。
それにしてもメソッド名がいい感じに長いです。私はこういうやたら説明的で長いメソッド名が大好きです。


NSArrayのループもこの通り。Javaっぽい。
あとNSDictionaryも調べてみましたけど、Javaと同レベル・・・これはいまいち・・・
JavaScriptやPythonみたいなスクリプト言語らしい使い勝手のいいDictと比べる方が悪いですけどね。


Xcodeの設定。これでコード補完をCommand + Spaceに割り振ることができます。



同じ項目がもう一カ所ありましたが、こちらは変えなくても特に問題ないらしいです。メニューから呼び出しているかそれともテキストエディタの中で直接呼び出しているかの違いのようです。

それでは引き続き残りの宿題をやってしまいますか!

CS193P Cocoa Programming - 1日目の宿題Aをやってみた

  • http://www.stanford.edu/class/cs193p/cgi-bin/index.php
  • これはわかりやすい
  • 宿題があるのが実にうれしい
  • Interface Builderで配置したクラスの属性(位置とか表示するイメージとか)を操作するときは、Command + Shift + I またはCommand + 1から4
  • Labelの文字の大きさとフォントは変えられたが色とか太字はなぜかうまくいかず
  • vertical centerな配置やholizonal centerな配置を行うには、メニューのLayout > Align
今日からスタンフォード大学の学生になった気分でさくさくっと勉強。
手始めに1日目の宿題から。・・・こんなに宿題を嬉々としながらこなす私の姿を学生時代の自分に見せてやりたいものだ。
一つ目の宿題は、コーディングはいっさいなく、interface builderからCocoa Touchが用意しているviewやclassを配置して画面をデザインしてビルドして走らせるだけというもの。


こんな感じでinterface builderにパーツを配置して・・・


できた!初日の宿題だけあって実に簡単。

こういったグラフィカルなGUI作成機能は別に目新しいものではない(Microsoft Visual Studio 2005などでもできる)が、これまで使ったことがあるツールよりは使いやすかった気がする。まぁVS2005はマイクロソフト語で書かれているので読めない=論外だし、Eclipse GEFはそもそもSwing自体があまりよいフレームワークでなかったため使いづらかった。
なによりiPhoneの場合はデバイスの画面サイズが決まっているから絶対座標指定がしやすい。

2008年11月8日土曜日

iPhone開発をこれから始めるときに参考にする資料

  • スタンフォード大学の授業が一番わかりやすい
  • iPhone Dev Centerのビデオは全体像をつかむために使える
  • 良い書籍がないか探索中
  • Core Animation, Core Audio, Quartz(Core Graphics), OpenGL ESあたりの使い方に詳しい資料が欲しい
つい最近までAndroidの開発に浮気していましたが、とあることがきっかけで、iPhoneの開発に本格的に戻ってくることにしました。
ところが私自身、Mac OS Xでの開発もやったことがないし、XCodeも使ったことがないので、そういう人向けの初めてのiPhoneアプリ開発用資料を集めてみました。


CS193P - Cocoa Programming | Announcements
スタンフォード大学のCocoa Programmingの授業。英語が読めるなら断然これがオススメですね。タダだし。本当にタダでいいんですかこれ?

iPhone Dev Center
Appleの総本山。ここのビデオをiPhoneに突っ込んで通勤中に見るのがオススメです。詳しいところは全く分かりませんが、全体像をつかむには適しています。


それから書籍。正直iPhone開発向けの本はこれ!と言うものがまだ見つかっておらず、Objective-Cの本のみを調査中。

詳解 Objective-C 2.0詳解 Objective-C 2.0
荻原 剛志

by G-Tools

4000円以上しますが、一番評価が高かったのがこれ。


あとはiPhoneのメディア系(アニメーション、画像描画、オーディオなど)の使い方の詳しい解説資料が欲しいのです。こちらは目下捜索中。使うのはHello Worldレベルから脱却してから。