好評販売中!!
月特化カレンダー Moca / 記念日リマインダー / Popin / 文庫ビューワ SkyBook / i単語帳
2015-03-18 (Wed)
■ storyboard 上で作った Container view で追加した childViewController を restoration したい場合 
ベースとなる ViewController 内で encodeObject するだけでなんだかうまくいくようだ。
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder { [super encodeRestorableStateWithCoder:coder]; UIViewController *viewController = self.childViewControllers[0]; [coder encodeObject:viewController forKey:@"viewController"]; } - (void) decodeRestorableStateWithCoder:(NSCoder *)coder { [super decodeRestorableStateWithCoder:coder]; }
2014-10-16 (Thu)
■ iCloud + CoreData の動作でわかんないことがあるんで整理 
iCloud + CoreData でデータベーススキーマの更新が思う通りにいかない。そもそも根本的な理解が足りない部分があるのかなあ。まとめるために書きなぐり。
スキーマを更新したい場合はLightweight Migrationだけ対応してるってことで、これはデータモデルの「Add Model Version」をしてModel VersionのCurrentのところを変更するっていう、普通にCoreDataを使っている場合にもやるやり方だと思うんだけど…。
When you use the SQLite store with iCloud, the store supports only lightweight migration.
Using the SQLite Store with iCloud
これだけだとデバイス間の同期が期待通りに行なわれない。
具体的には
- デバイスAで登録した内容をデータAとする
- デバイスBで登録した内容をデータBとする
- この段階ではデバイスA・BでそれぞれデータA・Bが両方表示されていて、同期も問題なく超いい感じ
- デバイスAで、スキーマを変更したアプリにアップデートすると、データAのみ表示される、データB(他のデバイスで登録した内容)はどっかいって戻ってこない
- デバイスBでアプリをアップデートすると、デバイスAにデータBが戻ってくる
- 以降はデバイスA・Bで同一の内容が同期される
つまり、片方のデバイスを失くすと、そのデバイスで登録した内容も一緒に失われる状態。悲しい。
iCloud内のデータ(Macだと~/Library/Mobile Documents/)を見たりして、いま理解できているのは
- iCloud上には、データベースのトランザクションログが記録される
- ログが格納されるフォルダは、「デバイス固有のUUID(?)/NSPersistentStoreUbiquitousContentNameKeyで指定した名前/データベースモデルのハッシュ」みたいな感じ
- → ようするに、デバイス毎+データベースのバージョン毎に別々にトランザクションログが記録される
- iCloud+CoreDataの同期の実装は、他のデバイスのトランザクションログをダウンロードしてきてローカルに適用する形
- → ただし、適用するのはデータベースモデルのハッシュが同じものに限る
- モデルの内容を変更したアプリを実行すると、データベースモデルのハッシュが変わり、そのデバイスで使うiCloud上のログ格納ディレクトリも新しく作られる
- → その実行したデバイスで登録したトランザクションログに限って、この時作られる新しいフォルダにコピーされる
- 新しいデータモデルに対応したアプリを実行しないと、そっちのデバイスで使っているiCloud上のトランザクションログの格納フォルダは古いまま更新されない
- → [4]でデータが失われたのも、他のデバイスではモデルのバージョンが古いからくさい。
トランザクションログを一箇所にまとめることはできないのか?または他のデバイスのモデルのバージョンが古くても同期する方法はないのか。ようわからん。
2013-12-18 (Wed)
■ Qiitaを使うのがいいかなあ 
というわけで、このまえ書いたEKReminderのメモの続きとなるもの?をQiitaに投稿してみた。
技術的なものはQiitaを使うのが情報がまとまってていい感じなのかなあ。hatena.vimみたいにvimで投稿できるものはあるかな。
2013-12-13 (Fri)
■ EKReminderのメモ 
リマインダーアプリのデータを取ってこれるEKReminderについてのメモ。こんど簡単に使い方とかまとめたいのだけど、とりあえず今日調べたことについて書いておく。
EKReminderにはdueDateComponentsていうプロパティがあって、これとalarmsっていういかにも通知に使いそうなプロパティがあるんだけど、リマインダーアプリの編集画面には「通知」という項目しかないのでどうなってるのか気になった次第。
EKEventStoreでリマインダーデータを登録して、アプリでどんな感じで表示されるのか調べた。調べてる最中。
- 期限(dueDateComponents)+アラーム(addAlarm)の両方を指定した場合
- アラームのみ指定した場合
- 期限のみ指定した場合
- 期限で日付のみ指定した場合
- 期限+アラームで時間が異なる場合
iOS7とMac(Mavericks)のリマインダーアプリとicloud.comのリマインダー、3種類共にリストには「期限」の日時が表示される。Macの場合は[2]のアラームのみ指定の場合にアラームの日時が表示された。
編集画面では「通知」という名前でアラームに指定した内容が編集できるようになっていて、基本的に「期限」は編集できない。ただし、アラームと期限の時刻が異なる場合は両方編集できる。期限のみ指定されてる場合は、編集画面で通知を登録すると期限がどっかに消える。
Macとicloud.comのリマインダーの場合は、アラームのみ指定の場合に期限が編集できる。
なんだかまとまりがないけどこんな感じ。
Calendar and Reminders Programming Guide にはリマインダーのことについてさらっとしか書いてなくてどう使えばいいのかまでは書いてない。
もしかすると「最初はdueDateComponentsとアラームに同じ時間を登録する」→「期限がやってきて通知された」→「オプションで"×分後に再通知"を選択」という操作をするとdueDateComponentsとアラームが違う値になりそう。試してみたら実際にそのようになった。ので、リマインダーを編集する際は基本dueDateComponentsとアラームと同じにする、というのがリマインダーアプリと同じような動きにする秘訣かも。
そうそう、問題はdueDateComponentsに日付のみを指定した場合だ。リマインダーアプリから編集する場合は時間が必須でどうにもならんのだけど、EventKit直接触る場合はそうでもない。実際にドキュメントのdueDateComponentsの所に「時間指定をしなかった場合はall-dayのリマインダーになる」と書いてある。ただこのall-dayになったリマインダーをリマインダーアプリでいじるとall-dayな情報が消えちゃったかも。この辺はまだ検証していない。ちなみに時刻指定をしない場合は-(BOOL)allDayを呼ぶとYESが返ってきます。undocumentedですががが。
2013-12-12 (Thu)
■ TwitterのSLRequestまわりでリジェクトされた件について 
みなさんこんにちは。
最近 Popin というアプリを作ってまして、Twitterのタイムラインから言及数が多かったりするURLを抜き出して表示してくれるアプリみたいですね。Twitterあんまり見てないけど最近話題になってる情報が知りたいって時に便利そうです。
で、Twitterのタイムラインからデータを取るのにSocial.frameworkのSLRequestを使ってるわけです。これを使うとTwitterのAPIが簡単に使えて超ベンリなのです。事前準備としてアカウントを選んでもらったりとかしないといけませんが、タイムラインにアクセスするだけだったらこんな感じになってます。
NSString *accountIdentifier = ...; NSURL *URL = [NSURL URLWithString:@"http://api.twitter.com/1.1/statuses/home_timeline.json"]; NSDictionary *params = { @"include_entities": @"1", @"count": @"100" }; SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:URL parameters:params]; ACAccountStore *accountStore = [[ACAccountStore alloc] init]; ACAccount *account = [accountStore accountWithIdentifier:accountIdentifier]; [request setAccount:account]; [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { // responseDataがJSON }];
それで、表題のリジェクトの件ですけどね。状況から言ってこのコードの辺りが原因でリジェクトされてしまいました。…もしやResolution CenterのやりとりはNDA範囲内かな。簡単に言うとガイドラインの「2.2 バグがあるアプリはダメ」って言われてスクリーンショットが添付されていました。UIAlertViewが表示されてるんですけど、内容は上記performRequestWithHandlerのNSErrorを表示したものと思われます。
NSURLErrorDomain:-1012
The operation couldn't be completed. (kCFErrorDomainCFNetwork error -1012.)
開発中にこういったエラーは見たことがなかったのでめんどくさいことになったなと…。
調べてみると、エラーコード1012はNSURLErrorUserCancelledAuthenticationとなってて認証まわりに問題がありそうだったから、ダメ元で「設定アプリでTwitterの認証をやり直してもう一回やってみてよ」って上申したら、つぎの日にReady for Saleになりました。やったね!