6

I have an SwiftUI View like this:

import SwiftUI

struct ReView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(
        entity: Re.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Re.name, ascending: false)
        ]
    ) var entities: FetchedResults<Re>
    
    var body: some View {
        NavigationView {
            List(entities, id: \.self) { entity in
                Text(entity.name ?? "Unknown")
            }
        }
    }
}

struct ReView_Previews: PreviewProvider {
    static var previews: some View {
            Group {
               
                ReView()
                    .environment(\.managedObjectContext, PersistentCloudKitContainer.persistentContainer.viewContext)
            }
        }
}

What can I do to show some sample data in my preview?

gurehbgui
  • 14,236
  • 32
  • 106
  • 178

3 Answers3

6

Here is adapted approach (early proposed in https://stackoverflow.com/a/61495490/12299030), tested with Xcode 12 / iOS 14

The idea is to separate explicit view + model from model provider (in this case cloud database), so view could be designed & tested (previewed) having local or dynamically constructed mock model (not involving heavy cloud connection)

struct ReView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(
        entity: Re.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Re.name, ascending: false)
        ]
    ) var entities: FetchedResults<Re>
    
    var body: some View {
        ReEntitiesView(entities: entities)
    }
}

struct ReEntitiesView<Results:RandomAccessCollection>: View where Results.Element == Re {
    let entities: Results

    var body: some View {
        NavigationView {
            List(entities, id: \.self) { entity in
                Text(entity.name ?? "Unknown")
            }
        }
    }
}

struct ReView_Previews: PreviewProvider {
    static let entity = NSManagedObjectModel.mergedModel(from: nil)?.entitiesByName["Re"]

    static var previews: some View {
       let object = Re(entity: entity!, insertInto: nil)
       object.name = "Test Name"

       return ReEntitiesView(entities: [object])
   }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
0

I've had some success with creating a static moc for the previews struct and then adding the data to that as needed.

Something like this:

struct ReView_Previews: PreviewProvider {
    static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    _ = Re(context: moc, mandatoryParam1: "foo" ...)
    _ = Re(context: moc, mandatoryParam1: "bah" ...)
    
    static var previews: some View {
        Group {
            ReView()
                .environment(\.managedObjectContext, moc)
        }
    }
}

I'm still early days with SwiftUI, so almost certainly more elegant ways to achieve. But, essentially creating the test data is just programming the sequence that would occur on the simulator to create it, since in Live mode that's what the preview is.

The downside is that clearing up corrupt test data can also take some programming effort.

Good luck.

shufflingb
  • 1,847
  • 16
  • 21
0

I went through this guide today, and I've ended up with a solution I am pleased with.

https://www.russellgordon.ca/tutorials/core-data-and-xcode-previews/

Xcode now solves part of this with its project template when you choose to include CoreData. There is a Persistence.swift file created. If you look inside, there is a static var preview where you can make some sample data that is committed to a "preview context."

In your PreviewProvider for the view you want to preview, add a modifier to the view instance that overrides the managedObjectContext, pointing to the "preview context" instead.

struct ProductsList_Previews: PreviewProvider {
    static var previews: some View {
        ProductsList()
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

Now when the FetchRequest in my ProductsList() view runs, it uses the "preview context" I've used in my override.

I learned how to do all of that before finding this article today. The hurdle I ran into after led me to the article - how to provide single examples of my entities to my previews. I'd recommend the article if you're having difficulty with that as well (which seems like the natural next step, in most cases).

Mathieson
  • 1,194
  • 2
  • 13
  • 28