[SwiftUI] @StateObject と @UIApplicationDelegateAdaptor の初期化順

SwiftUI

SwiftUI 2.0 で導入された StateObject と UIApplicationDelegateAdaptor の初期化順について説明します

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Catalina 10.15.7
  • Xcode 12.2
  • iOS 14.2

@StateObject

@StateObject は、定義した struct と同じライフサイクルを SwiftUI が保証してくれる property wrapper です。

Apple のドキュメントは、こちら

アプリケーションのデータを保持させるとすると、App に準拠した struct に保持させておくのが自然となります。

@StateObject

@main
struct MyApp: App {
    // (1)
    @StateObject var viewModel: MyViewModel = MyViewModel()
    var body: some Scene {
        WindowGroup {
            // (2)
            AppRootView(viewModel: viewModel)
        }
    }
}
コード解説
  1. アプリ(MyApp) と同じライフサイクルで保持したいので、@StateObject 指定しています
  2. 下位のビューにデータを渡しています。(.environment 等で渡す方法もあります)

@UIApplicationDelegateAdaptor

UIKit の時には、delegate に UIApplicationDelegate を指定することができ、さまざまなタイミングでの処理を行うことができました。

Apple のドキュメントは、こちら

SwiftUI で、ライフサイクルに SwiftUI を選択すると、現時点では、アプリケーションのフェーズが変更されたタイミングでの処理しか行うことができません。

そこで、ライフサイクルに SwiftUI が選択されていても、現時点では UIApplicationDelegate を指定することができるようになっています。
その指定に使われるのが、@UIApplicationDelegateAdaptor です。

UIApplicationDelegatAdaptor

@main
struct MyApp: App {
    // (1)
    @UIApplicationDelegateAdaptor(MyAppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            AppRootView(viewModel: viewModel)
        }
    }
}

class MyAppDelegate: NSObject, UIApplicationDelegate {
    // (2)
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        // initialization code here
        return true
    }
}
コード解説
  1. 指定したクラスを UIApplicationDelegate として設定します
  2. UIApplicationDelegate と設定されることで、アプリが起動したタイミングで呼び出されます

@StateObject, @UIApplicationDelegatAdaptor どちらも 初期化に関りますので、どちらが先に初期化されるか調べてみました。

先に記述された方が、先に初期化されるのか? 固定順なのか?

@StateObject と @UIApplicationDelegateAdaptor の処理順

注意
ここに記載していることは、デバッガーを使って確認した振る舞いです。将来的に実装が変更されるかもしれませんので、注意してください。

結論 「@UIApplicationDelegateAdaptor の初期化が先におこなわれる」 でした

理由の推測
おそらく、UIApplicationDelegate の呼び出されるタイミングの1つに、


application(_:willFinishLaunchingWithOptions:)
があります。

これは、アプリ起動直後しかし状態遷移が起こっていないタイミングで呼び出されるものです。

そのために、@StateObject 等の処理を行う前に @UIApplicationDelegateAdaptor を評価する必要があるのではないかと思います。(あくまで推測です)

ちなみに、UIKit のケースでは、上記のメソッドは、Storyboard 等が読み込まれた後に呼び出されると書かれていて、SwiftUI の扱いとは少し異なっていることもわかります。(SwiftUI のケースでは、Storyboard はありませんが、UI を定義している View 等は、まだ読み込まれていない状態です)

まとめ:@UIApplicationDelegateAdaptor と @StateObject の初期化順

@UIApplicationDelegateAdaptor と @StateObject の初期化順
  • @UIApplicationDelegateAdaptor の初期化が先
  • ApplicationDelegate の処理から、Model や ViewModel にデータを渡すには工夫が必要

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です