Study CoreData 22 ~最終章 『やめてはいけない!』~

みなさん、こんばんわ〜!
 

7月10日にスタートして21日目(22エントリ)を数える
『Study CoreData』も今回で最終回となります。

 
 
そんな最終回にあたって、今日はまず初めに
とある書籍の一節を紹介させて頂こうと思います。

 

注意:投稿者自身もCoraDataについて勉強中のため、このシリーズには誤りが含まれている可能性があります。もし、間違いに気付かれた方はコメント欄もしくはtwitterなどでご指摘いただけると幸いです<(_ _)>
また、開発環境はXcode3.2.3 iPhone SDK 4です。実機でのテストなどは自己責任でお願いいたします。

 
 

やめてはいけない!
 
よくあるように、物事がうまくいかなくなったとき、
きみが歩む道がどこまでも上り坂に見えるとき、
資金が減り、借金が増えてしまったとき、
微笑みたいが、ため息しか出ないとき、
心配事で胸が押しつぶされそうなとき、
心配ならば休んでもいい。しかし、やめてはいけない。

人生には紆余曲折があり、妙なところがある。
誰もがときおり経験するように、
多くの失敗者がそうであったように、
がんばっていれば、勝っていたということもあるのだ。

歩みが遅く見えても、あきらめてはいけない、
あともう一歩で、成功するかもしれないのだ。

成功とは失敗の裏返しなのだ。
それは疑いの雲に光る銀の色。
きみが成功にどれだけ近づいているかわからない、
それが遠くにあるように見えても、近くにあるかもしれないのだ。
どんなに強く打たれても、戦いをやめてはいけない。
物事が最悪に見えるときこそ、
やめてはいけない!

“何をそんなに心配しているのかね?”
マーク・ロットン著/沼口敏雄訳

 
 
僕は一年と4ヶ月ほど前にこの一節に出逢いました。
右も左も分からないながらiPhoneアプリの勉強を始め『TapDays』完成を第一の目標に掲げて動き出す、ちょうど一ヶ月ほど前のことでした。
 
それから、自分の環境も変わり
iPadの発売や数多くのOSアップデートなど
走り始めた頃とは状況が大きく変わっていきましたが、ほぼ毎日Xcodeを触っていますし僕の目標は変わっていません。
 
 
そんな中、数多くのアプリを開発し活躍していらっしゃるスタジオルーペさんの以下の記事を読ませて頂き、実に感慨深く、改めて信念に向かって突き進む勇気をもらえました。

飛躍の1年:さよなら工事現場。 – スタジオルーペ開発日記
 
 

そうです!
 
『もうダメかもしれない…』って思った時こそ、
ゴールの一歩先まで来ているのかもしれない!

 
だからこそ、もう一歩進む。
そんな気持ちで今後もやっていきたいって思ってます(^〜^)v

 
 
…と、かなり前置きが長くなりましたが、なにはともあれ最終回です!
気合いを入れて参りましょう!!
 
 

1. 検索を完成させる!

前回のエントリでシンプルな検索機能までは実装できたので、
コレをもう少し便利な検索にバージョンアップすればdotodoの実装はほぼ終了となります!

ってコトで、早速。
前回作った[setSearchResultForSearchString: scopeIndex:]を編集します。

 
では説明しますね。

まず、前回使っていた[CONTAINS]は
左辺に右辺が含まれていればYESを返す
のですが、例えば“ルク 散歩”のように[スペース]を入れて検索すると、
titleに”ルク[スペース]散歩”が含まれていればYES
となってしまうので、この[スペース]の箇所に[スペース]以外の文字が含まれている場合ははじかれてしまいます。
 
 
なので、検索フィールドに[スペース]が入っていた場合には
その前後を別々の検索文字列として認識させなければいけませんね。

そこで[*](ワイルドカード)ってのが使えそうなので使ってみたワケです。
コイツは[文字1*文字列2]とすることで
『[*]には何らかの文字が入るよ』ってすることができるみたい。

