Ios 环境属性+;entityForName:nil不是用于搜索实体名称的合法NSPersistentStoreCoordinator

Ios 环境属性+;entityForName:nil不是用于搜索实体名称的合法NSPersistentStoreCoordinator,ios,swift,core-data,swiftui,property-wrapper,Ios,Swift,Core Data,Swiftui,Property Wrapper,为什么环境(\.managedObjectContext).wrappedValue上的entityForName始终为零? 我收到此错误+entityForName:nil不是搜索实体名称“Project”的合法NSPersistentStoreCoordinator 使用@Environment(\.managedObjectContext)var viewContext我没有收到此错误。 但是我需要使用需要传递的NSManagedObjectContext控制器初始化视图 有人能帮助理解为

为什么
环境(\.managedObjectContext).wrappedValue
上的entityForName始终为零? 我收到此错误
+entityForName:nil不是搜索实体名称“Project”的合法NSPersistentStoreCoordinator

使用
@Environment(\.managedObjectContext)var viewContext
我没有收到此错误。 但是我需要使用需要传递的
NSManagedObjectContext
控制器初始化视图

有人能帮助理解为什么这两行不返回相同的对象吗?还是一样

@main
struct umbrellaApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}
ContentViewController
的初始值设定项

init(managedObjectContext: NSManagedObjectContext) {
    self.managedObjectContext = managedObjectContext
    self.projectsController = NSFetchedResultsController(fetchRequest: Project.projectsFetchRequest,
      managedObjectContext: managedObjectContext,
      sectionNameKeyPath: nil, cacheName: nil)
        
    super.init()

    projectsController.delegate = self
    do {
        try projectsController.performFetch()
        self.projects = projectsController.fetchedObjects ?? []
    } catch {
        print("failed to fetch projects!")
    }
}

简而言之,
环境
需要
@
,它是一个包装器。您试图做的并不是使用
环境

长话短说

你没有提供一个最小的可复制产品,但我看到的是

    let viewContextValue = Environment(\.managedObjectContext).wrappedValue
     
它不起作用,因为正如您所知,
@Environment
此时不可用,或者您只需要使用
viewContext

    let controller = ContentViewController(managedObjectContext: viewContextValue)
我知道您在这里试图做什么,但是如上所述,
@Environment
init

    self._controller = StateObject(wrappedValue: controller)
虽然它在表面上“起作用”,但它有点违背了
StateObject

SwiftUI可能随时创建或重新创建视图,因此这一点很重要 使用给定的一组输入初始化视图总是导致 同样的观点。因此,创建观察对象是不安全的 在视野之内。相反,SwiftUI为 这个目的。您可以在此视图中安全地创建书本实例 方式:
@StateObject var book=book()

根据我的经验,SwiftUI中的custom
init
s不能提供可靠的体验。我尽量远离他们。如果您必须在
init
上执行自定义工作,则在
类中作为
ViewModel
/
ViewController
执行该操作,这也是一个
observeObject
视图
不应执行任何工作

如果您想要替代您想要做的事情,请参阅

你所需要的只是

let persistenceController = PersistenceController.shared
在您的
ContentViewController
中,像这样初始化您的
StateObject

@StateObject private var controller: ContentViewController = ContentViewController()
下面是一个示例,我在其中使用了一个
FetchedResultsController
,它有几个部分

import SwiftUI
import CoreData
class TaskListViewModel: ObservableObject {
    let persistenceController = PersistenceController.previewAware()
    @Published var fetchedResultsController: NSFetchedResultsController<Task>?
    
    init() {
        setupController()
    }
    func setupController() {
        do{
            fetchedResultsController = try retrieveFetchedController(sortDescriptors: nil, predicate: nil, sectionNameKeyPath: #keyPath(Task.isComplete))
        }catch{
            print(error)
        }
    }
    func deleteObject(object: Task) {
        persistenceController.container.viewContext.delete(object)
        save()
    }
    func save() {
        do {
            if persistenceController.container.viewContext.hasChanges{
                try persistenceController.container.viewContext.save()
                objectWillChange.send()
            }else{
            }
        } catch {
            print(error)
        }
    }
}
//MARK: FetchedResultsController setup
extension TaskListViewModel{
    func retrieveFetchedController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
        
