ジンクスって恐い…

OpenCLでいくぜ! (1)

 てなわけで、あんまり間が空きすぎるから「iPhoneアプリ開発、その(212)」の続きを先にやるざます。ちなみにその(209)で実装したセッション処理はサーバーからセッションID(その(202)でtelnet使って確認したやつです)が返されてきて、NSHTTPCookieStorageで自動的に管理されるわけですが、このセッションIDがどんなのか調べたかったらKonohanaDBNet.mのinitメソッドあたりで
-(id)init {
if (self = [super init]) {
NSMutableURLRequest* loginRequest = [POSTRequest
requestLogin:@"http://localhost/konohana/login.php"
name:@"xcc" password:@"xcc"];
NSURLResponse* response;
NSError* error;
(void)[NSURLConnection sendSynchronousRequest:loginRequest
returningResponse:&response error:&error];
NSArray* cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]
cookiesForURL:[response URL]];
NSLog(@"login cookies %@\n", cookies);

}
return self;
}

 なんてやると、コンソールに
2011-01-31 02:05:17.536 konohana_test[27880:207] login cookies (
"<NSHTTPCookie version:0 name:@\"PHPSESSID\" value:@\"hkecglesok7vqtcgcu8e8j2rp6\" expiresDate:@\"(null)\" created:@\"318099918.000000\" sessionOnly:TRUE domain:@\"localhost\" path:@\"/\" secure:FALSE comment:@\"(null)\" commentURL:@\"(null)\" portList:[]>"
)

 て感じでセッションIDが出ます。こんな感じでクッキーとしてサーバーから送られてくるわけですな。
 NSHTTPCookieStorageはサーバーのURLごとにこのセッションIDを管理してて、NSURLConnectionのやり取り時に自動で受け渡し処理をしてくれるわけです。
 便利だわ。

 サンプル実行するとコンソールにlogin cookiesのログが連続して3つ出るんだけど、これは、タブバーのタブ項目ごとにKonohanaDBを作ってるからですね。

$テン*シー*シー-7
3つのタブごとにFirstViewControllerが作られて、KonohanaDBも3つ作られる

 特に支障はないけど、何回もログインを繰り返すのも気持ち悪いし、しかもアプリ終了時にKonohanaDBのdealloc呼ばれねーし…ここらへん設計を工夫すべきでしょうな。
 ま、いいけど。

 こんな感じで、xcc、reborn、2つのアカウントを作って登録や提案、支持をしてみて動作をチェックしてみますた。
 で、最初に見つけたのが…

【不具合1】
 提案が無いのに、空の項目が1つ出てしまう。

$テン*シー*シー-4
こんな感じで、提案が一つもないのに項目が一つ出てくる

 こいつは、konohanaParser.mのkonohanaParserForSuggestionクラスの新しいタグが開いた時の対応メソッドで
if ([className isEqualToString:@"top_suggestion"]
|| [className isEqualToString:@"row_suggestion"]) {
NSMutableDictionary* dic = [NSMutableDictionary alloc]init];
[list addObject:dic];
mode = mode_Row;
}

 と先に空の辞書作って登録してるのが問題なんですな。
 これだと、この後提案タグが見つからなくても、必ず1つ辞書が登録される事になってしまう。
 その空の辞書を提案1件と思って表示しちゃってるわけだ。
 ただ、先に作っておかないと後の処理がめんどくさいわけで…
 ここはそのままにして、結果をもらうKonohanaDBNet.mのconnectForSuggestion:suggestion:forword:メソッド側で対応することにしました。
for (id dic in parser_suggestion.list)

 のループで
if ([dic valueForKey:@"ID"] == nil) {
continue;
}

 ってふうにIDが入っているか調べて、空なら見つからなかったと判断するわけです。konohanaParserはKonohanaDB以外には公開しないので、こういう対応で済まします。
 konohanaParser自体を公開するならHTMLの解析終了時に自分listインスタンス変数を調整すべきでしょう。本来それが正しい。

 あとkonohanaParser.mでは
[NSMutableDictionary alloc]init];

 で辞書作っておいて、release忘れまくってますな。
[NSMutableDictionary dictionary];

 に修正。
 もう一つの不具合として…

【不具合2】
 自分の投稿についた提案に対する支持コメントしかリストされない。