そして、その[*]を文字ではなく“ワイルドカード”として認識させるために
[LIKE]を使います。
 
 
そこまで説明したところで[wildString]の生成部を見てもらうと…
 
 
まず、searchStringの頭とお尻に[*]をつけて、
[replaceOccurrencesOfString:…]を使って半角スペースを[*]に変更しています。

コレを使って各scopeでの検索を行っていきます。
 
 
scopeIndex:0(All)の場合は、そのまま全オブジェクトに対して検索してます。

次に(All)以外の場合には、まず各scopeの文字列を取得して[titles]に入れてます。

で、sopeIndex:1~3(@Home, Shopping, works)の場合には先の検索にもうひとつ条件を加えます。
 
Any tags.name IN selectedScopeTitle
つまり、
Any(いづれか)の[tags]の[name]に[selectedScopeTitle]が含まれていればYES
っていう条件です。

このように複数のオブジェクト(NSArrayやNSDictionaryやNSSetなど)を対象にする場合には[IN]を使うと良いようです。
 
 
そして、sopeIndex:4(Other)の場合
タグに@Home, Shopping, worksを含まないTodoを検索させたいので
[NONE]を使ってます。
ちなみに、前述の[IN]は配列 IN 配列のように使うこともできるってコトですね。
 
 
ってなワケで、コレでとりあえずな”AND検索”と”タグでの絞り込み”は出来るようになったはずなので…

せぇ〜のっ。
 
 
ビルダンゴーーっっ!!
 
 

2. 改善したいですが…

こんな感じにうまいこと検索できるようになったでしょうか?

実は、この検索にはまだ問題点があります。

ひとつは、例えば“ルクと散歩”というTodoを検索するために
“散歩 ルク”ってやると検索されない点。

あと”るく”と検索した場合も同様にはじかれてしまう点です。

もちろん、標準のメモやカレンダーアプリでの検索では、いずれの方法でもちゃんと検索されます。
(さらに”太郎のかぁちゃん”は”カー”でもヒットされます。)
ですが、今の僕の知識内で改善しようとしてもムダに複雑な実装になってしまいそうなので…<(_ _;)>
(例えばスペースで区切られた単語ごとにNSArrayに突っ込んで、for文でまわすとか…)
 
 
ただ、この辺りは“正規表現”(Regular Expressions)ってのが絡んできそうだなぁってことはなんとなく。
なので、まずは以下の辺りで勉強しようかと思ってます。
Regular Expressions – ICU User Guide
正規表現の基本 -.NET Tips: C#, VB.NET, Visual Studio
 
…ってコトで、もしこの解決法をご存知の方は是非ご教授くださいませ<(_ _)>
何卒、なにとぞよろしくお願い致しますです。
 
 

3. まだデバッグが…

と、ここまでですべての実装が終わりました!

ですが、確認のためもう一度出来上がったdotodoをビルドして色々試してみましょうね。
 
 
どうでしょう?
実は、このままではマズいところがひとつあるんですが、気付きましたか?

それはDeleteボタンです!
 
 
新規Todoを追加するためにタイトルを入れて詳細ビューに移動した際に”Deleteボタン”が表示されていますね。
コイツをタップするとアプリが落ちます。

ま、当たり前ですね…^^;
だって保存もしていないエンティティを削除しようとしてるワケですから。

なので、このボタンを表示させないようにしなくちゃですね。

そのためにまず、DetailViewController.hで新規タスクの編集かどうか?を判定するフラグを用意します。

 
そして、初期化時にそれを判定しておきます。

『あれ? コレ見たことあるぞ!?』
って気付かれた方もいらっしゃるでしょうね(^〜^;)
 
 
これですね。

 
では、先にこう変更しときましょうか。

 
 
で、肝心の削除ボタンの表示の部分を変更します。

これでオッケーなんですが、ついでに削除ボタンが非表示の時にView全体が真ん中辺りに表示されるようにしてみました。

(※sectionが0以外の場合の処理も忘れてたので追加してます<(_ _;)>)
 
 
そしたら、もう一度ビルドしてみましょ〜。

 
コレでインサート時には削除ボタンは表示されなくなりましたね。
もし、他に(ソートのエラー以外)バグがありましたら、コメントくださるとウレシイです<(_ _)>
 
 

4. オマケ

そして最後にもうひとつ。
このアプリを作りながら、ど〜〜〜ぉしても気になっていた事があります。

それは…
 
初回のキーボード表示が遅いっ!!
 
 
まぁ、この辺りはOSのバージョンアップで改善される(もしかしたらiPhone4では改善されてる??)部分かもしれませんが、
現段階(シミュレータ上でも)では、アプリを起動して初めてキーボードを表示させるところで明らかに画面が止まります。
iPhone3Gでは約1秒も!!

これはかなり気になるところで、まだ僕はiPhone4を触った事がないので最新のものではどうなっているのか分からないのですが、シミュレータでさえ遅く感じる処理なので、おそらく最新でもそのままなのではないかと?
(さらに、これは自作アプリだけではなく標準アプリでもある状況のようです。)
 
 
ですが、これを無理矢理^^;改善してみましたっ!
 
 

5. 初回からキーボードを素早く表示させる方法

その方法とは、
起動時にダミーのテキストフィールドを作って、
先にキーボードを表示させちゃおうぜ!
って方法です。
 
 
で、実装はこんな感じ。
(※以下、すべてDoTodoAppDelegate)

 
<<DoTodoAppDelegate.hにインスタンス変数を追加>>
	// Preload Keyboard
	UITextField *_dummyField;
	UIWindow *_kbWindow;

<<DoTodoAppDelegate.m>>
#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    
    window =  [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    RootViewController *rootViewController = [RootViewController new];
	navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
	[rootViewController release];
	[window addSubview:navigationController.view];

	
	/*! キーボードをプリロードするためのテキストフィールド */
	_dummyField = [[UITextField alloc] initWithFrame:window.bounds];
	_dummyField.background = [UIImage imageNamed:@"Default.png"];
	_dummyField.userInteractionEnabled = NO;
	[window addSubview:_dummyField];
	[_dummyField release];

	
    [window makeKeyAndVisible];
	
	
	// オープニングアニメーション
	[UIView beginAnimations:nil context:nil];
	[UIView setAnimationDuration:0.3f];
	_dummyField.alpha = 0.f;
	[UIView commitAnimations];
	
	[self preloadKeybord];
	
    return YES;
}

まず、このようにダミーのテキストフィールドを作って、さらにその背景画像としてDefault.pngを表示させちゃいます。
(これならユーザーはコレがテキストフィールドだとは思いません。)
 
 
そして、その_dummyFieldをフェードアウトする事でオープニングアニメーションのように見せます。
そのあとに次に紹介する[preloadKeybord]というメソッドを実行します。

 
#pragma mark -
#pragma mark UIKeyboardのプリロード
- (void)preloadKeybord {
	// キーボードの表示/非表示を通知
	id defaultCenter = [NSNotificationCenter defaultCenter];
	[defaultCenter addObserver:self selector:@selector(keyboardDidShow:) 
						  name:UIKeyboardDidShowNotification object:nil];
	[defaultCenter addObserver:self selector:@selector(keyboardDidHide:)
						  name:UIKeyboardDidHideNotification object:nil];
	
	[_dummyField becomeFirstResponder];
	
	NSArray *windows = [[UIApplication sharedApplication] windows];
	
	if ([windows count] > 1) {	// キーボードを表示するwindowを隠す
		_kbWindow = [windows lastObject];
		_kbWindow.hidden = YES;
	}
}

ここでは、まずキーボードの表示/非表示時にそれぞれあるメソッドに通知させるように設定してます。
(表示時は[keyboardDidShow:]、非表示時は[keyboardDidHide:])

その後に[becomeFirstResponder]してキーボードを表示させます。

