Post

Using ContentUnavailableView to show an empty state in SwiftUI

From time to time we have to show an emptu state in a view. Mostly it’s when there is no data, there is some sort of an error, or there are no search results for the text that user entered. This is where ContentUnavailableView comes in.

I will be honest, this cought me off guard. It’s a new SwiftUI view that comes with iOS 17. I only noticed it lately and somehow missed it during WWDC 2023.

Basic usage of ContentUnavailableView

Right out of the box, the easiest applience for this is when user enters a text and app cant find any results. All you have to do is this:

1
2
3
var body: some View {
    ContentUnavailableView.search
}

Example 1

You can also specify a text. This should a text, that user has entered, for which you were unable to find data:

1
2
3
var body: some View {
    ContentUnavailableView.search(text: "Banana")
}

Example 2

Now since we are able to show it, let’s see how real example might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct ContentView: View {
    let fruits = ["Apple", "Watermellon", "Lemon", "Orange"]

    @State var searchResults = [String]()
    @State var searchText = ""

    var body: some View {
        NavigationStack {
            List {
                ForEach(searchResults, id: \.self) { fruit in
                    Text(fruit)
                }
            }
            .navigationTitle("Fruits")
            .searchable(text: $searchText)
            .overlay {
                // If search results are empty and user has entered the text
                // We show our content unavailable view.
                if searchResults.isEmpty && !searchText.isEmpty {
                    ContentUnavailableView.search(text: searchText)
                }
            }
            .onChange(of: searchText) { _, newValue in
                searchResults = fruits.filter {
                    $0.lowercased().contains(newValue.lowercased())
                }
            }
        }
    }
}

Now if you would run this code, search for a fruit that exists in fruits array, you will see the fruit:

Example 2

Or if you search for a fruit that does not exist, you will see our ContentUnavailableView:

Example 2

This is the most basic way of using ContentUnavailableView. Now let’s see how to customise it for other scenarios, not just search

Customizing ContentUnavailableView

So ContentUnavailableView can also be used in other scenarios and represent different state when content is unavailable. We can customise ContentUnavailableView using different initialiser.

We can pass a label, description and actions to ContentUnavailableView. With this, we can change the appearence of the View and even add actions. This helps in multiple scenarios. Even on network errors, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct ContentView: View {
    var body: some View {
        ContentUnavailableView(label: {
            Label("No Mail", systemImage: "tray.fill")
        }, description: {
            Text("New mails you receive will appear here.")
        }, actions: {
            Button(action: {
                // Refresh emails
            }) {
                Text("Refresh")
            }
        })
    }
}

Example 2

Conclusion

Well, I can see myself using this in my own apps, especially since I always try to save time 😅. But if I would need to do something like this for a company, designs would probably not match to what ContentUnavailableView offers and I would probably end up creating a custom EmptyStateView which would match my app and would be way more flexible.

Anyways, have a great 2024 everyone!

This post is licensed under CC BY 4.0 by the author.