Easily preview SwiftUI and CoreData

Andy Nadal
5 min readMay 14, 2021

--

SwiftUI is an amazing new framework to build UIs, and one it’s defining features is Xcode’s previews, which allow you to code a UI live for the first time in iOS; Core Data is also an amazing platform, but a replacement is due, personally I hope they’ll announce a declarative new framework, built for Swift this WWDC 21, but meanwhile this guide will allow you to maximize your productivity using both frameworks.

Here’s a sneak peak of the final syntax you’ll use in this guide:

You’ll get a NSManagedContext, any EnvironmentObject and an instance of your data for free.

The Problem

If you create a project using the SwiftUI App and CoreData template, it works, right-out-of-the-box, but it’s very limited and could encourage bad practices, there’re two in particular that bother me.

The first one is they use a Singleton, to access a `NSManagedObjectContext` in Xcode’s previews, so the UI could be fed with some CoreData objects, this is a bad practice, since nothing is limiting you from accessing the Singleton in production code; in SwiftUI there’s no need to use Singletons, since we have `@EnvironmentObject`.

The second problem is scalability, the `previewingData` is hard-coded inside the `Persistence` Singleton, while this might work for small projects, for larger ones, you can end up having a gigantic Singleton to manage your `previewingData`.

The Solution

Let’s make a `Previewing` view, that could give us an instance of a `NSManagedObject` and/or a data-rich `NSManagedObjectContext`, and let’s set a clear place to put our previewing objects at, it also allows us to assign any `EnvironmentObject` for free to our Previews.

For existing projects jump to the second step

1st step: Create a Project

Start by creating a new project in Xcode, click Multi-platform, then App, and check the `Use Core Data` checkbox.

You can also use an existing project.

This template gives us an `Item` object, that has a `timestamp` attribute; if you go to `ContentView`, you’ll see on the `Preview` has an environment context, that’s referencing that infamous Singleton, this works, but we can do better.

2nd step: Create a new file called “Previewing.swift”

Here, we’ll create a `View` that takes two parameters, a data source using a `KeyPath`, so it feels very Swiftly, and a `@ViewBuilder`, so any view can be previewed; we’ll need two generic types, a `Content` which is of type `View`, and a `Model`, that could be any type, so we could preview arrays of objects or a single object, since we’ll be using `KeyPath` this is completely type-safe. Remember to import Core Data and SwiftUI!

All the functionality will be here, and your Previews will be clutter-free.

As you can see on the image, the `View` will have two attributes, the `Content` and the persistence manager, this will allow us to pass to the body the `Context` as an `Environment`.

3rd step: Create a Struct called PreviewingData

This is where we’ll declare our previewing data, this will allow us to use `KeyPaths`, to gain type safety and a very nice syntax, it’s preferable to put this struct in another file.

Your Previewing data will live here.

Note that the `items` computed property is a closure that takes in a `Context`, this will be provided by `Previewing` to generate this data and put in on a usable `Context`, here’s where you can add your own data!, simply add it as another computed property; note that this closure returns an Array of `Item`, you can return anything you like, for example a single `Item`, the `KeyPath` will allow to you access it easily: `\.myData`.

If you find that syntax to be weird, the reason behind its that we don’t want to store closures on that Struct, we can to keep it lightweight, so we use a computed property that returns a closure, that’s why there’re two curly brackets.

4th step: Create the initializers

We now need to use `KeyPath` and `@ViewBuilder` to generate the data, and pass it to the `context` and the `content`; an instance of `PersistenceController` is created to avoid using the singleton, you could add multiple `EnvironmentObject` if you need them here, and get them on your previews for free!, this initializer will allow you directly get an instance of the object to work on.

This Initializer will give you an instance of a NSManagedObject, to be used in a Preview.

Now, let’s create a second `init` this one will allow us to get a data-rich context, but not the actual data, we’ll call the first parameter `contextWith`, to differentiate the two; you can use `@FetchRequest`.

This Initializer will give you a context filled with your data!

Happy Previewing!

Now we can access a super simple syntax in our code, to make development super easy. This snippet uses the context with data initializer, useful for `@FetchRequest`. Note that the Production View in this Preview automatically gain access to the context.

This one uses an instance of `Item` so can design our `ItemRowView`.

On PreviewingData we have:

Manage multiple datasets for all your CoreData models.

On our View:

This View uses an instance directly, since it’s a row view, there’s no FetchRequest here.

--

--