SwiftUI Preview Data

One of the great features of SwiftUI is the Xcode support for previewing views without launching the simulator. This speeds up development but where do you keep your preview data? If you’re not careful your view code can become cluttered with sample data.

Preview Data

The tight integration Xcode provides between the declaration of a SwiftUI view and its preview gives you fast feedback as you make changes. At first I tended to create the sample data for the preview along with the preview. For example, here’s my preview of a view that displays a Country with some sample model data created in the preview provider:

Xcode preview canvas

That approach makes it easier to play with the data but it soon gets out of hand. I also want sample data when displaying countries in a list:

Xcode preview of list data

I created the rows in my list with country cell views which also need some of the same sample data:

Xcode preview of cell data

I’m also likely to want variations on the sample data for different edge cases. For example, how does my cell layout work when the country name wraps over multiple lines:

Xcode preview of cell data with long name

Without doing anything special I’m already copying and pasting variations of the same preview data multiple times. This is tiresome and a pain to maintain.

Development Assets

An approach I’ve seen from a few iOS developers and Apple engineers during WWDC videos is to create the preview data alongside the model. This might be in a standalone type or as an extension on the model (or view model):

extension Country {
  /// United Kingdom
  static let uk = Country(id: 2635167, name: "United Kingdom", 
    capital: "London", continent: "Europe", currency: "GBP", area: 244820, 
    north: 59.3607741849963, south: 49.9028622252397, east: 1.7689121033873,
    west: -8.61772077108559, population: 66488991, visited: true),

  /// ...
}

This cleans up the preview provider and allows the data to be used by any of the views:

struct CountryView_Previews: PreviewProvider {
  static var previews: some View {
    return NavigationView {
      CountryView(country: Country.uk)
    }
  }
}

It’s also a convenient place to create collections of model data that will make our list previews more realistic. For example, loading a larger set of country data from a JSON file:

extension Country {
  static var countries = loadPreviewData("countryInfo.json")

  static private func loadPreviewData(_ name: String) -> [Country]  { ... }
}

Collecting the preview data into one place is a good first step. The next step is to exclude this data from your release builds by letting Xcode know they are development assets.

You may have already noticed that if you create a new SwiftUI project Xcode creates a Preview Content folder for you and adds an empty asset catalog:

Xcode Preview Content folder

Xcode treats this asset catalog differently from your apps main asset catalog. Xcode will not include any assets you add to the preview asset catalog in your release builds. We are not limited to this one preview asset catalog. Xcode treats any files we add to the Preview Content folder as development assets.

You can add your own folders if you want. Add the files or folders to the Xcode project as usual then add them the list of development assets in the settings for the target:

Adding development assets to a target

In my example, I added the country preview data files to the existing Preview Content folder:

Preview content folder containing CountryData.swift and countryInfo.json

You can also use the development assets in your unit tests, just remember to add the assets to the targets:

Target membership

Read More

See this video from WWDC 2020 for a brief discussion (starting at around 9 mins) of using Xcode development assets for preview data: