【SwiftUI】アプリの起動状態を取得する方法【iOS】

初投稿です！
SwiftUIでiOSアプリを開発していて、アプリの起動状態を取得する方法を調べるのに苦労したので備忘録も兼ねてこの場で共有させていただきます。

# iOSアプリにおける起動状態について
iOSアプリの起動状態はActive, Inactive, Backgroundの3つの状態に分けることができます。
状態遷移図は下図の通りです。起動するときはInactiveを経由してからActiveになることや、ActiveからBackgroundになるときもInactiveを経由することが分かると思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/444d0efb-db3d-416c-30e5-442b6be47211.png" width="500"></img>

詳しくは<a ref="https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle">Apple公式ドキュメント</a>を参照してください。

# SwiftUIにおける取得方法
最近までSwiftUIではアプリの起動状態を取得するAPIが用意されていませんでしたが、iOS14からは<b>ScenePhase</b>を利用することで簡単に取得することができるようになりました。

具体的には、Appインスタンスに以下のプロパティを追加することで取得が可能になります。

~~~swift
@Environment(\.scenePhase) private var scenePhase
~~~

例を見てみましょう。Xcodeで新規プロジェクトを立ち上げたときに自動生成されるTestApp.swiftを以下のように書き換えます。(この場合Testがプロジェクト名となります)

~~~swift
//
//  TestApp.swift
//  Test
//
//  Created by Ossamoon on 2020/12/25.
//

import SwiftUI

@main
struct TestApp: App {
    @Environment(\.scenePhase) private var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("バックグラウンド状態になりました")
            case .active:
                print("アクティブ状態になりました")
            case .inactive:
                print("非アクティブ状態になりました")
            default:
                print("このメッセージがでたら何か変だよ")
            }
        }
    }
}
~~~
アプリをシミュレーターで起動してみましょう。すると、コンソールに「アクティブ状態になりました」というメッセージが現れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/c9143e0f-ccf6-3cd5-3823-c7d9ea5c40d9.png" width="200"></img>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/236f27e7-9baf-fc68-f32f-ffcc05122f29.png" width="500"></img>

ここからスワイプでメニューを表示すると、「非アクティブ状態になりました」というメッセージが現れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/fa6a99cd-1ea9-5b99-b745-5121ca0481b2.png" width="200"></img>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/8dd9e2e9-2b2e-90eb-6286-c15399ef9eca.png" width="500"></img>

さらに他の画面に移動すると、「バックグラウンド状態になりました」というメッセージが現れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/e591f1da-9854-036d-fe2c-a3d18dd7ae53.png" width="200"></img>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/c1124b47-3984-a585-d663-bdf09f9dedcf.png" width="500"></img>

ここで再びアプリの画面に戻ると、「非アクティブ状態になりました」というメッセージに続き「アクティブ状態になりました」というメッセージが現れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/b76d3dc8-1903-1ff6-05b2-d1ca0df477ee.png" width="200"></img>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961672/b7107847-1f80-9e79-d426-8b92ca600dc0.png" width="500"></img>

こんな感じの挙動を示します。実際に皆さんの手元で色々いじってみていただけたらと思います。

# まとめ

ScenePhaseを使うとアプリの起動状態を簡単に取得できることがお分かりいただけたかと思います。

iOS14以降でないと使えないという制約はありますが、とても便利なAPIなので皆さんもぜひScenePhaseをお試しください！

# 参考文献

<ul>
<li><a href="https://developer.apple.com/documentation/swiftui/scenephase">ScenePhase | Apple Developer Documentation</a></li>
<li><a href="https://medium.com/@prafullkumar77/swiftui-how-to-detect-if-the-application-in-background-or-foreground-20bc4c8d18a">SwiftUI: How to detect if the application in background or foreground? - Prafulla Singh - Medium</a></li>
<li><a href="https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle">Managing Your App's Life Cycle | Apple Developer Documentation</a></li>
</ul>