Empty List placeholder. SwiftUI
Tags: swift, swiftui, ios, placeholder
Published: 2021-07-08 14:15:32

Year twenty twenty-one. Almost a month has passed since the WWDC. As usual Apple presented many amazing features/updates :heart_eyes:. As expected updated SwiftUI framework. But did not add placeholder for [``List``](https://developer.apple.com/documentation/swiftui/list) view :sob:. It's not big deal, but it was one of my expectations from conference. Okay let's do it ourselves :muscle:

The [``List``](https://developer.apple.com/documentation/swiftui/list) is one of the most used view in apps.
When using the [``List``](https://developer.apple.com/documentation/swiftui/list), devs also must handle the state of an empty data range and show a placeholder. 

As an example, consider a simple list of countries. Show placeholder when data is empty.

![Countries](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3s18nmf1lbowhm38q9z.png)

![Placeholder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r21n4v592v6rb7yl7gza.png)

Country model:

```swift
struct Country: Identifiable {
    let id = UUID()
    let name: String
}
```

So, have any ideas on how to implement a placeholder? 

### First idea ``If else``
The first thing that comes to mind it's [``if else``](https://docs.swift.org/swift-book/ReferenceManual/Statements.html) conditional statement.


```swift
struct ContentView: View {
    
    @State var countries: [Country] = [] // Data source
    
    var body: some View {
        if countries.isEmpty {
            Text("No Countries") // Placeholder
                .font(.largeTitle)
        } else {
            List(countries) { country in // List countires
                Text(country.name)
                    .font(.title)
            }
        }
    }
}
```

Advantages: 
- the most simple and clear way
- easy to modify 
- it works and shows the placeholder when needed
- easy to use any view for placeholder 

Disadvantages: 
- the code looks cumbersome
- not reusable

It works and sometimes it's enough. But in production, it would be nice to have a component that implements the logic of displaying a placeholder inside the component. So, goes to the next idea. 

### Second idea ``EmptyList``

Improve ``if else`` idea and move logic show/hide placeholder to custom view, call it ``EmptyList``:

```swift

struct EmptyList<Items: RandomAccessCollection, ListRowView: View, PlaceholderView: View>: View where Items.Element: Identifiable {
    
    private let items: Items
    private let listRowView: (Items.Element) -> ListRowView
    private let placeholderView: () -> PlaceholderView
    
    /// - Parameters:
    ///   - items: Source data for List. Item must implement Identifiable protocol
    ///   - listRowView: View displayed for each source Item
    ///   - placeholderView: Placeholder. View displayed when the items collection isEmpty
    init(_ items: Items,
         @ViewBuilder listRowView: @escaping (Items.Element) -> ListRowView,
         @ViewBuilder placeholderView: @escaping () -> PlaceholderView) {
        self.items = items
        self.listRowView = listRowView
        self.placeholderView = placeholderView
    }
    
    var body: some View {
        if !items.isEmpty {
            List { // List countires
                ForEach(items) { item in
                    self.listRowView(item)
                }
            }
        } else {
            placeholderView()
        }
    }
}
```
Using the ``EmptyList`` is very easy. First parameter - data source, second parameter -  list row view, and finally third parameter - placeholder view. 

```swift

struct ContentView: View {
    
    @State var countries: [Country] = [] // Data source
    
    var body: some View {
        EmptyList(countries, // Data items 
        listRowView: { country in // List row view
            Text(country.name)
                .font(.title)
        }, placeholderView: {
            Text("No Countries") // Placeholder
                .font(.largeTitle)
        })
    }
}
```

Advantages: 
- code looks clean and clear :heart_eyes:
- easy to modify custom view
- reusable in project
- use any view for placeholder 

Disadvantages: 
- list is embedded in ``EmptyList`` view, and if want to add some ViewModifier-s to the list, need for more efforts and modify code
 
 Usually, I would have to say that this is all and say goodbye but is not all :sunglasses:. I want to share an idea of how I cook placeholder for lists in my projects. 

### Preferred idea ``ViewModifier``

Create custom [``ViewModifier``](https://developer.apple.com/documentation/swiftui/viewmodifier) to manage placeholder, call it ``EmptyDataModifier``:

```swift

struct EmptyDataModifier<Placeholder: View>: ViewModifier {
    
    let items: [Any]
    let placeholder: Placeholder
    
    @ViewBuilder
    func body(content: Content) -> some View {
        if !items.isEmpty {
            content
        } else {
            placeholder
        }
    }
}
```

Uses  ``EmptyDataModifier``:

```swift

struct ContentView: View {
    
    @State var countries: [Country] = [] // Data source
    
    var body: some View {
        List(countries) { country in
            Text(country.name)
                .font(.title)
        }
        .modifier(EmptyDataModifier(
            items: countries,
            placeholder: Text("No Countries").font(.title)) // Placeholder
        ) 
    }
}
```
That's it! Also via extension can little bit improve the solution and limited apply ``EmptyDataModifier`` only for ``List``. 

```swift
extension List {
    
    func emptyListPlaceholder(_ items: [Any], _ placeholder: AnyView) -> some View {
        modifier(EmptyDataModifier(items: items, placeholder: placeholder))
    }
}
```
```swift
struct ContentView: View {
    
    @State var countries: [Country] = [] // Data source
    
    var body: some View {
        List(countries) { country in
            Text(country.name)
                .font(.title)
        }
        .emptyListPlaceholder(
            countries,
            AnyView(ListPlaceholderView()) // Placeholder 
        )
    }
}
```

Advantages: 
- code look clean and clear :heart_eyes: :heart_eyes: :heart_eyes:
- no need to create a custom ``List`` view
- easy to modify
- reusable in project
- use any view for placeholder 
- this way for can be used for any view placeholder 

Disadvantages: 
- no  (subjective opinion)

### Instead of summary
In my opinion, the most suitable way to implement a placeholder is to use a custom [``ViewModifier``] (https://developer.apple.com/documentation/swiftui/viewmodifier). 
I'm sure sooner or later the Apple will add a placeholder for the List view. Maybe this article will be as a request for this feature for Apple. Who knows.

[Source code](https://github.com/Toni77777/ListPlaceholderSample) 

Thanks for reading! See you soon.