2015年3月25日水曜日

iOSのFacebook Eventカレンダーの謎

iOSで設定されているカレンダーの一覧は

let store:EKEventStore = EKEventStore()
let cals = store.calendarsForEntityType(EKEntityTypeEvent)

で取得ができます。

EKEventStoreを廃棄した後に、 取得したEKCalendar#source や EKCalendar#description などを使おうとすると、以下のようなエラーを吐いて、クラッシュします。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI sourceType]: unrecognized selector sent to instance 0x7fc7e2cba4b0'
*** First throw call stack:
(
 0   CoreFoundation                      0x0000000106c7da75 __exceptionPreprocess + 165
 1   libobjc.A.dylib                     0x00000001087d5bb7 objc_exception_throw + 45
 2   CoreFoundation                      0x0000000106c84d1d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
 3   CoreFoundation                      0x0000000106bdc9dc ___forwarding___ + 988
 4   CoreFoundation                      0x0000000106bdc578 _CF_forwarding_prep_0 + 120
 5   EventKit                            0x0000000106945475 -[EKCalendar type] + 67
 6   EventKit                            0x0000000106945649 -[EKCalendar description] + 97

まぁこれは、そういうもんなんだろうなー! という感じですよね。
多分、SourceはEKEventStoreで保持されていて、storeが廃棄されたらSourceも廃棄(nullじゃなくてなんか違うオブジェクトになっちゃう感じ?)になるとか、そんな感じだろうと。
が、ここで謎が一点浮上。

iPhoneに、Facebookアカウントの"Facebook Events"というカレンダーがあります。
このカレンダーは、EKCalendarでみると、
title は "Calendar"、 source は "CalDAV" になります。

"Facebook Events"という名前で取得できればいいのにー と思っていたのですが、
なぜか
EKEventStoreを廃棄した後に、EKCalendarのタイトルを見ると、"Facebook Events" という名前に変わっている!
(titleであれば、storeを廃棄した後でもクラッシュせずに取得できる)

わざとEKEventStoreを廃棄すれば、そのカレンダーはFacebook Eventsなのかどうかが判断出来るという。。。
• • •

2015年3月23日月曜日

iOS8.2でHealthKitが早くなったよ

iOS8.2になって、HealthKitからの情報読み込みがぐーんと早くなりました!

多分いちばん影響しているのが、iPhoneのモーションセンサーが記録している歩数などの記録間隔が長くなったこと。

iOS8.1では、1分間に2つぐらいのレコードが記録されていましたが、
iOS8.2では、5分に1つぐらいのレコードが記録されています。

そのため、全体のレコード数が随分少なくなりました。
分単位で細かい情報が知りたいということのほうがマレだと思うので、良い改善だったのではないかと思います。

「HealthKit試してみたけど、おもすぎて使えないー」と思っていた方がいたら、もう一度確認してみてもよいかもです!
FitPortアプリのグラフ表示も快適に♪

いやー、良かった良かった。


• • •

2015年3月6日金曜日

CocoaPodsで共通ライブラリ

CocoaPods を使って、自社用の共通ライブラリを整備中。
リソースファイルの扱いをどうすると一番やりやすいかを今日は悩んでいたのでメモ。

podsspecに下記のように書くと、そのリソースが MainBundleに含まれる。

spec.resources = "My/Resources/*"

この場合は、名前の重複に気をつける必要がアリ。
出来る限り、本体のアプリには影響を与えないようにライブラリを作成したいので、Bundleを試してみた。

Bundleの指定

spec.ios.resource_bundle = {'My-Resources' => ["My/Resources/*"]}

のようにかけば別のBundleになる。
上記の例だと、My/Resources/ 以下を全て含んだ My-Resources という名前のBundleができる。

ローカライズのファイル

ja.lproj/Localized.strings
en.lproj/Localized.strings

などは、
ja.lproj, en.lproj のディレクトリも含めてバンドルにする必要があるので、

Resources/* 

のようなディレクトリ指定にする。

Resouces/**/Localized.strings

のようなファイル指定だとディレクトリが出来ないのでダメ。

Bundleからリソース読み込み

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"My-Resources" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];

のBundleを使用する。

ローカライズの文字列を取得するには

NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment)
で取得が可能。
Bundleにすると、Key名の重複を気にしなくてもいいのがマル。

Storyboardの扱い

Bundleの中にStoryboardファイルを入れた場合、

UIStoryboard#storyboardWithName:(NSString *)name bundle:(NSBundle *)storyboardBundleOrNil;

にて、bundleを適切なものを設定すれば読み込める。

ただし、Storyboardに画像を設定していて、その画像をAssetCatalog(xcassets)で管理している場合は、以下のメッセージとともに、画像読み込みが出来なかった。

Could not load the "imageName" image referenced from a nib in the bundle with identifier "(null)"

bundle identifierが(null)となっているのが気になるが、そういうものなのだろうか...

AssetCatalogで管理しているUIImageの扱い

StoryboardをBundleにした場合、上述のように画像読み込みが出来なかった訳だが、
StoryboardとAssetCatalog (Images.xcassets)を同じBundleにしても同様に読み込みは出来なかった。

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"My-Resources" ofType:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
UIImage *image = [UIImage imageNamed:@"btnName" inBundle:bundle compatibleWithTraitCollection:nil];

としても UIImageは取得出来ない。

[UIImage imageNamed:@"btnName"]

でなぜか画像が取得できる。

ということで、
AssetCatalog (Images.xcassets) をBundleに入れたとしても、
コンパイルしてAssets.carにまとめられるときに、MainBundleのものとして生成されているのと想像。

試してはないが、AssetCatalogを使わず、画像ファイルを直接扱えば、別Bundleとして扱える気がする。

AssetCatalogを使う場合には、画像名が重複しないようにプレフィックスなどをつける運用が必要。

現状まとめ

私の場合、StoryboardやAssetCatalogはデザイナーさんに通常のプロジェクトと同じように触って欲しいので、あれこれせずにMainBundleとして取り込む方針に。

  • Storyboard はBundleにしない
  • 画像名とStoryboard名は重複しないようにプレフィックスをつける運用へ
  • • • •