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.
- Put a breakpoint in
applicationDidFinish...
right after the guard. - 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 theAppDelegate
. 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
Including Zeros in Front of an Integer
Why Does Function Has Multiple Return Types in Swift
Swiftui Mysterious Spacing Between Large Text and Textfield in VStack
How to Handle Two Possible Date Formats
Example for Drag and Drop Inside Nscollectionview
Decode/Encode Dictionary Keyed by Date
Different Path Url for Filemanager Everytime I Open the App
Xcode 8.0 Cbcentralmanager Issue
Resizing Large Resolution Images Producing 1000X1000 Pixels Size When Size Is Set to 500X500 Pixels
(Cross-)Compiling Swift for Raspberry Pi
Equivalent of Skaction Scaletox for a Given Duration in Unity
"Unexpectedly Found Nil While Unwrapping an Optional Value" When Retriveing Pffile from Parse.Com
Calling a Child Inside 2 Levels of Nodes
Any Way to Get a Gif as a Background with Swiftui
Get Images from Document Directory Not File Path Swift 3
Problem Creating and Writing a Subdirectory in Swift 5