NotificationCenterを使用して、アプリの状態を認識する

エンジニアのことエンディニアっていうやつだいたい癖強い。
どうもこんにちはTOSHです。

中高生にプログラミングを教えていると、文化祭用にアプリを開いた状態で時刻を取得するアプリを作りたいと言われました。
基本的には、そこら辺の動作はAppDelegateのなかで管理しやすいのかなと思ったけど、通常のViewControllerの中でも管理できるのかなということでやっていきます。

今回はNotificationCenterを使用して、アプリのフォアグラウンド状態、バックグラウンド状態を認識する方法を紹介していきたいと思います。
何かおかしい事等あったら、ご指摘お願いします。

# アプリの状態について
基本的に大きく分けて、状態としては2つ。
・フォアグラウンド：画面上で動いている状態。
・バックグラウンド：画面上にはそのアプリは動いていないが、裏ではコードが動いている状態。

さらに、フォアグラウンドには、inactiveとactiveな状態があるようですね。
それぞれ、
・inactive：スクリーン上で動いているが、イベントを受け取っていない状態
・active：スクリーン上で動いているし、イベントを受け取っている状態

これらを図にすると以下のような感じ
![00b28327-17dc-4f0c-866f-29f854edfce3.png](https://qiita-image-store.s3.amazonaws.com/0/180994/28fb57b9-615c-1e41-03ce-54a0e247e32c.png)
*Appleの公式ページより参照

少し前に、使われた画像も参考までに
![high_level_flow_2x.png](https://qiita-image-store.s3.amazonaws.com/0/180994/64b706e7-4e01-98ce-5e48-a5488c4c17c6.png)
*こちらも同様にAppleのページより参照

という事で、今回はアプリを開いた時の状態を認識したいので、backgroundからACtiveになる状態を認識する。

#実装方法
ネットでググってみると`NSNotificationCenter`を使用しているコードは多数見つかるものの、なかなか、`NotificationCenter`を使用しているコードは見つからなかったので、まとめておきます。
少し文法が変わっていたので、参考になればと思います。

```swift
//NotificationCenterを定義
let notificationCenter = NotificationCenter.default

//observerを追加        
notificationCenter.addObserver(
    self,
    selector: #selector(呼び出したいメソッド名),
    name: UIApplication.ここに変化を感知させたい状態を書く,
    object: nil
)
```

よく使用しそうなNotificationのみを抜粋して一覧にしておきます。

|UIApplication.  |説明  |
|---|---|
|didBecomeActiveNotification  |アプリがActiveになるときに投げられる  |
|didChangeStatusBarFrameNotification|ステータスバーのフレームが変更されるときに投げられる|
|didEnterBackgroundNotification|アプリがバックグラウンドに入るときに投げられる|
|didFinishLaunchingNotification|launch(起動）が終わった直後に投げられる|
|willTerminateNotification|アプリがterminate(終了)する直前に投げられる|
|willEnterForegroundNotification|バックグラウンドから離れ、アプリがアクティブになるちょっと前に投げられる|
|willResignActiveNotification|Activeではなくなるときに投げられる|
詳しくは、[UIApplication](https://developer.apple.com/documentation/uikit/uiapplication)を参照してください。

意外と、読んで見ても違いがわかりずらいものもあったりするので、実際に動かしてみるといいと思います。
今回は、一番上の`didBecomeActiveNotification`を使用してみます。

全体のコードは以下の感じ

```swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var currentTimeLabel: UILabel!
    
    var currentTime: Date!
    let formatter = DateFormatter()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //現在時刻を取得
        currentTime = Date()

        //NotificationCenterを定義
        let notificationCenter = NotificationCenter.default

        //Observerを追加
        notificationCenter.addObserver(
            self,
            selector: #selector(self.currentTimeUpdate),
            name: UIApplication.didBecomeActiveNotification,
            object: nil
        )
        
        currentTimeLabel.text = dateFormatting()
    }

    //observerが発火したタイミングで呼ばれるメソッド
    @objc func currentTimeUpdate() {
        currentTime = Date()
        currentTimeLabel.text = dateFormatting()
    }

    //Date型をString型に変更するための処理
    func dateFormatting() -> String {
        //今回は、時、分、秒で変換している
        formatter.setLocalizedDateFormatFromTemplate("jms")
        
        return formatter.string(from: currentTime)
    }
}
```

実際に動いているのがこちら、
![gif](https://media.giphy.com/media/1zJDNYSkyr5yuuyAAA/giphy.gif)
ちゃんと、アプリを開いたら時刻が変化していますね。

# GitHub
今回のサンプルコードをGitHubの方にあげておきました、参考までにどうぞ！
[GitHub](https://github.com/tosh7/StateChangeSample)

#参考にしたページ
[Managing Your App's Life Cycle](https://developer.apple.com/documentation/uikit/core_app/managing_your_app_s_life_cycle)
[アプリの状態が変わったタイミングで処理を行わせる実装](https://qiita.com/musclemikiya/items/bea909499aa543400218)
[アプリの状態とマルチタスキング](https://gist.github.com/shinyaohira/5975781)
[The App Life Cycle](https://developer.apple.com/library/archive/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html)