jarinosuke blog

about software engineering, mostly about iOS

Push 通知内の Payload の内容を起動時にデバッグする

Payload

Push 通知には Payload と呼ばれるデータ領域があり、

そこにはシステムがユーザの警告するためのデータや、別用途で用いるためのカスタムデータなどが入っています。

iOS 側での実装

対象のアプリケーションが起動していない状態で、Notification Center 内の通知をタップするなどして起動すると、

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

が走り、launchOptions の中に Push 通知内の Payload のデータが入っているというわけです。

そのデータは以下の様にして取得する事ができます。

launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]

デバッグが困難

では実際に Payload 内におかしなデータが入っている可能性があり、

その内容をみながらデバッガを使ってデバッグしたいという場合があるとします。

しかし、

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

はアプリケーションの起動してすぐに走ってしまうので Xcode でビルドした時に呼ばれてしまい、

起動中に Push 通知を受け取った場合は以下の別のメソッドが呼ばれてしまうのです。

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

解決策

長々と書いてきましたが、以下の様にして簡単に解決出来ます。

Edit Scheme から対象の Scheme を選び、

Info タブの Launch トグルを Automatically から Wait for ~ to be launched menu に変えるだけです。

f:id:jarinosuke0808:20140618181624p:plain

こうすることでアプリが実際に指などで起動されるまでデバッガが起動しなくなります。

早川製菓 ふなっしー 梨汁ブシャーキャンディ 80g×6袋

早川製菓 ふなっしー 梨汁ブシャーキャンディ 80g×6袋

CADisplayLink について

ユースケース

CADisplayLink を実際に使う例と共にどんなクラスなのか簡単に紹介します。

例えば現在時刻を表示する場合。

画面に表示されている日時を定期的に更新する必要があります。

そのような場合に NSTimer で 0.01 秒など適当なインターバルを設定して更新、みたいなこと実装した経験ありませんか?

僕はあります。

それを解決するための表示されているビューを更新するためのイベントを取得するためのクラス、それが CADisplayLink です。

最近 facebookOSS 化した pop や、長年 iOS の 2D ゲームフレームワークとして親しまれている cocos2d でも、もちろん使われていました。

facebook/pop

cocos2d/cocos2d-iphone

CADisplayLink の使い方

CADisplayLink を以下の様にしてセットアップすることで、ディスプレイの更新タイミングをトリガーにしたイベント実行が可能になります。

///1
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];


///2
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

画面更新には CADisplayLink

前述したバッドケースとして NSTimer で更新、というのがありました。

これの何が悪いかというと、メインスレッドのロックなどが無ければ、画面の更新は大抵の場合 1/60 秒間隔で行われます。

なので、NSTimer でそれより細かい間隔で更新してもディスプレイには反映されません。

また画面の更新間隔はメインスレッドの具合によって変動するので、 CADisplayLink を用いて画面と関連づけて更新した方が良いというわけです。

ちなみに Building Paper を見て初めて知りましたが、大体メインスレッドで5ms以上処理がかかると、ドロップフレームが起こるらしいです。

facebookの”Building Paper”はすべてのiOSエンジニアがみるべき

CADisplayLink の考え方としてはゲーム開発などのフレームワークなどに良くある update 関数と近いというか同じだと思います。

画面上に描画する処理をユーザからの入力や、システム内のループで行っているものを CADisplayLink に置き換える事でアプリケーションをヌルヌル動かす事ができるようになるかもしれません。

アップル Apple Mini DisplayPort-Dual-Link DVIアダプタ MB571Z/A

アップル Apple Mini DisplayPort-Dual-Link DVIアダプタ MB571Z/A

UIViewTintAdjustmentMode について

tintColor

iOS 4 以前は tintColor プロパティは UIToolBar など、限られたクラスにのみ提供されていました。

UIToolBar - tintColor | iOS Developer Library

iOS 5 になると UIAppearance が提供され、他にもたくさんの UIView のサブクラスが tintColor プロパティを持つようになりました。

iOS 7 では遂に UIView 自身が tintColor プロパティを持つようになり、アプリケーション全体への tintColor の反映が可能なりました。

どういうことかというと、値を明示的に指定しない限り tintColor プロパティは親ビューの値を受け継ぐので、

たとえば keyWindow の tintColor プロパティを設定するだけでアプリ全体に行き渡る、ということです。

UIViewTintAdjustmentMode

iOS 7 からは tintColor 同様、UIView に tintAdjustmentMode というプロパティが追加されました。

