この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
Xcode 12からAppDelegate.swiftとSceneDelegate.swiftファイルを使わないアプリ起動が選べるようになりました。
また、Swift5.3からアプリのエントリーポイントを指定できる@main属性が追加されました。
SE-0281: @main: Type-Based Program Entry Points
今回は、そんなアプリの起動まわりで変更になった部分を、簡単にではありますがまとめてみました。
変更になった部分
Xcode 12でプロジェクトを新規作成するとLifecycleという項目が追加されています。
こちらの選択肢ですが、InterfaceをSwiftUIにするとUIKit App Delegateの他にSwiftUI Appが選べるようになります。
生成されるファイルを比べてみる
それぞれ選択した際に初期に生成されるファイルが違います。
UIKit App Delegate の時は、旧来通りのAppDelegate.swiftとSceneDelegate.swiftファイルが生成されるのに対し、
SwiftUI Appの場合は<プロジェクト名>App.swiftファイルが生成されます。
info.plistを比べてみる
それぞれ生成されたプロジェクトのinfo.plistを比べてみると、Application Scene Manifest という項目の内容が違っています。
SwiftUI Appの場合はEnable Multiple Windows が YES なのに対し、UIKit App Delegateの方は NO になっており、Scene Configurationという項目が追加されています。
試してませんが、info.plistを書き換えることで、最初に選んでないLifecycleの方に変更できそうな感じですね...
コードを比べてみる
次に、それぞれ生成されたコードを見てみます。
UIKit App Delegate (今まで通り)
AppDelegate
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
SceneDelegate.swift
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
@UIApplicationMain が @main になっています。また、SwiftUIの場合はSceneDelegateのfunc scene(_ ... で対象のViewをUIHostingControlleでラップしています。
SwiftUI App
次に、SwiftUI Appを選択した時の<プロジェクト名>App.swiftファイルの中身を見てみます。 ここではLifeCycleSwiftUIというプロジェクト名にしています。
LifeCycleSwiftUIApp.swift
import SwiftUI
@main
struct LifeCycleSwiftUIApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
短くてシンプルになっている印象です。
SwiftUI Appで起動時の処理などが必要な場合
ScenePhaseを使う方法とUIApplicationDelegateAdaptorを使う方法の2パターン存在する様です。
ScenePhase
@Environment(.scenePhase)を追加することにより、アプリのアクティビティ状態を取得することが出来ます。
import SwiftUI
@main
struct LifeCycleSwiftUIApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { scene in
switch scene {
case .active:
print("scenePhase: active")
case .inactive:
print("scenePhase: inactive")
case .background:
print("scenePhase: background")
@unknown default: break
}
}
}
}
UIApplicationDelegateAdaptor
@UIApplicationDelegateAdaptorを使用することで、既存のAppDelegateを利用することができるようになります。
import SwiftUI
@main
struct LifeCycleSwiftUIApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
return true
}
// 必要に応じて処理を追加
}
さいごに
起動時の処理がシンプルなアプリに関しては、LifecycleをSwiftUI Appにするとコードが少なくなって楽だなと思いました。 逆に色々と複雑な対応が必要なアプリに関しては、UIKit App Delegateの方で作った方が良い場合もありそうな印象を受けました。