iphoneアプリ開発日記その5
RSS リーダーを作る
突然 RSS リーダーが作りたくなった。
参考になりそうなページがあったので、とりあえず見てみる。
【コラム】実践! iPhoneアプリ開発 (5) RSSリーダの作り方 (1) - RSSフィードをダウンロードする | 開発・SE | マイナビニュース
【iPhone開発】RSSを読み込む方法 | STUDIO BEATNIX
おお、動く動く
…がいまいち挙動がわからん。
コード見てもさっぱりさっぱり。
こういうときは、問題を切り分けて個別にやっつけていこう。
RSS リーダーの処理を考えると、こんな感じになるか
それに加えて、「ダウンロード → パース」を並列化している。
なのでその辺も理解する必要がある。
xml パーサーの動きをみる
まずは xml パーサから。
選択肢はこの5つ
名前 | 解析タイプ | 備考 |
---|---|---|
NSXMLParser | SAX | ios標準 |
libxml2 | SAX | ios標準、c言語API |
GDataXML | DOM | google製 |
KissXML | DOM | |
TBXML | DOM |
性能評価はこのへんHow To Choose the Best XML Parser for your iPhone Project
そのうち読みたい
以前 pyhton で sax パーサを作った気がするので、今回も sax で。
標準的な NSXMLParer を使うことにしよう。
最低限以下のメソッドを実装すれば動くようだ
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
ひとまずこれでログを吐きまくると、挙動のイメージがつきそうだ。
さて、ここで何のタグを解析すればいいのか、ということに思い当たる。
RSS の xml ってどういう構成になってるのだろうか。
RSS フィードの構成を調べる
RSS のフィード構成がどうなっているのかを調べてみた。
このへんを参考に⬇
RSSのフォーマット・仕様・構造 - RSS1.0、RSS2.0、Content-Type
RSS(RDF Site Summary)によるサイト情報の要約と公開
大きな要素として、channel と item があって、その中にいろいろ情報が入ってる
入ってる情報の中で、使いそうなものはこのへんか
- title
- link
- description
- pubDate
なお、date に関しては、標準の pubDate を使っていたり、拡張モジュールの dc:date を使っていたりする。
そのため、一応両対応しておこう。
xmlを解析してみる
さて、RSS フィードをばらしてみよう
作ったサンプルコード
XMLParser.h
#import <Foundation/Foundation.h> @interface XMLParser : NSObject<NSXMLParserDelegate>{ NSXMLParser* parser; NSMutableDictionary* feedsDict; BOOL isChannel; BOOL isItem; } - (void)parseRSS: (NSURL*) url; @end
XMLParser.m
#import "XMLParser.h" @implementation XMLParser static NSString* CHANNEL_KEY = @"channel"; static NSString* ITEM_KEY = @"item"; - (void)parseRSS: (NSURL*)url{ parser = [NSXMLParser.new initWithContentsOfURL:url]; parser.delegate = self; [parser parse]; } - (void)parserDidStartDocument:(NSXMLParser *)parser{ feedsDict = NSMutableDictionary.new; isChannel = NO; isItem = NO; } - (void)parserDidEndDocument:(NSXMLParser *)parser{ isChannel = NO; isItem = NO; } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ if ([elementName isEqualToString:CHANNEL_KEY]){ NSLog(@"channel %@ start", elementName); isChannel = YES; }else if ([elementName isEqualToString:ITEM_KEY]){ NSLog(@"item %@ start", elementName); isItem = YES; } else{ NSString* log_prefix = @"other"; if(isChannel){ log_prefix = CHANNEL_KEY; }else if (isItem){ log_prefix = ITEM_KEY; } NSLog(@"%@ element: %@ : start", log_prefix, elementName); } } - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ if ([elementName isEqualToString:CHANNEL_KEY]){ NSLog(@"channel %@ end", elementName); isChannel = NO; }else if ([elementName isEqualToString:ITEM_KEY]){ NSLog(@"item %@ end", elementName); isItem = NO; }else{ NSString* log_prefix = @"other"; if(isChannel){ log_prefix = CHANNEL_KEY; }else if (isItem){ log_prefix = ITEM_KEY; } NSLog(@"%@ element: %@ : end", log_prefix, elementName); } } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ NSString* trimmedString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (![trimmedString isEqualToString:@""]) { NSLog(@"found: %@", string); } } - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{ NSLog(@"error: %@", parseError); } @end
結果(channel)
2013-06-06 19:52:16.885 CUILesson[82227:303] other element: rdf:RDF : start 2013-06-06 19:52:16.887 CUILesson[82227:303] channel channel start 2013-06-06 19:52:16.887 CUILesson[82227:303] channel element: title : start 2013-06-06 19:52:16.887 CUILesson[82227:303] found: 4Gamer.net 2013-06-06 19:52:16.888 CUILesson[82227:303] found: − 最新記事 2013-06-06 19:52:16.888 CUILesson[82227:303] channel element: title : end 2013-06-06 19:52:16.888 CUILesson[82227:303] channel element: link : start 2013-06-06 19:52:16.888 CUILesson[82227:303] found: http://www.4gamer.net 2013-06-06 19:52:16.888 CUILesson[82227:303] channel element: link : end 2013-06-06 19:52:16.889 CUILesson[82227:303] channel element: description : start 2013-06-06 19:52:16.889 CUILesson[82227:303] found: 日本最大級の総合ゲームサイトです。コンシューマゲームやオンラインゲーム,iPhone/iPadのゲーム,そしてPC のディープなハードウェア情報まで,日本だけでなく世界からも最新ゲームニュースをお届けします。ほかにも,ユ 2013-06-06 19:52:16.889 CUILesson[82227:303] found: ーザーレビューなどのクチコミ情報も充実。 2013-06-06 19:52:16.889 CUILesson[82227:303] channel element: description : end 2013-06-06 19:52:16.889 CUILesson[82227:303] channel element: dc:language : start 2013-06-06 19:52:16.890 CUILesson[82227:303] found: ja 2013-06-06 19:52:16.890 CUILesson[82227:303] channel element: dc:language : end 2013-06-06 19:52:16.890 CUILesson[82227:303] channel element: dc:rights : start 2013-06-06 19:52:16.890 CUILesson[82227:303] found: Copyright (C) 2000-2013 Aetas Inc. All Rights Reserved. 2013-06-06 19:52:16.890 CUILesson[82227:303] found: 本サ イト「4Gamer.net」の内容は,すべて無断転載を禁止します。ただし商用利用を 除き,リンクについてはその限りではありません。転載,商用利用,二次利用の 希望,ご意見などはwebmaster\@4gamer.netまで。 2013-06-06 19:52:16.890 CUILesson[82227:303] channel element: dc:rights : end 2013-06-06 19:52:16.891 CUILesson[82227:303] channel element: dc:date : start 2013-06-06 19:52:16.891 CUILesson[82227:303] channel element: dc:date : end 2013-06-06 19:52:16.891 CUILesson[82227:303] channel element: dc:publisher : start 2013-06-06 19:52:16.891 CUILesson[82227:303] found: Aetas 2013-06-06 19:52:16.891 CUILesson[82227:303] found: (株) 以下略
実行結果ログ(item)
2013-06-06 19:52:17.022 CUILesson[82227:303] item item start 2013-06-06 19:52:17.023 CUILesson[82227:303] item element: title : start 2013-06-06 19:52:17.023 CUILesson[82227:303] found: 「EVE Online」,探索コンテンツの再活性化を目指した大型アップデート「Odyssey」が実装に 2013-06-06 19:52:17.023 CUILesson[82227:303] item element: title : end 2013-06-06 19:52:17.023 CUILesson[82227:303] item element: link : start 2013-06-06 19:52:17.024 CUILesson[82227:303] found: http://www.4gamer.net/games/004/G000412/20130606085/ 2013-06-06 19:52:17.024 CUILesson[82227:303] item element: link : end 2013-06-06 19:52:17.024 CUILesson[82227:303] item element: dc:date : start 2013-06-06 19:52:17.024 CUILesson[82227:303] found: 2013-06-06T19:48:55+09:00 2013-06-06 19:52:17.024 CUILesson[82227:303] item element: dc:date : end 2013-06-06 19:52:17.024 CUILesson[82227:303] item element: description : start 2013-06-06 19:52:17.025 CUILesson[82227:303] found: CCPGamesがサービスを行うMMORPG\343\200\214EVEOnline」に,19回めとなる大型アップデート「Odyssey」実装されたことが,本日(2013年6月6日)ネクソンから発表された。探索コンテンツ再活性化を目指した今回のアップデート。新機能として 2013-06-06 19:52:17.026 CUILesson[82227:303] found: ,星間に隠された宝物や遺物といったものを発見するスキャナ「センサーオーバーレイ」が導入されるなどしている。 2013-06-06 19:52:17.026 CUILesson[82227:303] item element: description : end 2013-06-06 19:52:17.026 CUILesson[82227:303] item item end 2013-06-06 19:52:17.027 CUILesson[82227:303] item item start 2013-06-06 19:52:17.027 CUILesson[82227:303] item element: title : start 2013-06-06 19:52:17.027 CUILesson[82227:303] found: 「『真・女神転生IV」,DLC第3弾が配信。公式サイトにて詳細が公開 2013-06-06 19:52:17.027 CUILesson[82227:303] item element: title : end 2013-06-06 19:52:17.027 CUILesson[82227:303] item element: link : start 2013-06-06 19:52:17.027 CUILesson[82227:303] found: http://www.4gamer.net/games/166/G016690/20130606086/ 2013-06-06 19:52:17.028 CUILesson[82227:303] item element: link : end 2013-06-06 19:52:17.028 CUILesson[82227:303] item element: dc:date : start 2013-06-06 19:52:17.028 CUILesson[82227:303] found: 2013-06-06T19:39:13+09:00 2013-06-06 19:52:17.030 CUILesson[82227:303] item element: dc:date : end 2013-06-06 19:52:17.030 CUILesson[82227:303] item element: description : start 2013-06-06 19:52:17.030 CUILesson[82227:303] item element: description : end 2013-06-06 19:52:17.030 CUILesson[82227:303] item item end 2013-06-06 19:52:17.030 CUILesson[82227:303] item item start 以下略
ログを見ていくと、パーサーが開始/終了タグを見ているっぽいのがわかる。
この仕組みを利用して、必要なデータを確保することにしよう
以下次号
iphoneアプリ開発日記その4
過去に作ったアプリに関しての備忘録的な何か
発端
iphone4s で使っていた google talk 用のチャットツールが、不安定な動作をするようになった。
もともと多少不満もあったので、ios 開発の練習がてらにアプリを作ってみようかと。
不満としては
- 他のアプリで送受信した履歴を表示する
- ひたすら電話帳をインポートしようとして、起動が遅い
やったこと
とりあえず以下のサイトを参考に xmpp チャットアプリを動かしてみた。
setup/Building a Jabber Client for iOS: Interface Setup
サンプルコードは ARS 対応ではないので修正。
チャットビューから戻るとメッセージが無くなるので、AppDelegate に持たせるように修正。
というか、まず受信した段階でデータを保存しておいて、必要に応じて表示すると。
また、バディリスト表示時にメッセージ受信すると死ぬので、そのへんも修正してみた。
あと アカウント名を表示したりとか。
ただ、メインの目的である履歴の取得方法がわからない。
今後やるとすれば、 アカウントの画像を表示できるようにするとかか。
ejabberd の設定方法がよくわからなかったので、そのへんを調べてみるのもいいかも。
iphoneアプリ開発日記その3
Block とラムダとクロージャの間
ios の Blocks について調べてみた
ひとまず前買ってた本を読んでみる
…ああ、これラムダなのかな?
そういえば、どこかのサイトに Blocks = クロージャ みたいに書いてあった気がする。
クロージャについて調べてみよう
http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3
どうやら、クロージャはラムダ式を使って実現しているとのこと。「ラムダ式 ⊃ Blocks」 って感じか
で、実行時でなく定義時の環境をコピーするんだとか
試してみた
ソース
int captured_value = 10; __block int block_value = 20; // Blocks 用の変数を宣言 void (^external_block)(int); void (^external_declared_block)(int); // 実体を代入 external_block = ^(int loop){ printf("external ~ loop: %d, captured: %d, block: %d\n", loop, captured_value, block_value); block_value ++; }; for (int loop_counter = 0; loop_counter < 3; loop_counter++){ external_block(loop_counter); external_declared_block = ^(int loop){ printf("external declared ~ loop: %d, captured: %d\n", loop, captured_value); }; external_declared_block(loop_counter); void (^internal_block)(int) = ^(int loop){ printf("internal ~ loop: %d, captured: %d, block: %d\n\n", loop, captured_value, block_value); }; internal_block(loop_counter); captured_value ++; } external_block(111); external_declared_block(111); //スコープ外なんで未定義のはず。やっちゃだめ
結果
external ~ loop: 0, captured: 10, block: 20 external declared ~ loop: 0, captured: 10 internal ~ loop: 0, captured: 10, block: 21 external ~ loop: 1, captured: 10, block: 21 external declared ~ loop: 1, captured: 11 internal ~ loop: 1, captured: 11, block: 22 external ~ loop: 2, captured: 10, block: 22 external declared ~ loop: 2, captured: 12 internal ~ loop: 2, captured: 12, block: 23 external ~ loop: 111, captured: 10, block: 23 external declared ~ loop: 111, captured: 12
ということで、宣言時の変数が保持されているのがわかる
__block ってやると、Blocks 内部で変数を弄れるようになる
注意点
Delegate の代わりに Blocks を使うのが流行ってるらしいのだが、
Blocks 内部でメンバ変数にアクセスすると、さっくり循環参照を起こすとか。
対応策はこんな感じのようだ
- 弱参照をつかう
- 解放処理をきっちり書く
いまいち完全に把握できてないので、もう少し調べる必要があるかなー
iphoneアプリ開発日記その2
さくっと現状をまとめてみる。
ゴール設定
設定するゴールは、ひとまずこんな感じ
- ios 関連の職に就く
- apple store にアプリを出す
- なんらかのゲームを作る
で、それと平行して objective-c の学習を行います。
できてること
- クリップボード機能の利用
- pickerView の利用
- 基本的な UI の実装(テーブルとか)
- xmpp を使ったチャットツール作成(google talk 用)
- NSUserDefault を使ったアプリデータ保存
- AppDelegate を自作して差し替え
- バックグランド実行(10分間のやつ)
- cocos2d の基本的な利用
- Delegate 関連の理解
やること予定
- 通知センター関連
- xcode でのテストコードの作り方
- ソフトキーボードを閉じる処理をいろいろ実装してみる
- Blocks の学習
- 課金関連の処理実装
- iAd 実装
- ios でのネットワーク関連処理(ソケットかその上のレイヤー?)
- Unity4でゲーム作成
- cocos 2d でまともなゲーム作成
できればやりたい
- GCD 学習
- UITableViewCell のカスタム
- テーブルのソート機能実装
- NAS 接続する画像ビューア作成
- サムネイル機能をもった動画プレイヤーの作成
- タブ機能を持つRSSリーダー的なアプリ作成
- イベントで動くクリップボード監視機能(今はポーリングなので)
iphoneアプリ開発日記その1
あらすじ
- はりまや は ていきてきに おこる
- チャレンジングな しょうどう に かられ
- アイフォーン へと むかった
- だが そこで まちうけていたのは
- けんとうの おいのり であった
- アピールが たりない と いわれ
- つい かっとなって しまう はりまや
- しかし どうじに
- ひさしぶりに モチベーションが
- あがってくるのを かんじているの だった
- ついでに げーむでも つくるかと
- かるい きもちで かんがえている が
- まだ そのゆくてに まちうけるものを しらない・・・
おかしい
iphoneアプリ開発のはずが、
css3 アニメーションとか jquery ばっかりかいてる…
web系はあんまやってないので、
よい勉強になりました
イメージ→
FF2 op - YouTube