ITS
iPhone App Development
Besides SwiftUI, in 2019, Apple introduced the concept of multiple windows, where each window, more appropriately called scene, represents a UI instance of our app.
Fast forward one year later, and SwiftUI has a brand new life-cycle, dropping both UIKit’s app and scene delegates entirely. Here’s what’s left:
@main
struct FSApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
While the SwiftUI team keeps moving forward at a neck-breaking speed, not all third party libraries and, admittedly, not even all Apple’s API, are ready for the big shift:
to this day, many APIs still require either an AppDelegate or an UIScene to operate.
Does it mean that we can’t use the new SwiftUI life-cycle? No! The SwiftUI team has thought about these scenarios and provided us with the right tools. Let’s dig in.
Along with the new SwiftUI life-cycle, the @UIApplicationDelegateAdaptor property wrapper has been introduced, letting us associate an app delegate to a SwiftUI app.
First, let’s define our UIApplicationDelegate:
class FSAppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// …
return true
}
}
@main
struct FSApp: App {
@UIApplicationDelegateAdaptor var delegate: FSAppDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
SwiftUI will both initialize and manage our delegate lifetime. No further work is necessary.
Environment is one of the most powerful SwiftUI features, if we conform our app delegate to ObservableObject, it will be accessible anywhere in our app:
class FSAppDelegate: NSObject, UIApplicationDelegate, ObservableObject { // 👈🏻
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// …
return true
}
// …
}
With just this change, we can access our app delegate like any other environment object:
struct ContentView: View {
@EnvironmentObject var appDelegate: FSAppDelegate
var body: some View {
Button(“Tap me”) {
// appDelegate. …
}
}
}
Besides receiving the app life-cycle events, we can add to our app delegate any @Published properties, and views depending on it will produce a new body whenever changes are published, for example:
class FSAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
@Published var date: Date = .now // 👈🏻
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// 👇🏻 Publishes every second
Timer
.publish(every: 1, on: .main, in: .default)
.autoconnect()
.assign(to: &$date)
return true
}
}
struct ContentView: View {
@EnvironmentObject var appDelegate: FSAppDelegate // 👈🏻
var body: some View {
Text(appDelegate.date.formatted(date: .omitted, time: .standard))
}
}