用 UIApplicationDelegateAdaptor 在 SwiftUI 處理 UIKit 的 AppDelegate。
在 SwiftUI 中使用 UIKit 的 AppDelegate
在以 SwiftUI 為主要框架的專案中,如果要使用 UIKit 的 app delegate,要先定義一個遵從 UIApplicationDelegate protocol 的 class,並在裡面實作我們需要用到的 delegate methods。舉例來說,我們可以實作 application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
在成功註冊 Apple Push Notification service(APNs)時做某件事。
class MyAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
// Record the device token.
}
}
在定義完 AppDelegate 之後,就可以在 App 底下,使用 UIApplicationDelegateAdaptor property wrapper 來告訴 SwiftUI 我們的 AppDelegate 型別:
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor private var appDelegate: MyAppDelegate
var body: some Scene { ... }
}
這裡可能會覺得奇怪,因為我們並沒有在宣告時就 init AppDelegate,而只告訴它型別,其實 SwiftUI 會自己建立 AppDelegate,並在對應的生命週期裡去呼叫 delegate methods。
注意
盡可能不使用 UIKit 的 app delegate 來處理 app 的生命週期事件,而應優先使用 SwiftUI 自身提供的。如:優先使用 ScenePhase
,而非使用 delegate callback application(_:didFinishLaunchingWithOptions:)
。
ObservableObject:iOS 14.0+
如果我們定義的 AppDelegate 像上面的例子一樣遵從了 ObservableObject protocol,那 SwiftUI 就會把這個 delegate 放進 Environment 裡,我們可以在任何 Scene、任何 View 裡,用 Environment property wrapper 去取得 AppDelegate:
@EnvironmentObject private var appDelegate: MyAppDelegate
如果我們在自己定義的 AppDelegate 中有用到 @Published 的話,就可以直接使用錢字號($)來拿到 binding。
Observable:iOS 17.0+
相對於 ObservableObject,Observable 是比較新的 protocol,效果一樣,但不再需要使用 @Published。要從 Environment 中拿到遵從 Observable 的 AppDelegate 方法如下:
@Environment(MyAppDelegate.self) private var appDelegate
我們也可以直接使用錢字號($)來拿到 AppDelegate 中變數的 binding。
Scene delegates
有的 iOS app 會自己定義遵從 UIWindowSceneDelegate 的 SceneDelegate 來處理 Scene 相關的事件,如:app shortcuts。
class MySceneDelegate: NSObject, UIWindowSceneDelegate, ObservableObject {
func windowScene(
_ windowScene: UIWindowScene,
performActionFor shortcutItem: UIApplicationShortcutItem
) async -> Bool {
// Do something with the shortcut...
return true
}
}
我們可以藉由在 AppDelegate 裡的 application(_:configurationForConnecting:options:)
方法中回傳自己定義的 SceneDelegate 型別給 SwiftUI。
extension MyAppDelegate {
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
let configuration = UISceneConfiguration(
name: nil,
sessionRole: connectingSceneSession.role)
if connectingSceneSession.role == .windowApplication {
configuration.delegateClass = MySceneDelegate.self
}
return configuration
}
}
在這個 func 裡設定 UISceneConfiguration,我們只需告訴 SwiftUI 自己定義的 SceneDelegate 型別,而不需 init,SwiftUI 會自己 create 並管理 SceneDelegate,處理任何相關的 delegate callback。
如果我們讓 SceneDelegate 也是個 observable object,那就和 AppDelegate 一樣,SwiftUI 會把它放進 Environment 裡,我們便可在任何地方,用 Environment property wrapper 去取得並拿到 binding。
關於 XcodeProject
XcodeProject 創立於 2023,致力於協助開發者探索 Apple 的創新世界,學習在 iOS、iPadOS、macOS、tvOS、visionOS 與 watchOS 上開發 App,發現眾多技術與框架,讓開發者獲得更多能力。