+Entityforname: Nil Is Not a Legal Nsmanagedobjectcontext Parameter Searching for Entity Name 'Account''

+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Account''

- (NSManagedObjectContext *)managedObjectContext
{
if (managedObjectContext != nil) return managedObjectContext;

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {

managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return managedObjectContext;
}
  • You haven't provided a lazy loading implementation of persistentStoreCoordinator
  • so coordinator will always be nil
  • so you will always be returning nil from this method
  • which means you will always get the error above.

To explain the error:

+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'Account'

It's not immediately obvious from reading it, but this means that nil is not a legal thing to pass for the managed object context. On first reading, it looks like you're doing entityForName:nil but that isn't the case.

To fix the problem, you will need to provide a valid persistent store coordinator. I have a small article here which explains just how little code you need to set up a core data stack, this may help you.

iOS: Swift: Core Data: Error: +entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name

When you set var fetchedResultsController, your managedObjectContext is still nil. Therefore, your app crashes when you declare:

let entity = NSEntityDescription.entityForName("OneItemCD", inManagedObjectContext: self.managedObjectContext)

Make sure to set managedObjectContext correctly before you call the previous line of code.

One more thing:

To use a Swift subclass of OneItemCD with your Core Data model, prefix the class name in the Class field of the model entity inspector with the name of your module, just like this: "testTableViewCoreData.OneItemCD" (See more about Core Data and namespaces here).

Environment property wrapper throws +entityForName: nil is not a legal NSPersistentStoreCoordinator for searching for entity name

Short answer, Environment needs the @, it is a wrapper. What you are trying to do isn't a documented use of Environment

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

Long answer,

You haven't provided a Minimal Reproducible product but here is what I see

    let viewContextValue = Environment(\.managedObjectContext).wrappedValue

It doesn't work because as you know the @Environment isn't available at this point or you would just use viewContext.

    let controller = ContentViewController(managedObjectContext: viewContextValue)

I see what you are trying to do here but as stated above @Environment just isn't available during init

    self._controller = StateObject(wrappedValue: controller)

And this while it "works" on the surface it kind of defeats the virtues of StateObject

SwiftUI might create or recreate a view at any time, so it’s important
that initializing a view with a given set of inputs always results in
the same view. As a result, it’s unsafe to create an observed object
inside a view. Instead, SwiftUI provides the StateObject attribute for
this purpose. You can safely create a Book instance inside a view this
way:
@StateObject var book = Book()
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

In my experience custom inits in SwiftUI don't provide a reliable experience. I try to stay away from them as much as I can. If you have to do custom work upon init do it in a class as a ViewModel/ViewController that is also an ObservableObject a View shouldn't do any work.

If you want an alternative to what you want to do this see this SO question

All you need is

let persistenceController = PersistenceController.shared

Inside your ContentViewController and initialize your StateObject like this.

@StateObject private var controller: ContentViewController = ContentViewController()

Here is a sample where I used a FetchedResultsController it has sections

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()

}
}

previewAware() is just a method that decides wether to pass the built-in preview or shared

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


Related Topics



Leave a reply



Submit