Swift: Nil Error When Using Self.Moc.Save() to Save in Core Data

Swift: Nil Error when using self.moc.save() to save in core data

Change

@Environment(\.managedObjectContext) var moc 

To something like (If you are using the sample code that Xcode generates) if not manually get your moc however you set it up

var moc = PersistentController.shared.container.viewContext

@Environment wrappers are inconsistent in a class they should only be used in a SwiftUI View.

If you don't have a PersistentController get the code from a brand new project with SwiftUI lifecycle and CoreData. Be CAREFUL and keep the name from the original stack it is usually the app name

Comment out (DONT delete until you are sure this works) the stack code from the SceneDelegate and

make a variable

let context = PersistentController.shared.container.viewContext

So you can keep the context in your View

Saving core data entity in popover in SwiftUI throws nilError without passing .environment to SubView again

WOW THIS DROVE ME NUTS! Especially because the errors tells you absolutely no information as to how to fix.

Here is the fix until the bug in Xcode is resolved:

        .navigationBarItems(trailing:
Button(action: {
self.add = true
}, label: {
Text("Add Todo List")
}).sheet(isPresented: $add, content: {
AddTodoListView().environment(\.managedObjectContext, managedObjectContext)
})
)

Just add .environment(\.managedObjectContext, managedObjectContext) to your secondary view (a Modal, in this example).

Could not save data in core data

It seems like your persistent store is not added. Does your AppDelegate look like THIS ? (I've got this file from THIS tutorial, but it's essentially the same among most apps)

Initialize CoreData Date with nil

You have to create your objects with the CoreData context.

//assign the context to the object
let meal = Meals(context: self.moc)

meal.id = UUID()
....
meal.will_eat = nil // or just remove that line

saveToPersistentStore()

I am referencing to your moc declared in the other function..

let moc = CoreDataStack.shared.mainContext

Managed Object Context is nil for some reason in iOS

I don't see anything immediately "wrong" so lets debug this a bit.

  1. Put a breakpoint in applicationDidFinish... right after the guard.
  2. Put a breakpoint at the creation of the privateContext.

Which fires first?

Where is the registerUser function? In a view controller? I hope not :)

the breakpoint right after my guard statement fires first. And yes, my registerUser function is indeed inside a ViewController.

Putting network code in view controllers is a code smell. View Controllers have one job, manage their views. Data gathering belongs in a persistence controller; for example, extending your NSPersistentContainer and putting data collection code there.

However, that is not the issue here, just a code smell.

Next test.

Is your persistent container and/or viewContext being passed into your view controller and bring retained?

Is your view controller being destroyed before the block fires?

To test this, I would put an assertion before Alamofire.request and crash if the context is nil:

NSAssert(self.managedObjectContext != nil, @"Main context is nil in the view controller");

I would also put that same line of code just before:

privateContext.parent = self.managedObjectContext

Run again. What happens?

I ran the test as you described, and I get the error: Thread 1: Assertion failed: Main context is nil in the view controller

Which assertion crashed? (probably should change the text a bit...)

If it is the first one then your view controller is not receiving the viewContext.

If it is the second one then the viewContext is going back to nil before the block executes.

Change your assumptions accordingly.

discovered something that is relevant here: If I place a button to call the registerUser() function at the user's discretion instead of calling it directly from the viewDidLoad() method, there is no crash, the code runs fine, and the MOC has a value

That leads me down the theory that your registerUser() was being called before your viewDidLoad(). You can test that by putting a break point in both and see which one fires first. If your registerUser() fires first, look at the stack and see what is calling it.

If it fires after your viewDidLoad() then put a breakpoint on the context property and see what is setting it back to nil.

So if I remove that line, how do I set the MOC property on my RootViewController via Dependency Injection?

The line right before it is the clue here.

let storyboard = self.window?.rootViewController?.storyboard

Here you are getting a reference to the storyboard from the rootViewController which is already instantiated and associated with the window of your application.

Therefore you could change the logic to:

(self.window?.rootViewController as? ViewController).managedObjectContext = container.viewContext

Although I would clean that up and put some nil logic around it :)

The problem I realize is that the MOC in the RootViewController is being used BEFORE the MOC is returned from the closure, and set in the AppDelegate. What do I do here?

This is a common synchronous (UI) vs. asynchronous (persistence) issue. Ideally your UI should wait until the persistence loads. If you load the persistent stores and then finish the UI after the stores have loaded it will resolve this issue.

Without a migration we are generally talking ms here rather than seconds.

BUT ... You want the same code to handle the UI loading whether it is milliseconds or seconds. How you solve that is up to you (design decision). One example is to continue the loading view until the persistence layer is ready and then transition over.

If you did that you could then subtly change the loading view if a migration is happening to inform the user as to why things are taking so long.

Error saving NSManagedObjectContext in swift

I investigated what was causing the error in the first place. I'd assumed it was a simple model validation error (trying to save an object with a missing required attribute) but all my attributes are optional.

It turns out that the problem was a Transformable attribute where I was trying to save an array of objects that don't adopt NSCoding.

I've fixed that and tried deliberately causing other types of errors (e.g. model validation) and the save function works as expected.

Shame that I wasn't getting a sensible error for the transformable thing, but glad it's otherwise working, and a good learning experience!



Related Topics



Leave a reply



Submit