そしてすぐその後に現在のwindowの数を取得します。
(みなさんご存知のようにwindowは通常ひとつなのですが、キーボードが表示されている時はキーボード用のwindowが別に表示されているようです。)
そして通常のwindowの上にキーボード用windowが来るので、それをhiddenにしてしまいます。
 
 
次が各通知の際に呼び出されるメソッドの実装になります。

 
- (void)keyboardDidShow:(NSNotification *)notification {
	
	[_dummyField resignFirstResponder];
	// 通知センターをリムーブ
	[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
}

- (void)keyboardDidHide:(NSNotification *)notification {
	
	[_dummyField removeFromSuperview];
	
	_kbWindow.hidden = NO;		// 元に戻す。
	_kbWindow = nil;
	[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
}

ここでは、表示されたらすぐ[resignFirstResponder]でキーボードを非表示にして通知センターをリムーブ。

そして、非表示になったら_dummyFieldをリムーブして、キーボード用windowを元の状態に戻したら通知センターをリムーブ。
 
 
これで、起動後初めてのキーボード表示時にもスムーズに画面遷移されるようになります。
これらの処理を追加する前と比べてみるとよく分かると思いますよ。
 
 
ただし、この実装にはリスクもあります!
 

それは、起動時間。つまりLaunch Timeがわずかながら長くなるという点。
でも、僕はアプリを操作中に待たされる一秒起動時に待たされる一秒では
圧倒的に前者の方が長く(不快に)感じるのではないか
と思っているので、今回の実装には意味があると思っています。
 
ただ、マルチタスクに対応した場合はアプリを起動する回数が減る(バッググラウンドに入るため)なので、必須な実装ではないかもしれませんが…^^;

ともあれ、ほんの少しでも快適に使えるようにする悪魔のような繊細さも大事でしょ!!ってコトで。
 
 

6. やる事はまだまだある。

みなさま、ここまで着いて来てくださり
本当にお疲れさまでした!

 
 
ですが、実際にアプリを作る上ではここからが本当のキメドコロなのかも。

・アプリの全体的な高速化
・マルチタスクへの対応
・CPU使用率の低減
・さらに細かいデバッグ
・メモリ警告時の対応

…などなど。

 
 
例えば今回のdotodoでは、テーブルビューでの表示にイメージ文字色となるUIColorを読み込んだり、NSDateFormatterを使ったりしていますが、これらはテーブルセルごとに毎回呼ばれるため、リユースできるような設計にしてあげた方がパフォーマンスは上がります。
他に起動を高速にするためにスレッドを分けたりする方法もありますね。

そういった事を改善できてはじめて、ユーザーに認めてもらえるアプリになるんじゃないかなぁなんて思ってたりするんですよね。
 
 
そして、今回のメインであったCoreDataに関しても触れなかった点があります。
 
 
UndoManagerとMigration(マイグレーション)
 
 
まずUndoManagerは名前の通り、アンドゥ/リドゥを管理するクラスです。
これはManagedObjectContextにセットできるのですが、今のところ必要となる要素が見つからなかったのでまだ深くはつっこんで調べていません。

ただ、おそらくドロー系のアプリなどでは必須のクラスになるんぢゃないかと思ってます。
 
 
つぎのマイグレーションについても、まだまだ僕の勉強不足で説明できる事は少ないのですが…<(_ _;)>
おそらく、バージョンアップに伴うデータモデルの変化に対応させるのがメイン機能なのではないかと。

例えば、dotodo ver.1.0 ではCategoryエンティティの属性に[name]というのがありましたが、
ver. 1.1の時には、[name]を[title]に変えたい!
といった場合に使えるようで、マイグレーションを使う事でそれまで[name]として設定されていたデータを[title]として設定し直してくれるみたいです。
(相変わらず曖昧な説明でスミマセン^^;)
 
 
…と、ざっと上げただけでもこれだけやる事や勉強しておくべき事があるってことですね。
もしかしたら、ココまでやって初めてスタートラインに立てたのかもしれませんが、ここまで『Study CoreData』をこなして来た僕らなら後は何とかなるだろ!って気持ちで共にがんばっていきまっしょい!
 
 

7. おわりに。

いや〜、なにはともあれ
21日間に及ぶなが〜い旅路、本当にお疲れさまでした!

 
 
この『Study CoreData』シリーズは以上で完結となりますが、
まだ、このシリーズにあたって参考にさせて頂いたリソースを紹介していませんでしたので 明日、改めて紹介させて頂きますね。
 
 
そして最後に。
ここまでお付き合いしてくださった多くのみなさまにはもちろん、このブログを紹介してくださった方々にも本当に心から感謝をしたいと思います!!
 
 
ありがとうございました〜<(_ _)>
 
 
そして、みなさんが素晴らしいアプリをリリースされ、大成功される事を期待してます!

それでは!

はっぴーでべろっぴん♬
 
 
 
以上。
Jacminikでした!
 

広告

Study CoreData 22 ~最終章 『やめてはいけない!』~」への15件のフィードバック

  1. ピンバック: Tweets that mention Study CoreData 22 ~最終章 『やめてはいけない!』~ « Everything was born from Love -- Topsy.com

  2. ピンバック: [朝刊] iPhone や iPad をトラックパッドにするアプリが気になる。

  3. Jacminikさん、こんにちは。非常にためになるエントリ長い間ごくろうさまでした。

    アプリ開発初心者の私はというと、もう何十回もエントリ0から読み直していますが、ビルド→エラーの繰り返しで、まだシリーズの8,9あたりをうろうろしています。でも、その繰り返しのおかげで着実に前進してる感じはあるんですよね。今まで読んだどんな解説本より楽しさと達成感があります。

    そして、今日のエントリの書籍の一節を読んで、「これでいいのだ!」と、また勇気をもらえました。

    何日かかってもこの最終エントリに到達するぞ!

    • Junichi_さん、本当に嬉しいコメントありがと〜ございまっす♬

      このシリーズでは、”最後まで自分でやらないと理解できない”っていうある意味不親切な方法をあえてとっているので、結構大変だと思います^^;

      それに僕自身不慣れな解説で分かりにくい部分も多々あるかと思うので、行き詰まった時には気軽にtwitterなどで、質問してみてくださいね。

      応援してます!共にがんばりまっしょ〜!

  4. はじめまして。ご参考になればと思いコメントいたします。
    いくつかある方法の一つです。

    > ひとつは、例えば“ルクと散歩”というTodoを検索するために
    > “散歩 ルク”ってやると検索されない点。

    正規表現を使わなくとも、次のような方法でも検索できると思います。

    (1) スペース区切りで文字列を分割する。componentsSeparatedByCharactersInSetメソッドなどでできます。

    (2) 文字列ごとに「*文字列*」でヒットする形のNSPredicateクラスのオブジェクトを作成する
    NSComparisonPredicateクラスでも、NSPredicateクラスのpredicateWithFormatでもどちらでも良いと思います。

    (3) (2)で作成したNSPredicateをNSCompoundPredicateクラスでand結合する

    (4) (3)で作成したNSPredicateをNSFetchRequestにセットする

    それと、ひらがなとカタカナを無視して検索できるようにする方は、検索用のフィールドを用意して、そちらには常にカタカナで入れたり、ひらがなで入れたりするようにするという方法もあると思います。

    • 林さん、はじめまして!

      なるほど、とっても参考になる実装方法ですね。ありがとうございます。
      ただ、まだ実際にやってみないと理解できない部分が多いので、頂いたコメントを参考に実装してみようと思います。

      あと仮名の同一性については、Mac向けの開発ではNSPredicateで普通にできるようなので、その辺も含めて、もう少し勉強しなきゃなって^^;

      貴重なコメント本当にありがとうございました!今後ともよろしくお願いします。

  5. 長々とご苦労様ですが、いつになったらそのTapDaysとかいうライフデザインツール(爆笑)は公開されるのですか?
    iPhoneアプリ開発に挫折する方の特徴の一つとして「最初からあまりに実用的なアプリを作ろうとする」というのがあるそうですよ。あと「開発期間が長すぎる」というのもあなたに該当しますね。
    あなたの周囲の方がお優しい方ばかりで苦言を言われることはないのかもしれませんが、今のままではあなたにはiPhoneアプリ開発は無理かと思います。
    あと、どうしてiPhoneアプリブログは「中学生が」とか「初心者が」って但し書きが付くのか不明。完成しなかった場合やレベルの低い場合の言い訳としか思えません。

    • hogeraさん、はじめまして。
      コメントありがとうございます。

      確かにご指摘の通り、開発期間は長すぎですね…。それは認めざるを得ませんし、恐らく表には出さなくてもhogeraさん以外にもそう感じる方はいるだろうなぁというのはなんとなく感じていました。
      ただ、元々そのTapDaysを作りたいがためにアプリ開発を始めた所もあるので、完成させるのは大前提でやっています。
      難しいのは初めから分かっていたので。

      あとごめんなさい。正直に言うと公開予定期日はまだ決められる状態ではないです
      もしかしたら、その前にもう少し実装が簡単なものを出すことになるかもしれません。

      確かに言われてみれば『初心者が…』って言うのは、そう思われても仕方ないのかも知れませんね。
      特に意識はしてなかったのですが、もしかしたら自分自身にそういう言い訳じみたもちょっとはあるのかもなぁと思いました。
      ここは肝に銘じておきたいと思います。
      ご指摘ありがとうございました!

      そして頂いたコメントの中の

      >今のままではあなたにはiPhoneアプリ開発は無理かと思います。

      白状します!正直イラっときました^^;
      でも『今のままでは』って書いてくれてるんですね。
      これも現状の自分への戒めとして、心に留めさせて頂きます。

      おそらくご納得頂けるような返信にはなっていないと思いますが、完成したアプリで納得してもらえるようにするしかないですよね。

      とにかく、僕にとっては貴重なコメントでした。
      あえて苦言を呈してくださりありがとうございました。

  6. Jacminikさん、こんにちは
    出来の悪い生徒のaonsです。やっとここまで来ました。いっぱい勉強させて頂きました。iPhoneアプリを作るための本やネット上のコードを今までいくつか見ましたが、パーツとしての情報が多く、初心者の私にはそれをどう組み合わせていったら良いかがよく分かりませんでした。
    デモデータも解説がないので、知識の少ない私には意味が分からない事も多いです。その意味でJacminikさんのこのサイトは一つのアプリを作る過程を詳細に説明してくれるのでとても有意義でした。教えてもらったものを参考にしながら、自分なりのアプリを作ってみます。またStudy CoreData_2の登場する日があることを期待してます。ありがとうございました。

    • aonsさん、こんにちわ。

      ここまで来るのは大変だったと思います。
      出来の悪いなんてとんでもないですよっ(^〜^)v 本当にお疲れさまでした!

      このシリーズを始めた頃は、ウェブにしろ書籍にしろ、初心者を脱したくらいの中級者向けのまとまった実践的な情報が少ないなぁと僕自身感じていたので、こうやって少しでも参考にしてもらえるのは本当にウレシイです。

      Study CoreData 2ですかぁ^^;
      CoreDataに限らずやってみたい気持ちはあるのですが、実は異常に大変な作業だったりするので、また気が向いて時間が出来た時に…ってコトで。

      今度はお互いにAppStoreで切磋琢磨し合えるといいですね。
      本当にありがとうございました!
      今後ともヨロシクです<(_ _)>

  7. Hi jacminik.
    I am a student in Thailand.
    
I can’t understand in Japaness language.
    i would like to consult about Coredata.
    I have some e-mail you. :)

    • Hi! Chatzkub.
      Sorry…
      I can’t speak Thai, and English little.
      I think The English Book “More iPhone 3 Development Tackling iPhone SDK 3” help you.

  8. Hi jacminik.
    Thank you for suggestion about iPhone text book (More iPhone 3 Development Tackling iPhone SDK 3), it very useful. But in text book don’t have tutorial function for choose or tags and Search function.
    I think if I have your sourcecode I wil go faster, because of this I could you send me for your sourcrcode to myemail please.
    If it possible I think I can study it myself and graduate in this semester. Yours Faithfully.

    Thank you. :))
    chat_dj@hotmail.com .

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中