Here is a demo of what I have (kind of a lot of code, but I hope someone can follow it).
I have one entity inside Core Data named Activity with one string field. For that I use this extension to display the data in the Previews:
extension Activity {
var _name: String {
name ?? ""
}
static var example: Activity {
let controller = DataController(inMemory: true)
let viewContext = controller.container.viewContext
let activity = Activity(context: viewContext)
activity.name = "running"
return activity
}
}
For setting up Core Data I use a DataController object:
class DataController: ObservableObject {
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "Model")
if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { storeDescription, error in
if let _ = error {
fatalError("Fatal error loading store")
}
}
}
static var preview: DataController = {
let dataController = DataController(inMemory: true)
let viewContext = dataController.container.viewContext
do {
try dataController.createSampleData()
} catch {
fatalError("Fatal error creating preview")
}
return dataController
}()
func createSampleData() throws {
let viewContext = container.viewContext
for _ in 1...10 {
let activity = Activity(context: viewContext)
activity.name = "run"
}
try viewContext.save()
}
}
In the app file I do the following setup:
struct TestApp: App {
@StateObject var dataController: DataController
@Environment(\.managedObjectContext) var managedObjectContext
init() {
let dataController = DataController()
_dataController = StateObject(wrappedValue: dataController)
}
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}
}
In my ContentView I display a list of this string from Core Data, which works correctly:
struct ContentView: View {
let activities: FetchRequest<Activity>
init() {
activities = FetchRequest<Activity>(entity: Activity.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Activity.name, ascending: false)], predicate: nil)
}
var body: some View {
List {
ForEach(activities.wrappedValue) { activity in
ActivityView(activity: activity)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var dataController = DataController.preview
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
.environmentObject(dataController)
}
}
But in my ActivityView where I display the string in a simple text field, previewing doesn't work.
struct ActivityView: View {
let activity: Activity
init(activity: Activity) {
self.activity = activity
}
var body: some View {
Text(activity._name)
}
}
struct ActivityView_Previews: PreviewProvider {
static var previews: some View {
ActivityView(activity: Activity.example)
}
}
I can see the string "run" in my list, 10 times, the way it is setup, but in the ActivityView screen I don't see anything displayed in the preview.
Not sure why is that, I hope someone has an idea.
edit:
I also tried this in the preview, but still doesn't work.
struct ActivityView_Previews: PreviewProvider {
static var dataController = DataController.preview
static var previews: some View {
ActivityView(activity: Activity(context: dataController.container.viewContext))
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}
viewContextthat you created inpreviewto thecreateSampleDatafunc, otherwise you are using two different contexts.