ContentUnavailableView
Tags: swiftui, contentunavailablevi
Published: 2024-04-11 08:31:40

---
title: ContentUnavailableView
published: true
date: 2024-03-10 15:56:47 UTC
tags: SwiftUI,ContentUnavailableVi
canonical_url: https://wesleydegroot.nl/blog/ContentUnavailableView
---

In the ever-evolving world of SwiftUI, creating a seamless user experience involves handling empty states gracefully. Whether it's due to networking failures or empty search results, providing clear feedback to users is crucial. Enter **ContentUnavailableView** , a powerful SwiftUI component introduced in iOS 17 during WWDC 2023. In this article, we'll explore how to use it effectively and why it's a valuable addition to your toolkit.

## What is `ContentUnavailableView`?

**ContentUnavailableView** is a specialized SwiftUI view designed specifically for presenting empty states. It's your go-to solution when you need to communicate to users that content is unavailable due to various reasons. By leveraging this view, you can maintain consistency across your app while ensuring accessibility and localization.

## Use Case: Search Empty State

In this example, if there are no search results while the query is non-empty, we display the **ContentUnavailableView**. The resulting view looks familiar to users, as it's widely used across the system. Plus, it automatically translates into the languages your app supports.

```swift
struct ContentView: View {
    @Bindable
    var pokemon: PokemonViewModel

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if pokemon.pokemon.isEmpty {
                    ContentUnavailableView.search(text: pokemon.query)
                }
            }
            .searchable(text: $pokemon.query)
        }
    }
}
```

## Use Case: No Internet Connection
#NetworkMonitor.swift
```swift
import SwiftUI
import Network

final class NetworkMonitor: ObservableObject {
    @Published
    var isConnected = true

    let monitor = NWPathMonitor()

    init() {
        monitor.pathUpdateHandler = { [weak self] path in
            if path.status == .satisfied {
                // Internet connection is available
                self?.isConnected = true
            } else {
                // Internet connection is not available
                self?.isConnected = false
            }
        }

        // Start monitoring
        let queue = DispatchQueue(label: "Monitor")
        monitor.start(queue: queue)
    }
}

import SwiftUI

struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    @ObservedObject
    var pokemon: PokemonViewModel

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if !network.isConnected {
                    ContentUnavailableView {
                        Label(
                            "Connection issue", 
                            systemImage: "wifi.slash"
                        )
                    } description: {
                        Text("Check your internet connection")
                    } actions: {
                        Button("Refresh") {
                            pokemon.fetch()
                        }
                    }
                }
            }
        }
    }
}
```

## Combined Use Case: No Internet Connection and Empty Search Results
#NetworkMonitor.swift
```swift
import SwiftUI
import Network

final class NetworkMonitor: ObservableObject {
    @Published
    var isConnected = true

    let monitor = NWPathMonitor()

    init() {
        monitor.pathUpdateHandler = { [weak self] path in
            if path.status == .satisfied {
                // Internet connection is available
                self?.isConnected = true
            } else {
                // Internet connection is not available
                self?.isConnected = false
            }
        }

        // Start monitoring
        let queue = DispatchQueue(label: "Monitor")
        monitor.start(queue: queue)
    }
}
```
#PokemonViewModel.swift
```swift
import SwiftUI

class PokemonViewModel: ObservableObject {
    @Published
    var query = "" {
        didSet {
            fetch()
        }
    }

    @Published
    var pokemon: [String] = ["Dragonite", "Pikachu", "Lugia"]

    func fetch() {
        if query.isEmpty {
            pokemon = ["Dragonite", "Pikachu", "Lugia"]
            return
        }

        if query.lowercased().hasPrefix("d") {
            pokemon = ["Dragonite"]
        }

        if query.lowercased().hasPrefix("p") {
            pokemon = ["Pikachu"]
        }

        if query.lowercased().hasPrefix("l") {
            pokemon = ["Lugia"]
        }
    }
}
```

ContentView.swift

```swift
import SwiftUI

struct ContentView: View {
    @ObservedObject
    private var network = NetworkMonitor()

    @ObservedObject
    var pokemon = PokemonViewModel()

    var body: some View {
        NavigationStack {
            List(pokemon.pokemon, id: \.self) { pokemon in
                Text(verbatim: pokemon)
            }
            .navigationTitle("Pokémon")
            .overlay {
                if !network.isConnected {
                    ContentUnavailableView {
                        Label(
                            "Connection issue", 
                            systemImage: "wifi.slash"
                        )
                    } description: {
                        Text("Check your internet connection")
                    } actions: {
                        Button("Refresh") {
                            pokemon.fetch()
                        }
                    }
                }
                if network.isConnected && pokemon.pokemon.isEmpty && !pokemon.query.isEmpty {
                    ContentUnavailableView.search(text: pokemon.query)
                }
            }
            .searchable(text: $pokemon.query)
        }
    }
}
```

[Download the full example](https://wesleydegroot.nl/resources/ContentUnavailableView.swiftpm.zip)

## Caveats

While **ContentUnavailableView** is a powerful tool, it's not suitable for all scenarios. especially when handling empty states or situations where your app’s content cannot be displayed, and in complex situations a custom view may be better for empty state handling.

## Benefits of Using ContentUnavailableView

1. **Consistency** : By reusing a standard component, you ensure a consistent look and feel throughout your app.
2. **Localization** : The view automatically adapts to the languages your app supports.
3. **SwiftUI Integration** : It seamlessly integrates with SwiftUI's navigation stack and other components.

## Conclusion

**ContentUnavailableView** is a valuable addition to your SwiftUI toolkit. It's a powerful tool for handling empty states gracefully, ensuring a consistent user experience across your app. By leveraging this view, you can communicate to users that content is unavailable due to various reasons, such as networking failures or empty search results. This results in a more seamless and accessible user experience.

Resources:

[https://developer.apple.com/documentation/swiftui/contentunavailableview](https://developer.apple.com/documentation/swiftui/contentunavailableview)