        return try initFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
    }
    private func initFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
        fetchedResultsController = getFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
        //fetchedResultsController!.delegate = self
        do {
            try fetchedResultsController!.performFetch()
            return fetchedResultsController!
            
        } catch {
            print( error)
            throw error
        }
    }
    func getFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) -> NSFetchedResultsController<Task> {
        
        return NSFetchedResultsController(fetchRequest: getEntityFetchRequest(sortDescriptors: sortDescriptors, predicate: predicate), managedObjectContext: persistenceController.container.viewContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
    }
    private func getEntityFetchRequest(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> NSFetchRequest<Task>
    {
        
        let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
        fetchRequest.includesPendingChanges = false
        fetchRequest.fetchBatchSize = 20
        if sortDescriptors != nil{
            fetchRequest.sortDescriptors = sortDescriptors
        }else{
            fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Task.dateAdded), ascending: false)]
        }
        if predicate != nil{
            fetchRequest.predicate = predicate
        }
        return fetchRequest
    }
}
struct TaskListView: View {
    @StateObject var vm: TaskListViewModel = TaskListViewModel()
    @State var taskToEdit: Task?
    var body: some View {
        
        if vm.fetchedResultsController?.sections != nil{
            List{
                ForEach(0..<vm.fetchedResultsController!.sections!.count){idx in
                    let section = vm.fetchedResultsController!.sections![idx]
                    TaskListSectionView(objects: section.objects as? [Task] ?? [], taskToEdit: $taskToEdit, sectionName: section.name).environmentObject(vm)
                    
                }
            }.sheet(item: $taskToEdit, onDismiss: {
                vm.save()
            }){editingTask in
                TaskEditView(task: editingTask)
            }
            
        }else{
            Image(systemName: "empty")
        }
    }
}

struct TaskEditView: View {
    @ObservedObject var task: Task
    var body: some View {
        TextField("name", text: $task.name.bound)
    }
}
struct TaskListSectionView: View {
    @EnvironmentObject var vm: TaskListViewModel
    let objects: [Task]
    @State var deleteAlert: Alert = Alert(title: Text("test"))
    @State var presentAlert: Bool = false
    @Binding var taskToEdit: Task?
    @State var isExpanded: Bool = true
    var sectionName: String
    
    var body: some View {
        Section(header: Text(sectionName) ,                content: {
            ForEach(objects, id: \.self){obj in
            let task = obj as Task
            Button(action: {
                taskToEdit = task
            }, label: {
                Text(task.name ?? "no name")
            })
            .listRowBackground(Color(UIColor.systemBackground))
            
            
        }.onDelete(perform: deleteItems)
        })
        
    }
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            deleteAlert = Alert(title: Text("Sure you want to delete?"), primaryButton: Alert.Button.destructive(Text("yes"), action: {
                let objs = offsets.map { objects[$0] }
                
                for obj in objs{
                    
                    vm.deleteObject(object: obj)
                }
                //Because the objects in the sections aren't being directly observed
                vm.objectWillChange.send()
                
            }), secondaryButton: Alert.Button.cancel())
            
            
            self.presentAlert = true
            
        }
    }
}

struct TaskListView_Previews: PreviewProvider {
    static var previews: some View {
            TaskListView()
        
    }
}

我想将
PreviewProvider
中的视图上下文传递给我的视图,以便在Xcode中预览。我看到您的
PersistanceController
上确实有
previewAware()
。这能解决我的问题吗?这是我制定的令人信服的方法。
PersistenceController
自动提供标准代码在检测到您正在预览中运行时生成的
preview
控制器。上面的代码只需将
func
添加到
PersistenceController
中即可解决我的问题。使用此函数,我不需要在预览和代码中传递
环境中的
managedObjectContext
。我只需在我的控制器中调用
PersistanceController.previewware()
,这就是我的viewContext。非常感谢。是的,您也可以在
视图中使用
previewware()
。这样你就不用担心改变它了。在我的
PersistenceController
中,我将
shared
设置为
private
,这样我就不会意外调用它。
import SwiftUI
import CoreData
class TaskListViewModel: ObservableObject {
    let persistenceController = PersistenceController.previewAware()
    @Published var fetchedResultsController: NSFetchedResultsController<Task>?
    
