Edited at

GoogleAnalyticsを全ての画面やボタン等に一括で差し込む方法

More than 3 years have passed since last update.


Podfile

BlockInjectionというライブラリを利用するため、Podfileを編集します。

メソッドを実行する前後に処理を差し込むことが出来るもので、GA以外にも全画面に及ぶ処理等を実装する時に重宝します。


Podfile

pod "GoogleAnalytics-iOS-SDK"

pod "BlockInjection"


viewDidAppear:

viewDidAppear:が呼び出される度に実行するメソッドをblockで渡します。

blockの第一引数にはメソッドを実装してるクラスのインスタンスが渡り、第二引数以降にはメソッドに渡される引数が渡ります。

対象とするクラスには正規表現が適用できるため、UIViewControllerのサブクラス名に命名規則を持たせていると便利です。

今回はGAS(GoogleAnalyticsSample)から始まり、ViewControllerで終わる、という命名規則を持たせています。


AppDelegate.m

[BILib injectToClassWithNameRegex:BIRegex(@"^GAS.*ViewController$") methodNameRegex:BIRegex(@"^viewDidAppear:$") postprocess:^(UIViewController* viewController, BOOL animated) {

NSString* screenName = NSStringFromClass(viewController.class);
NSLog(@"screenName:%@", screenName);
[GAI.sharedInstance.defaultTracker set:kGAIScreenName value:screenName];
[GAI.sharedInstance.defaultTracker send:GAIDictionaryBuilder.createScreenView.build];
}];


UIButton

上記と同様に、UIViewControllerのサブクラスのメソッドに処理を注入します。

メソッド名に対しても正規表現が利用できます。

今回は、UIButtonのTouchUpInsideイベントハンドラのメソッド名はTouchUpInside:で終わる、という命名規則を持たせています。


AppDelegate.m

[BILib injectToClassWithNameRegex:BIRegex(@"^GAS.*ViewController$") methodNameRegex:BIRegex(@"^.*TouchUpInside:$") postprocess:^(UIViewController* viewController, UIButton* sender) {

NSString* category = NSStringFromClass(viewController.class);
NSString* action = @"touchUpInside:";
NSString* label = sender.titleLabel.text;
NSNumber* value = @(sender.tag);
NSLog(@"category:%@, action:%@, label:%@, value:%@", category, action, label, value);
[GAI.sharedInstance.defaultTracker send:[GAIDictionaryBuilder createEventWithCategory:category action:action label:label value:value].build];
}];


UIBarButtonItem

UIButtonの場合と同様に、BarButtonItemAction:で終わるメソッドの後に実行する処理をblockで定義します。


AppDelegate.m

[BILib injectToClassWithNameRegex:BIRegex(@"^GAS.*ViewController$") methodNameRegex:BIRegex(@"^.*BarButtonItemAction:$") postprocess:^(UIViewController* viewController, UIBarButtonItem* sender) {

NSString* category = NSStringFromClass(viewController.class);
NSString* action = @"barButtonItemAction:";
NSString* label = sender.title;
NSNumber* value = @(sender.tag);
NSLog(@"category:%@, action:%@, label:%@, value:%@", category, action, label, value);
[GAI.sharedInstance.defaultTracker send:[GAIDictionaryBuilder createEventWithCategory:category action:action label:label value:value].build];
}];


UITableViewCell

今回はカスタムセルのSetterに処理を差し込んでいますが、UITableViewControllerに差し込んでも良いと思います。

サンプルでは全てのカスタムセルでcell.textLabel.textをlabelとしていますが、このあたりも適宜変更してください。

selectedがNOの場合は無視するようにしていますが、こちらも必ずしもそうする必要がある訳ではないと思います。


AppDelegate.m

[BILib injectToClassWithNameRegex:BIRegex(@"^GAS.*TableViewCell$") methodNameRegex:BIRegex(@"^setSelected:animated:$") postprocess:^(UITableViewCell* cell, BOOL selected, BOOL animated) {

if (!selected) return;
NSString* category = NSStringFromClass(cell.class);
NSString* action = NSStringFromSelector(@selector(setSelected:animated:));
NSString* label = cell.textLabel.text;
NSNumber* value = @(selected);
NSLog(@"category:%@, action:%@, label:%@, value:%@", category, action, label, value);
[GAI.sharedInstance.defaultTracker send:[GAIDictionaryBuilder createEventWithCategory:category action:action label:label value:value].build];
}];


注意事項

Xcode6.1のローカル環境で実行してみたところ、実機では期待通りに動くのですが、シミュレータでの挙動がおかしいです。

そのことに本日ギークハウス横浜で無限お好み焼きを食べている最中に気付いて、絶賛キョドっています。

具体的には、シミュレータ上で実行すると


  • UIButtonを押下してもsenderとしてnilが渡っているためlabelがnilになる

  • UIBarButtonItemを押下してもsenderとしてUITouchesEventのインスタンスが渡り「titleメソッドがない」と言われて落る

等の症状を確認しています。


ErrorLog

-[UITouchesEvent title]: unrecognized selector sent to instance 0x7f9013901bf0

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITouchesEvent title]: unrecognized selector sent to instance 0x7f9013901bf0'

これはblockに対してだけでなく、inject先のメソッドでも同様の引数の渡され方がされているため、GA以外の処理に対しても影響が及びます。

現状こちら解決できていませんが、Advent Calendarの担当日が過ぎ去りそうなので、苦し紛れに投稿します。

解決したら改稿しようかなと思いますが・・

解決できるかな(´・ω・`)