Xcode 11 を使って iOS 13 向けに作成されたSwiftUI のアプリだと AppDelegate と SceneDelegate のライフサイクルでアプリが作成されている。
Xcode 12 以降だと、プロジェクト作成時に SwiftUI のアプリのライフサイクルが選べるようになっている。
選べるのは、以下だ。
- SwiftUI App(iOS 14以降有効)
- protocol App を使う
- UIKit App Delegate (いままでの)
既存の IUIKit App Delegate
で作成した SwiftUI のアプリを iOS 14 以降の SwiftUI App
のライフサイクルに変更する手順を以下にメモする。(iOS 13 は動作対象外になることに注意)
Info.plist
- Info.plist の
UISceneConfigurations
の部分を削除する。
こうなっていたのが
<key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <false/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict> <key>UISceneConfigurationName</key> <string>Default Configuration</string> <key>UISceneDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> </dict> </array> </dict> </dict>
削除すると、こうなる。
<key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <false/> </dict>
アプリのメインエントリを App プロトコルに変更
App から派生した struct を定義したファイルを作る。
WindowGroup に最初に始まる View を設定する。
import SwiftUI @main struct TestApp: App { var body: some Scene { WindowGroup { ContentView() // 最初に表示される View } } }
既存の AppDelegate.swift, SceneDelegate.swift はプロジェクトから外して、ビルドされないようにする。
特に AppDelegate の @UIApplicationMain
と@main
が両方存在すると、メインエントリが重複してしまいエラーになることに注意。
この状態でビルド→実行で、アプリ起動して View が表示されるところまで確認できる。
以下、Info.plist の UIApplicationSupportsMultipleScenes
についての注意点。
既存のライフサイクルのアプリを、あたらしいアプリに置き換える際に UIApplicationSupportsMultipleScenes
がもともと false の場合、インストール済みで起動しているアプリをあたらしいライフサイクルのアプリで上書きして実行すると、最初の View が表示されず黒い画面のままになる現象を確認した。(一度前のアプリを停止させてれば、画面は表示される)
UIApplicationSupportsMultipleScenes がもともと false で、ライフサイクル変更と共に true にして上書きインストールすると、上記の現象は起きなかった。
この時 UIApplicationSupportsMultipleScenes を変えるとアプリの動作にも影響があるため、変更する場合は気を付けたい。
UIApplicationSupportsMultipleScenes
既存の AppDelegate で実行している処理を新しいライフサイクルでも実現
AppDelegate
の didFinishLaunchingWithOptions
で初期化処理をしていたとする。たとえば Firebase とか。
class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Fireabase を初期化する FirebaseApp.configure() return true } }
これが新しいライフサイクルでも呼ばれるようにするには UIApplicationDelegateAdaptor というプロパティラッパーを使用して、従来の UIKit の UIApplicationDelegate
の機能も利用できるようにする。
import SwiftUI @main struct TestApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() // 最初に表示される View } } } class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Fireabase を初期化する FirebaseApp.configure() return true } }