State Restoration in SwiftUI
Learn how to use SceneStorage in SwiftUI to restore iOS app state. By Tom Elliott.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
State Restoration in SwiftUI
30 mins
- Getting Started
- Understanding Scene Storage
- Saving State
- Restoring All The Things
- State Restoration and the Navigation Stack
- Restoring Characters
- Handling Scene Changes
- Recognizing That Books Are Unique
- Updating the Scene Change
- Understanding Active Users
- Adding Window Dressing
- Where to Go From Here?
In almost any app imaginable, some state will be defined by actions the user has taken — the tab the user last selected, the items the user added to a basket or the contents of a draft message.
It would be confusing for users if they were to stop using your app for a short while, then come back to find the state lost. But this is the default behavior for iOS apps. Why? Because after a user puts an app into the background, the operating system may choose to terminate it at any time. When this happens, the system discards in-memory state.
There is a feature in iOS where the operating system can restore state when an app is re-launched. This is known as state restoration.
In this tutorial, you’ll learn:
- How to add state restoration to your SwiftUI apps.
- The
@SceneStorage
property wrapper for saving state of simple data. - Using
NSUserActivity
to pass state when launching an app.
Time to get started!
Getting Started
Download the project materials by clicking the Download Materials button at the top or bottom of this tutorial. The materials contain a project called Hawk Notes. This tutorial builds an app for making study notes for the Shadow Skye series, a trilogy of epic fantasy books.
Open the starter project. Now, build and run the app using Product ▸ Run from the toolbar or by clicking the Run arrow at the top of the Project Navigator. Once running, the app displays four tabs: one for each of the three books and a fourth for your favorite characters.
Within each of the first three tabs, you'll see four things:
- An overview of the book.
- A link to view it on Amazon.
- A list of study notes you can make about the book, which is currently empty.
- A list of the book's main characters.
Tap a character, and the app navigates to a character detail screen. This screen contains a synopsis for the character as well as a list of notes you can add about that character. You can also tap the heart to mark this character as one of your favorites.
Finally, tap the Favorites tab. There, the app lists all the characters, split into two sections: one for your favorites and another for all the others.
Switch back to Xcode and take a look around the code. Open ContentView.swift. This is the entry point into the app proper. Notice how it defines a BookModel
environment object. This model contains the information for each book and is the primary data source for the app. The content view itself displays a tab view with the four tabs from above — one for each book plus the favorites tab.
Next, open BookView.swift. This is the view for displaying a book. The view comprises a vertical stack containing an overview, a link to view the book on Amazon, a list of notes and finally, a list of characters for this book.
Next, open CharacterView.swift. Here, a ScrollView
contains a VStack
showing views for the character's avatar, a toggle switch for marking the character as a favorite, a synopsis for the character and finally, the notes for the character.
Finally, open FavoritesView.swift. This view shows a list of all the main characters for the three books split into two sections: first, a list of your favorite characters, and secondly, a list of all the other characters.
Switch to the Simulator and select the third tab for The Burning Swift. Now, put the app in the background by selecting Device ▸ Home. Next, switch back to Xcode and stop the app from running by selecting Product ▸ Stop in the menu. Build and run the app again.
Once the app restarts, note how the third tab is no longer selected. This is an example of an app that doesn't restore state.
It's time to learn a little more about how a SwiftUI app's Scene works now.
Understanding Scene Storage
In SwiftUI, a Scene
is a container for views that have their lifecycle managed by the operating system. All iOS apps start with a single Scene
. Open AppMain.swift, and you can see this for yourself.
// 1
@main
struct AppMain: App {
private var booksModel = BooksModel()
// 2
var body: some Scene {
WindowGroup("Hawk Notes", id: "hawk.notes") {
ContentView()
.environmentObject(booksModel)
}
}
}
In the code above, which is already in your app:
-
AppMain
is of typeApp
. The@main
attribute signals to the runtime that this is the entry point for the entire app. - The
body
property for anApp
returns aScene
that acts as the container for all views in the app.
To make state restoration really easy, Apple provides a new attribute you can add to a property: @SceneStorage
.
SceneStorage
is a property wrapper that works very similarly to the State
property wrapper, which you may have used already. Like State
, your code can both read and write to a property attributed with @SceneStorage
, and SwiftUI automatically updates any parts of your app that read from it.
SwiftUI also saves the value of properties attributed with @SceneStorage
into persistent storage — a database — when the app is sent to the background. Then, it automatically retrieves and initializes the property with that value when the app enters the foreground again.
Because of this, SceneStorage
is perfect for adding state restoration to your apps.
It really is that simple! So let's now start coding.
Saving State
It's time to add some state restoration goodness to the Hawk Notes app. Open ContentView.swift.
Near the top of the view, find the line that defines the selected tab for the app:
@State var selectedTab = ""
Update this line to use the SceneStorage
property wrapper like so:
@SceneStorage("ContentView.CurrentTab") var selectedTab = ""
With this change, you've updated the selectedTab
property to use SceneStorage
— rather than State
— with an identifier to use as its storage key: ContentView.CurrentTab
. The identifier should be unique within your app. This allows you to create multiple SceneStorage
variables which won't clash with each other.
Build and run the app. Once running, switch to the third tab again. Then perform a cold launch of the app that you learned how to perform earlier.
How easy was that! By simply changing the attribute on the selectedTab
property from @State
to @SceneStorage(...)
, your app now automatically restores the state correctly when launched. That was easy!