SwiftUI app life cycle

Ario Liyan
8 min readSep 11, 2022

--

In this journey, we will achieve the needed base knowledge about the app life cycle in SwiftUI applications and how we can respond to them.

Table of contents

  • Quick Recap: The App Delegate — UIKit applications
  • Let’s get to the meat: life cycle in SiwftUI applications
  • Supplement knowledge

Quick Recap: The App Delegate — UIKit applications

Every application in iOS has a starting point(think of it as a function that is called by the operating system when your app is launching) which is kind of a connection route between the application and the operating systems(on most platforms the function is called “main”).

Prior to iOS 14(almost with the introduction of SwiftUI — to be exact from SwftUI 2.0) iOS apps had a class named AppDelegate that the creation of this class was the starting point of your app. Technically talking, iOS creates an instance of UIApplication class and then assigns a created instance of your app delegate class to it(as delegation). Because of existence of this delegation you then could customized what you want to happen when your app launches. This was how we were able to execute different functions in different steps in the app cycle.

Note: based on your app, the life cycle can be important or not important at all.

Let’s get to the meat: life cycle in SiwftUI applications

  • Explaining the code
  • App Configuration
  • Executing codes in the scene lifecycle event
  • I want my beloved App Delegate back
  • The swift way of interacting with the app phases

From iOS 14 the app protocol was introduced in order to purify the SwifUI apps and replace scene delegate(don’t worry about this) and app delegate so it can take over many of its functions(so the SwiftUI app doesn’t have any app delegate anymore).

Now when you create an app instead of seeing the AppDelegate file you’ll find a AppStruct with a code like bellow:

The code given above initializes our app with a view called ContentView

Explaining the code

  • @Main
  • struct SampleApp: App
  • var body: some Scene
  • Notes
  • what is a “Scene”
  • WindowGroup

@Main
This property wrapper is declared to indicate that this struct is the entry execution point of the app(and starts the flow of the application). Somehow it’s a fancy way of saying that our app execution starts from this struct.

struct SampleApp: App
This line indicates that this structs conforms to the App protocol and launches the app views.

Notes:

  • according to SwiftUI documentation the app Protocol provides a default implementation of the main() method that the system calls to launch the app.
  • You can only have one entry point among all of your app’s files.

var body: some Scene
This “var body” is required so that the struct can conform to the App protocol. The body itself conforms to Scene protocol.

Each scene contains the root view of a view hierarchy and has a life cycle managed by the system. SwiftUI provides some concrete scene types to handle common scenarios, like for displaying documents or settings. You can also create custom scenes.
Documentation

Notes
“some” keyword: The some keyword was introduced in Swift 5.1 and is an improvement in generics world. The keyword is used together with a protocol to create something that is called an “opaque type”.

An opaque type is a way to return a type without needing to provide details on the concrete type itself(The opaque type represents something that is conformed to a specific protocol). It limits what callers need to know about the returned type, only exposing information about its protocol compliance.

When we use an opaque type, its exact type is going to be calculated by the compiler based on the actual returned value (the same as using the angle brackets or a trailing where clause at the function signature. for more search about generics).

So we use the somekeyword on a variable, we are telling the compiler that we are working on a specific concrete type, thus the opaque type’s underlying type must be fixed for the scope of the variable.

And in SwiftUI's case, “some View” means that the body will always be implementing the View protocol, or here means it will always be implementing scene protocol.

what is a “Scene”?

A scene acts as a container for a view hierarchy that you want to display to the user. The system decides when and how to present the view hierarchy in the user interface in a way that’s platform-appropriate and dependent on the current state of the app. They can look different depending on the platform the app is running on.
Documentation

For example, in iOS, the screen usually only displays one scene at a time. In macOS, every window in an app might be a different scene.

WindowGroup
This is a cross-platform struct that represents a scene of multiple windows. You can use it on macOS, iOS, etc. It’s the container for your apps view hierarchy. Within the WindowGroup, you declare the first view (“User Interface”) for your app.

App Configuration

One of many ways that we relied on App delegate before and now on SwiftUI app struct is for configurating our app. In app delegate we had lots of different functions that each was called in different phase of the app and whenever we need to execute something in a particular state of the app we simply wrote the code in the related function. but what about in swiftUI struct?

We’ll continue with a common example of running a firebase app(if you don’t know Firebase is no worry it’s only for a sake of example and you can later search about it).

In our case and many others there, are scenarios that we want to configure or run something before our app initialization is done, in these cases we can simply put our code within the AppStruct init function as shown bellow:

Executing codes in the scene lifecycle event

  • @Environment(\.scenePhase) private var scenePhase
  • .onChange(of: scenePhase) { phase in

There are scenarios that we may need to respond to some lifecycle changes(for example we want to call a function for when our app become minimized by the user). For those cases we can use the code bellow

@Environment is a collection of environment values propagated through a view hierarchy.

@Environment(\.scenePhase) private var scenePhase
Here we are using scenePhase variable from our environment variables and assign it to a private one within our struct and as the scenePhase is a state variable in its essence, we can track its changes when occurred by allocating an observer to it. (if you don’t know about observer design pattern don’t worry, it is basically a function that is called when a variable changes, for more search for observer design pattern).

.onChange(of: scenePhase) { phase in
This is a function with a closure(if you don’t know what closures are read this Link). Within the closure is given to us the variable that we sent to the function and now the function is going to look on the sent variable and when ever observes a change it’ll send the new value to the closure and execute the code nested in the closure. Here we check the variable for if the app is put in the background.

Note: @Environment(\.scenePhase) has three states, given bellow.

iOS is in control of these phases when we say the app is going to the background, our app can be running at the background and it also can be quitted because iOS needs resources or the user doesn’t use it for a while etc… .

I want my beloved App Delegate back

  • @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

So if you are not yet ready to let the App Delegate go, there is a good news for you, we still can use the App Delegate in our SwiftUI app Through @UIApplicationDelegateAdaptor property wrapper!(don’t worry about property wrapper if you don’t know them. you can always search about it later 🥸)

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
In the code above we simply ordered the App Struct to create an instance of AppDelegate and assign it to appDelegate property, and consider it as the app delegate object of our application and all this happens with this line of code.

Note: bellow the app struct we have the APPDelegate class, so it’s obvious that we have access to all its functions(good old ways 🥸). There are functionalities that are not available in the App Struct like remote push notifications, but now we can use them through this method.

The swift way of interacting with the app phases

1.First we are going to create a singleton so we can have access to its shared instance through our app.

2.Second we are going to define a scenePhase variable, look for its changes and execute related actions.

Supplement knowledge

main() function:

Framework providers in apple gave us a default implementation of “main()” function.

As you can see the function is implemented in an extension. Since SwiftUI framework is closed source, we can’t see how apple implemented this. But there are couple of open source alike codes that we can see how they implemented the same thing. swift-argument-parser is one of them.

Let’s see the implementation of main() (in swift argument parser)

--

--

Ario Liyan

As an iOS developer with a passion for programming concepts. I love sharing my latest discoveries with others and sparking conversations about technology.