Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Preview canvas and Core Data

Preview canvas is is crashing but in simulator everything working fine. I assuming it related to @ObservedObject and @Fetchrequest...

tried solution for here Previewing ContentView with CoreData

doesn't work

 import SwiftUI
 import CoreData

struct TemplateEditor: View {

@Environment(\.managedObjectContext) var managedObjectContext

@FetchRequest(
    entity: GlobalPlaceholders.entity(),
    sortDescriptors: [
        NSSortDescriptor(keyPath: \GlobalPlaceholders.category, ascending: false),
    ]
) var placeholders: FetchedResults<GlobalPlaceholders>


@ObservedObject var documentTemplate: Templates
@State private var documentTemplateDraft = DocumentTemplateDraft()
@Binding var editing: Bool


var body: some View {

    VStack(){
        HStack(){
            cancelButton
            Spacer()
            saveButton
        }.padding()
        addButton
        ForEach(placeholders)  {placeholder in
            Text(placeholder.name)
        }
        TextField("Title", text: $documentTemplateDraft.title)

        TextField("Body", text: $documentTemplateDraft.body)
            .padding()
            .frame(width: 100, height:400)
        Spacer()
    }

...




}
struct TemplateEditor_Previews: PreviewProvider {
    static var previews: some View {

    let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Templates")
    request.sortDescriptors = [NSSortDescriptor(keyPath: \Templates.created, ascending: false)]
    let documentTemplate = try! managedObjectContext.fetch(request).first as! Templates


    return TemplateEditor(documentTemplate: documentTemplate, editing: .constant(true)).environment(\.managedObjectContext, managedObjectContext).environmentObject(documentTemplate)

    }
}

Expected to generate preview

like image 310
Victor Sea Avatar asked Sep 03 '19 19:09

Victor Sea


3 Answers

I'm not sure if your try line will work if there is no data.

let documentTemplate = try! managedObjectContext.fetch(request).first as! Templates

To get mine to work I created a test Item to use. Like this:

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//Test data
        let newEvent = Event.init(context: context)
        newEvent.timestamp = Date()
        return DetailView(event: newEvent).environment(\.managedObjectContext, context)
    }
}

I've also noticed that I needed the .environment(.managedObjectContext, context) code in an earlier tabView that hosted the CoreData views or the preview would fail.

like image 173
Don Nissen Avatar answered Nov 04 '22 08:11

Don Nissen


This answer seems to work in my recent project by replacing the default ContentView_Previews struct, though others are questioning whether it pulls persistent data. Credit goes to @ShadowDES - in the Master/Detail template project in Xcode Beta 7

I'm able to CRUD anything using Canvas (XCode Version 11.3 (11C29)) and it seems to run flawlessly.

    #if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        return ContentView().environment(\.managedObjectContext, context)
    }
}
#endif

Add Values, mark priority

Swipe to delete

Deleted!

like image 13
Trapp Avatar answered Nov 04 '22 10:11

Trapp


What works for me:

I create all of my sample data in the preview property of my persistence controller, building off of the template generated by Xcode when starting a project with the following settings: Interface - SwiftUI, Lifecycle - SwiftUI App, Use Core Data, Host in CloudKit. I have posted the template here:

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext

        // ** Prepare all sample data for previews here ** //

        for _ in 0..<10 {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
        }
        do {
            try viewContext.save()
        } catch {
            // handle error for production
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "SwiftUISwiftAppCoreDataCloudKit")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // handle error for production
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}

In my preview, I inject the persistence controller into the preview environment and for my view argument I use the registeredObjects.first(where:) method on the preview viewContext to pull the first object of the desired type:

struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView(item: PersistenceController.preview.container.viewContext.registeredObjects.first(where: { $0 is Item }) as! Item)
            .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}

Edited 11/15/21

Persistence

    import CoreData
    
    struct PersistenceController {

        static let shared = PersistenceController()
    
        static var preview: PersistenceController = {
            let result = PersistenceController(inMemory: true)
            let viewContext = result.container.viewContext
            Seed().prepareData(for: viewContext)
            return result
        }()
    
        let container: NSPersistentCloudKitContainer
    
        init(inMemory: Bool = false) {
            container = NSPersistentCloudKitContainer(name: "SwiftUISwiftAppCoreDataCloudKit")
            if inMemory {
                container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
            }
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    // handle error for production
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            })
        }
    }

    struct Seed {
        func prepareData(for viewContext: NSManagedObjectContext) {
            // ** Prepare all sample data for previews here ** //

            for _ in 0..<10 {
                let newItem = Item(context: viewContext)
                newItem.timestamp = Date()
            }
            do {
                try viewContext.save()
            } catch {
                // handle error for production
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

Item Preview

    struct ItemView_Previews: PreviewProvider {
    
        static let persistence = PersistenceController.preview
    
        static var item: Item = {
            let context = persistence.container.viewContext
            let item = Item(context: context)
            item.timestamp = Date()
            return item
        }()
        
        static var previews: some View {
            ItemView(item: item)
                .environment(\.managedObjectContext, persistence.container.viewContext)
        }
    }
like image 11
Azure Avatar answered Nov 04 '22 09:11

Azure