ViewControllerが表示されるイベントログを横断的に取得する - Activity Tracking

## ユーザーのアクティビティログを取る(すべてのviewDidAppearなど)
各ViewControllerが呼ばれたタイミング（viewDidAppearなど）でトラッカーを呼び出し、ユーザーの行動ログをとりたいときがあるかと思います。
しかし、すべてのViewControllerのviewDidAppearにその処理を書き込むことはあまり現実的ではありません。

検討する上ではだいたいこのような視点があると思います。

- 実装コストを減らしたい、１度きりの実装で済ましたい
- ログの漏れをなくしたい（特定のケースで取れてないことを防ぎたい
- ログのせいでバグを起こしたくない
- 将来的にも使い続けられる（言語のバージョンアップデートに左右されにくい

## 方法例

5. NSNotificationを使う
1. アスペクト指向ライブラリを使う
3. Base Classを作る
4. 独自の遷移メソッドをつくる
2. File templateを使う

## NSNotificationを使う
非公開のNSNotificationの中に`UINavigationControllerDidShowViewControllerNotification`というものがあります。
これは、UINavigationControllerDelegateの[navigationController:didShowViewController:animated](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationControllerDelegate_Protocol/index.html#//apple_ref/occ/intfm/UINavigationControllerDelegate/navigationController:didShowViewController:animated:)が呼ばれた際に飛んでいます。
`UINavigationControllerNextVisibleViewController`というkeyで、表示されるViewControllerが取得できます。

```swift
// nameをnilにすると、すべてのnotificationを受け取れる
NSNotificationCenter.defaultCenter().addObserverForName(nil, object: nil, queue: nil) { (notification) in
    if notification.name == "UINavigationControllerDidShowViewControllerNotification",
        let userInfo = notification.userInfo as? [String: AnyObject],
        let viewController = userInfo["UINavigationControllerNextVisibleViewController"] as? UIViewController {
        debugPrint(viewController) // 表示されるViewControllerが取れる
    }
}
```

***いいところ***
ログの実装を完全に分離でき、また下手な影響を及ばす心配がありません。

***気になるところ***
UINavigationControllerを介さずに表示される場合のケースが取れないです。
また、非公開APIなのでいつ使えなくなるかは不明です。


## アスペクト指向ライブラリを使う
横断的関心事を分離して実装していけるアスペクト指向のライブラリを使う手段です。
以下の様なライブラリがあるようです。

- [Aspects](https://github.com/steipete/Aspects)
- [MOAspects](http://qiita.com/moaible/items/efcd43016b9320cf6d63)

以下の記事も参考になりましたm(_ _)mありがとうございます
[iOSのトラッキング実装ベストプラクティスを考える](http://qiita.com/horimislime/items/71702594363b17483567)
[妄想iOSアプリ新規開発](https://www.wantedly.com/companies/wantedly/post_articles/33179)の記事内の「トラッキング（横断的関心事）にあるの織り込み」

***いいところ***
網羅的に実装できます。実装コード量も少なく、コードが分離できているので管理も楽です。

***気になるところ***
ライブラリなので、もしサポートが足りない場合は修正を送るかforkしていく必要があります。（ただソース量が少ないので、自己管理下に置きやすそうではあります）
swizzleの黒魔術感があります。

## Base Classを作る
すべてのUIViewControllerに継承させるBaseViewControllerを用意して、その中でトラッキングする処理を書くというものです。

```swift
class BaseViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        // ログ処理
    }
}

// 継承させることでログ処理を走らせる
class SampleViewController: BaseViewController {}
```

***いいところ***
この実装方法が打ち切られることはまずなさそうです。

***気になるところ***
新規にViewControllerを追加するときに必ず継承させることを意識する必要があります。
中で条件が書かれるようになるとしんどくなっていくと思います。

参考：[iOSアプリの設計でBaseViewControllerのようなのは作りたくない](http://qiita.com/yimajo/items/8ef1850e1362bd3bf3d8)

## 独自の遷移メソッドをつくる
ViewControllerが表示される方法としては、コードまたはStoryBoardで使われている[メソッドたち](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/#//apple_ref/occ/instm/UIViewController/showViewController:sender:)がありますが、それを直接利用せずにそれぞれのメソッドをwrapしたメソッドを用意します。

```swift
extension UIViewController {
    func presentViewControllerWithLogging(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?) {
        // ログ処理
        self.presentViewController(viewControllerToPresent, animated: animated, completion: completion)
    }
}
```

***いいところ***
どのメソッドを通して開かれたのを知りたいときはいいかも、、？

***気になるところ***
抜け漏れがないように絶対このメソッドを使うようにさせるのが辛そうです。

## [File template](https://robots.thoughtbot.com/creating-custom-xcode-templates)を使う
Xcodeで、新しいファイルを追加する際に、customで用意したFileTemplateを選択することができます。
そこに、トラッキングする処理が書かれた状態のUIViewControllerのテンプレートを用意してしまうという方法です。


***いいところ***
コード編集を毎回意識することになるので、影響コントロールはしやすいかもしれません。

***気になるところ***
必ずカスタムテンプレートを使うことを意識する必要があります。
コードがあまりきれいに見えなさそうです。

## まとめ
選ぶ判断は自分たちの設計方針、なにをメリデメにするかで変わってくると思いますので、そこに照らしあわせて選ぶことがよいと思います。