微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

环境属性包装器抛出 +entityForName: nil 不是用于搜索实体名称的合法 NSPersistentStoreCoordinator

如何解决环境属性包装器抛出 +entityForName: nil 不是用于搜索实体名称的合法 NSPersistentStoreCoordinator

为什么 Environment(\.managedobjectContext).wrappedValue 上的 entityForName 始终为零? 我收到此错误 +entityForName: nil is not a legal NSPersistentStoreCoordinator for searching for entity name 'Project'

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

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

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

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedobjectContext,persistenceController.container.viewContext)
        }
    }
}
struct ContentView: View {
    @Environment(\.managedobjectContext) var viewContext // Works
    @StateObject private var controller: ContentViewController
    
    init() {
        // Crashes
        let viewContextValue = Environment(\.managedobjectContext).wrappedValue

        let controller = ContentViewController(managedobjectContext: viewContextValue)
        self._controller = StateObject(wrappedValue: controller)
    }
    
    
    var body: some View {
        NavigationView {
            Text("Hello World")
        }
    }
}

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!")
    }
}

解决方法

简短的回答,Environment 需要 @,它是一个包装器。您尝试做的不是对 Environment

的记录使用

https://developer.apple.com/documentation/swiftui/environment

长答案,

您还没有提供最小可重复产品,但这是我所看到的

    let viewContextValue = Environment(\.managedObjectContext).wrappedValue
     

它不起作用,因为如您所知,此时 @Environment 不可用,否则您将只使用 viewContext

    let controller = ContentViewController(managedObjectContext: viewContextValue)

我明白你想在这里做什么,但如上所述,@Environment 只是在 init

期间不可用
    self._controller = StateObject(wrappedValue: controller)

虽然表面上“有效”,但它有点违背了 StateObject

SwiftUI 可能随时创建或重新创建视图,所以这很重要 使用给定的一组输入初始化视图总是会导致 相同的观点。因此,创建观察对象是不安全的 在一个视图里面。相反,SwiftUI 为 这个目的。您可以安全地在视图中创建一个 Book 实例 大大地: @StateObject var book = Book() https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

根据我的经验,SwiftUI 中的自定义 init 不能提供可靠的体验。我尽量远离他们。如果您必须对 init 进行自定义工作,请在 class 中将其作为 ViewModel/ViewController 进行,这也是 ObservableObjectView不应该做任何工作。

如果您想要替代方法,请参阅this SO question

你只需要

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]?,sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task> {
        fetchedResultsController = getFetchedResultsController(sortDescriptors: sortDescriptors,sectionNameKeyPath: sectionNameKeyPath)
        //fetchedResultsController!.delegate = self
        do {
            try fetchedResultsController!.performFetch()
            return fetchedResultsController!
            
        } catch {
            print( error)
            throw error
        }
    }
    func getFetchedResultsController(sortDescriptors: [NSSortDescriptor]?,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()
        
    }
}

previewAware() 只是决定传递内置 previewshared

的方法
static func previewAware() -> PersistenceController{
    if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
        return PersistenceController.preview
    }else{
        return PersistenceController.shared
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。