このプロパティは上記の tintColor の継承関係をどのように反映させるかを決定するものです。

subviews の中に tintColor プロパティがデフォルトのものしか無ければ値は UIViewTintAdjustmentModeNormal になります。

UIViewTintAdjustmentModeDimmed を明示的に tintAdjustmentMode に設定すると、

その UIView の tintColor からは dimmed 加工が施された UIColor が return されるようになります。

ユースケース

f:id:jarinosuke0808:20140406193757p:plain

そもそもこのプロパティを見つけるに至ったきっかけになるのですが、

UIAlertView のようなモーダルで覆うカスタムビューを作る場合などに使用します。

以下のコードが非常に勉強になりました。

TeehanLax/TLAlertView

ちなみに UIAlertView を show したときに後ろ側に回る keyWindow を見るとわかりますが、

UIAlertView はしっかり keyWindow の tintAdjustmentMode を dimmed に設定しています。

なので UIKit の作法に従うためにも、細かいですがしっかりと実装していきたいところですね。

参考

UIView - tintAdjustmentMode | iOS Developer Library

[iOS 7] UIViewにtintColorが追加されました!| Developer.IO

Adopting iOS 7 APIs

TeehanLax/TLAlertView

UIAppearance | NSHipster

Asset Catalog の AppIcon を使って複数ターゲットで出し分ける

Asset Catalog 以前

Asset Catalog が出るまでは、アプリのアイコンをターゲット毎に出し分ける際は

以前に僕が書いた

アプリの情報をアイコンにオーバーレイさせる方法

と同じ要領で、ビルド後のスクリプトを用いてターゲット毎に用意された画像を適宜配置する必要がありました。

Asset Catalog 以後

Xcode 5 から Asset Catalog という画像管理に便利な機能が追加されました。

Xcode 5 で新しくプロジェクトを作成すると、Images.xcassets というフォルダがデフォルトで作成されます。

内部には AppIcon と LaunchImage という Image Set があり、これらに画像を適用するといった具合です。

画像の slicing を直感的に指定できたり、AppIcon も iOS/iPad 毎、iOS のバージョン毎に指定できたりと便利です。

Asset Catalog を用いたアイコン出し分け

では上記の AppIcon や Launch Image を出し分けるかという肝心な所ですが、

Build Settings を覗くと、 以下の項目がある事に気付くかと思います。

Asset Catalog Compiler - Options

その中に、

Asset Catalog App Icon Set Name
Asset Catalog Launch Image Set Name

という設定項目があり、デフォルトでは AppIcon と LaunchImage が設定されています。

これを ReleaseAppIcon などの自身で設定した Image Set 名に設定する事で、

ターゲット毎に Image Set を変更する事が可能になります。

ちなみに xcconfig などで書くのであれば、上記2項目のディレクティブは以下です。

ASSETCATALOG_COMPILER_APPICON_NAME
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME

Asset Catalog のとても残念な所

ここまで Asset Catalog の良い所だけ書いてきましたが、実のところ残念な所も多いです。

たとえば上記で行ったターゲット毎の Image Set の出し分けを、

アプリ内で利用するリソースに対しても行うのは非常に困難で、

結局ビルド後のスクリプトで Images.xcassets 配下の画像リソースを上書きする手間が出てきます。

詳細は下書き程度に書いた僕のブログを読んでください。

Asset Catalogs with Multiple Targets | jarinosuke stash

iOS 7.1 から iAd 経由でのアプリインストールのコンバージョン計測が可能に

コンバージョン計測

iOS 7.1 から以下の API が iAd.framework に追加されています。

iOS 7.1 API Diffs

ADClient

- (void)determineAppInstallationAttributionWithCompletionHandler:(void (^)(BOOL appInstallationWasAttributedToiAd))completionHandler NS_AVAILABLE_IOS(7_1);

この completionHandler を登録しておくと、 iAd 経由でユーザがアプリをインストールした場合に発火するといった具合です。

また設定において、Limit Ad Tracking を行い、広告トラッキングをオプトアウトしている場合には appInstallationWasAttributedToiAd が NO で発火するようです。

iAd の発展

iOS 6 から advertisingIdentifier が追加され、UDID や MAC Address の取得が禁止になり、 iOS 7 ではiAd の導入が劇的に簡単になりました。

以下のような噂(1年前の記事ですが)も立っているようなので、今後も Apple の広告への取り組みは注目されそうです。

Appleがクッキーを利用しているアプリを拒絶へ: Ad Identifierへの統一がねらい

参考

AdとID周辺の動き