$テン*シー*シー-5

 こいつもkonohanaParser.mの解析時の問題で、konohanaParserForVoteクラスの解析処理で、新しいタグが開いた時の処理が削除ボタンが付いてる時しかコメントを取らないようになってました。これだと自分の投稿以外だとコメントが取れないっすね。
 タグが閉じた時に、ここらへんのチェックするようにします。

$テン*シー*シー-6
無事表示

 でまあ、これで見つけた不具合は対応終わって、いよいよ自分の投稿だけ、自分の提案のある投稿だけを出すタブ側にかかるわけですが

$テン*シー*シー-7

 実装自体はその(204)でPHP側に完了してるわけです。
 あとは、自分のアカウントのIDを送ればいいだけ。ただ、アカウントのIDはPHP側のソースで使ってるだけで、アプリ側には返してないんですな。
 で、まあID受け取るやり取りを追加してもいいんだけど、めんどうなのでPHP側を調整しますた。
 修正自体は簡単で、mainlist.phpの
$aug_author_id = $_REQUEST['sug_author_id'];

 の下に
if ($aug_author_id == 0)
$aug_author_id = $login_id;

 を追加するのと、同じく
$author_id = $_REQUEST['author_id'];

 の下に
if ($author_id == 0)
$author_id = $login_id;

 追加するだけです。
 あとはKonohanaDBNet.mのconnect:forword:メソッドで遥か昔に用意したfilter変数に従い
const char* bindstr = "";
if (inID == -1) {
page = @"";
bindstr = "?";
} else {
if (forword) {
page = [NSString stringWithFormat:@"?top_id=%d", inID];
} else {
page = [NSString stringWithFormat:@"?bottom_id=%d", inID];
}
bindstr = "&";
}
if (filter == ownContributionOnly) {
page = [NSString stringWithFormat:@"%sauthor_id=0", bindstr];
} else if (filter == ownSuggestionOnly) {
page = [NSString stringWithFormat:@"%ssug_author_id=0", bindstr];
}

 ってやってやれば、自分の投稿のみ表示タグ、自分の提案が入っている投稿のみ表示タグがそれぞれ機能するようになるわけっす。

$テン*シー*シー-8

 これで、あとはソースコードに埋め込んでるアカウント名、パスワードを切り替えられるようにするモーダル画面、そしてパスワードを安全に保存するためのKeychain Services APIの利用で、長らく続いた「この花は?」習作アプリはめでたく終了~ちゅうことになるわけですな。

Certificate, Key, and Trust Services Programming Guide

 このまま通信処理部分をNSOperationを使って非同期処理してみたり、再読み込み、追加読み込みGUIをもっと見栄えのいいものにしたり、CoreDataでキャッシュしたりと拡張していくのもいいんだけど、プロジェクトが大きくなりすぎて見通し悪いからね。
 いったん「この花は?」習作はこれまでとして、また小さいアプリを作ってみようと思っとります。
 なのでNSOperationやCoreDataは、印刷とか、Google APIとの連携やDropboxを使ったアプリの習作でやってみようかと思っとります。

 ま、とりあえず次回は、Keychain Services APIだ。
 Keychain Services APIというのは、大事なデータをアプリケーション以外から見れないように暗号化する機能。ここらへんをやらずに素のパスワードをNSUserDefaultsあたりに保存したりしてると、iTunes経由でパスワードをコピーされたりするわけですな。
 他人に自分のiPhoneを勝手にiTunesに繋がれる時点でアウトだとは思うけど、一応アプリ制作者の矜持としてパスワードはKeychain Services使って保存しましょう。
 次回、Keychain Services API!

------------
サンプルプロジェクト:konohana_test26.zip

追記:
 いつのバージョンからかiPhoneシミュレータ、アルバムが空になっちゃってんだよね。

$テン*シー*シー-1

 で、これじゃシミュレータで画像選択できないんですが、ググると自分の画像を登録する方法が何件か表示されました。
 ちょっと、どのサイトだったか覚えてないんで紹介できないんだけど、とりあえず情報に感謝!
 紹介されてたのは以下の方法。

$テン*シー*シー-2

 するとアクションシートが出てくるので、ここで"Save Image"を選択すればアルバムに写真を登録できます。

$テン*シー*シー-3