SSG logoSubscribe
Digital art by Anonymous

The App Launch Sequence

There are several phases involved in launching an application, most of which the system handles automatically. The operating system calls methods in your app delegate during the launch sequence, allowing you to prepare your app for user interaction. The system provides some initial instances and an initial interface to run.

The Entry Point

When the app launches, the system knows where to find compiled binary within the app bundle. Thanks to info.plist in the same bundle, this file has an executable file key(CFBundleExecutable) whose value is the name of the app’s binary.

The system loads the binary and links any required frameworks. Now it needs to execute a code from the app’s binary to launch it. But where?

The answer would be obvious if this app were an Objective-C program. Objective-C is derived from C programing language, so the entry point is main function. Objective-C project would have a main.m file containing the main function:


_10
int main(int argc, char *argv[]) {
_10
@autoreleasepool {
_10
return UIApplicationMain(argc, argv, nil,
_10
NSStringFromClass([AppDelegate class]));
_10
}
_10
}

The main function performs two tasks:

  • It creates an environment for memory management, thanks to @autoreleasepool.
  • It executes the UIApplicationMain function, which enables your application to use its bootstraps to set itself up and running.

If our app is Swift program, it has no main function. Rather, Swift has a specific attribute: @main. You can see it in the AppDelegate.swift file where AppDelegate class is defined.


_10
@main
_10
class AppDelegate: UIResponder, UIApplicationDelegate {
_10
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
_10
return true
_10
}
_10
}

Basically, this property handles all of the functionality Objective-C main.m file: it creates an entry point that executes UIApplicationMain to launch the app.

Note: Swift 5.3 introduced the @main term; this attribute was formerly known as @UIApplicationMain.

It would be very uncommon, but you can create a main.swift file like Objective-C main file. Delete the @main attribute and instead use main file. This file, main.swift, gets special treatment, so the name is critical.

This file has also another special ability, it will execute code at the top level of the file, outside any function body. Other swift files, all executable code lives in a function. From a specific angle, computer programs just calls functions in the related files. Swift equivalent of the Objective-C call to UIApplicationMain, like this:


_10
import UIKit
_10
UIApplicationMain(
_10
CommandLine.argc, CommandLine.unsafeArgv, nil,
_10
NSStringFromClass(AppDelegate.self)
_10
)

You are calling the UIApplicationMain function in a iOS application. The main task of your application is this function call. All your application really does is make one massive call to UIApplicationMain.

images/AppStateTransitions

SwiftUI App Life Cycle

By default, when you make a new SwiftUI application, there is no AppDelegate class in the application that is generated. It has been replaced by an struct that conforms to the App protocol. Similar to how SwiftUI creates structs for views, it also use struct for launch process with a @main attribute.


_10
import SwiftUI
_10
_10
@main
_10
struct SwiftUIApp: App {
_10
var body: some Scene {
_10
WindowGroup {
_10
ContentView()
_10
}
_10
}
_10
}

You might need to revert to the previous version of the UIApplicationDelegate ability - possibly to handle push notification registration or or 3rd party library integration(Firebase etc.).

First, create a AppDelegate class that inherits from “NSObject” and conforms to the “UIApplicationDelegate” protocol, like this:


_12
class AppDelegate: NSObject, UIApplicationDelegate {
_12
func application(_ application: UIApplication,
_12
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
_12
configureApp()
_12
return true
_12
}
_12
_12
private func configureApp() {
_12
// Add any configuration steps here.
_12
print("Application launched!")
_12
}
_12
}

After that, you can add a reference to the AppDelegate file in your App.swift file with UIApplicationDelegateAdaptor property wrapper.


_10
@main
_10
struct SwiftUIApp: App {
_10
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
_10
_10
var body: some Scene {
_10
WindowGroup {
_10
ContentView()
_10
}
_10
}
_10
}

App State Transitions

The state transitions can be tracked by implementing UIApplicationDelegate callbacks with AppDelegate or SceneDelegate, it depends on how new the project is.

images/AppStateTransitions

Pre-warming

With iOS15 and later, the system might decide to pre-warm your app before the user tries to open it. This process minimizes the length of time the user has to wait before the app is usable. Pre-warming runs the launch sequence of an application until main() calls UIApplicationMain methods, but not beyond that point. This helps the system to build and cache any low-level structures that are needed.

Once your app is prewarmed by the system, its launch sequence is halted until the app starts or the system kills the prewarmed app from memory in order to reclaim resources. The system can prewarm your app after every device reboot.

If your application runs code before the calling UIApplicationMain methods, such as load(), be careful when assuming which resources and services are available. For instance, keychain items could not be available because their data protection policies need an unlocked device status.

Key Points

  • Entry Point: Objective-C programs have a traditional main function, while Swift programs use the @main attribute or a main.swift file to initiate the application launch sequence.
  • SwiftUI App Lifecycle: SwiftUI apps replace the traditional AppDelegate with a struct conforming to the App protocol, simplifying the initialization process with a @main attribute.
  • Pre-warming: iOS 15 introduced app pre-warming, which initiates parts of the launch sequence to reduce user wait times.
  • Executable File Identification: The system identifies the executable file of an app through the info.plist file, crucial for locating and launching the application's binary.