Exploring the application entry point in modern iOS
Create a new SwiftUI project in XCode 11 to find a file named SceneDelegate, that has a bunch of default iOS declarations, ever wondered what that is?
While we won’t be diving much into the documentation, we’ll have our hands dirty to get this quickly into our brain cells.
Identifying system properties#
Firstly, to facilitate the launch of apps the system uses a property list (Info.plist), this meta data provides a list of key-value pair that provides important identifiers to our system. Mostly used internally to identify requirements, supported devices and system names; but also important to provide critical data to the user, or the system framework or library. This is where the system keeps details about our application scene(s).
Expand the option Application Scene Manifest
> Scene Configuration
> Application Session Role
> Item 0
, where you should easily identify
the SceneDelegate
Class name value, that is assigned to the property Delegate Class Name
.
Take a look into some of the other options, for example, the Enable Multiple Windows
that is disabled. Where otherwise would enable multiple windows for your application on environments such as the iPadOS; This also explains the break from the life-cycle approach we had prior to the release of iOS 13, opening up new possibilities for our applications. Our iPad users can now open up multiple-windows (as we been calling Scene), side-by-side improving user experience and opening up better workflows.
We can get our hands dirty by renaming the SceneDelegate
to something else such as SomethingDelegate
and run the simulator.
Once compiled, the simulator should display a black screen
[SceneConfiguration] Info.plist configuration "Default Configuration" for
UIWindowSceneSessionRoleApplication contained UISceneDelegateClassName key,
but could not load class with name "Test.SomethingDelegate".
Meaning that we can declare any scene delegate
, or by renaming the default Class name SceneDelegate
to something else, such as we did by naming it SomethingDelegate
.
<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).SomethingDelegate</string>
</dict>
</array>
</dict>
</dict>
Responsability#
Prior to iOS 13, the main entry point of our applications was the AppDelegate, that bootstrapped logic and initial state. The responsability is
now split between the AppDelegate
and the Scene delegate
- where one setups up core objects such as the database, networking stack, etc, the other handles the instance of your app user interface.
AppDelegate
The AppDelegate
no longer holds the relation one-to-one with our scenes since the release and support for multiple-windows in iOS 13; Is no longer responsible for handling UI related lifecycle events, such as going to the foreground or background. Meaning that it splits need to execute processes for each instance and instead runs it once for all the existing instances.
application(_:configurationForConnecting:options:)
Returns the configuration data to use when creating a new scene. Called right before a new scene is created.
application(_:didDiscardSceneSessions:)
Tells the delegate that the user closed one or more of the app’s scenes from the app switcher. This method gets called right before a scene is destroyed.
Which means that for our initial application, provided by XCode we can delete much of the template code and just keep the Class name and types.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {}
SceneDelegate
scene(_:willConnectTo:options:)
When the user or your applications requests a new instance of the user interface, a Scene is created and connected to the application. This method can be used to load any data that the sceen requires. This and the other methods are called for a particular scene and not for the whole application!
You can literally delete the remaining methods, apart from scene
method and the UIWindow
object - this is your main window, needed to present your View.
It’s important to understand what each of those methods do, so that you can use it wisely and improve your application. For example, you could use the sceneDidBecomeActive
to refresh the contents of the view, or increase frame rates for your user interface. While in the sceneDidEnterBackground
can be used to free up any shared resources, for example.
import UIKit
import SwiftUI
class SomethingDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: SomethingView())
self.window = window
// this is the key window to look at our app through
// xcode knows the sceneDelegate as first, because its defined in the info.plist
// find it in the scene configuration
window.makeKeyAndVisible()
}
}
}
Now everytime you start a new project, you should be a bit more comfortable about exploring the entry point the same way we did here and use the Apple documentation for each of the properties, methods, or features you want to dive deeper into.
Feel free to look at the github repository for this article code here