    init() {
        setupController()
    }
    func setupController() {
        do{
            fetchedResultsController = try retrieveFetchedController(sortDescriptors: nil, predicate: nil, sectionNameKeyPath: #keyPath(Task.isComplete))
        }catch{
            print(error)
        }
    }
    func deleteObject(object: Task) {
        persistenceController.container.viewContext.delete(object)
        save()
    }
    func save() {
        do {
            if persistenceController.container.viewContext.hasChanges{
                try persistenceController.container.viewContext.save()
                objectWillChange.send()
            }else{
            }
        } catch {
            print(error)
        }
    }
}
//MARK: FetchedResultsController setup
extension TaskListViewModel{
    func retrieveFetchedController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
        
        return try initFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
    }
    private func initFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
        fetchedResultsController = getFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
        //fetchedResultsController!.delegate = self
        do {
            try fetchedResultsController!.performFetch()
            return fetchedResultsController!
            
        } catch {
            print( error)
            throw error
        }
    }
    func getFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) -> NSFetchedResultsController<Task> {
        
        return NSFetchedResultsController(fetchRequest: getEntityFetchRequest(sortDescriptors: sortDescriptors, predicate: predicate), managedObjectContext: persistenceController.container.viewContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
    }
    private func getEntityFetchRequest(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> NSFetchRequest<Task>
    {
        
        let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
        fetchRequest.includesPendingChanges = false
        fetchRequest.fetchBatchSize = 20
        if sortDescriptors != nil{
            fetchRequest.sortDescriptors = sortDescriptors
        }else{
            fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Task.dateAdded), ascending: false)]
        }
        if predicate != nil{
            fetchRequest.predicate = predicate
        }
        return fetchRequest
    }
}
struct TaskListView: View {
    @StateObject var vm: TaskListViewModel = TaskListViewModel()
    @State var taskToEdit: Task?
    var body: some View {
        
        if vm.fetchedResultsController?.sections != nil{
            List{
                ForEach(0..<vm.fetchedResultsController!.sections!.count){idx in
                    let section = vm.fetchedResultsController!.sections![idx]
                    TaskListSectionView(objects: section.objects as? [Task] ?? [], taskToEdit: $taskToEdit, sectionName: section.name).environmentObject(vm)
                    
                }
            }.sheet(item: $taskToEdit, onDismiss: {
                vm.save()
            }){editingTask in
                TaskEditView(task: editingTask)
            }
            
        }else{
            Image(systemName: "empty")
        }
    }
}

struct TaskEditView: View {
    @ObservedObject var task: Task
    var body: some View {
        TextField("name", text: $task.name.bound)
    }
}
struct TaskListSectionView: View {
    @EnvironmentObject var vm: TaskListViewModel
    let objects: [Task]
    @State var deleteAlert: Alert = Alert(title: Text("test"))
    @State var presentAlert: Bool = false
    @Binding var taskToEdit: Task?
    @State var isExpanded: Bool = true
    var sectionName: String
    
    var body: some View {
        Section(header: Text(sectionName) ,                content: {
            ForEach(objects, id: \.self){obj in
            let task = obj as Task
            Button(action: {
                taskToEdit = task
            }, label: {
                Text(task.name ?? "no name")
            })
            .listRowBackground(Color(UIColor.systemBackground))
            
            
        }.onDelete(perform: deleteItems)
        })
        
    }
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            deleteAlert = Alert(title: Text("Sure you want to delete?"), primaryButton: Alert.Button.destructive(Text("yes"), action: {
                let objs = offsets.map { objects[$0] }
                
                for obj in objs{
                    
                    vm.deleteObject(object: obj)
                }
                //Because the objects in the sections aren't being directly observed
                vm.objectWillChange.send()
                
            }), secondaryButton: Alert.Button.cancel())
            
            
            self.presentAlert = true
            
        }
    }
}

struct TaskListView_Previews: PreviewProvider {
    static var previews: some View {
            TaskListView()
        
    }
}
static func previewAware() -> PersistenceController{
    if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
        return PersistenceController.preview
    }else{
        return PersistenceController.shared
    }
}