UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Struggling with Core Data and SwiftUI

Forums > SwiftUI

I am struggling with Core Data and SwiftUI to the point that it is causing some crashes in my app I cannot explain. Looking in it, it always comes down to creating a core data entry as part of a sheet popping up and somehow the context seems wrong.

In my MainApp:App I do have

ContentView()
    .environment(\.managedObjectContext, dataController.container.viewContext)
    .environmentObject(dataController)

In ContentView I do have

@EnvironmentObject var dataController: DataController 
@Environment(\.managedObjectContext) var managedObjectContext

Now I could save the object in two ways as part of a sheet I do pop up (to which I add the .environment and .environmentObject).

  1. Create the object in the managedObjectContext and save it directly in the sheet using the managedObjectContext

    let whatever = Whatever(context: managedObjectContext)
    managedObjectContext.save()

    properly try/catching this

  2. Create the object in the context of my persistence controller (dataController) and save it as part of a generic save routine in DataController

    let whatever = Whatever(context: dataController.container.viewContext)
    dataController.save()

    again that save routine in dataController implements try/catch.

To me that should not make any difference, whether I am using dataController.container.viewContext or managedObjectContext, they are essentially the same (when set up in the App structure).

But it looks like there is a difference. Is it better to save a new object directly and as quickly as possible (i.e. as part of the same sheet) rather than relying on a generic routine in DataController that saves the context?

Any help appreciated.

3      

First, do these crashes happen on the Simulator or on a real device? My experience with "unmotivated" crashing with no hint whatsoever (the usual BAD_EXEC) is that it happens only on the simulator in context with SwiftUI. When I test it on a real device I can't reproduce these crashes.

Second, I think your approach is somehow redundant. For myself, I pass only the dataController and I have a variable

var context: NSManagedObjectContext {
  container.viewContext
}

on my dataController for easier access.

3      

Thanks,

Crashes happen on real devices; my app is life and from the limited crash reports I can see in Xcode, the error is mostly related to creating a new core data object as part of a sheet. I myself cannot reproduce the crash one way or another which makes it difficult to tackle.

It almost feels like a timing issue; saving in the sheet using viewContext.save() is quicker then going out to the generic persistence controller (in my case dataController); as if that takes longer and the app is losing reference. I bumped on this documentation from Apple https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/MO_Lifecycle.html about creating a strong reference between managed objects and context but I am not sure whether that is the right path to go into...

3      

I guess you pass the dataController or the context with the environment to the sheet?

I would try to pass the dataController/context in the initializer to the sheet and store it in a local variable and look if the problem persists.

I for myself struggle with the lifecycle of the SwiftUI views and their sometimes irratic behaviour (not to mention sometimes it's just the simulator) and went for this approach and I don't experience problems like that anymore. I don't know if this fixes it for you but it would be a way to test it.

4      

What do you mean with initializer? You're suggesting an init() function as part of the sheet's implementation?

3      

Yes. My experience with the environment is not as good as it's with an initializer. Even if it's not the SwiftUI way.

The problem I have with SwiftUI is that the lifecycle is sometimes not the way I expect it. See this thread for example. https://www.hackingwithswift.com/forums/swiftui/how-to-show-a-sheet-splash-screen-only-on-first-time-app-launch-when-the-user-presses-ok-the-splash-screen-should-never-be-seen-in-subsequent-launches/9594 I would expect that the UserDefaults/AppStorage is all initialized before any method in the body property is executed, but it isn't. At least at first start of the app.

So I go with the init() as Xcode yells at me if I access something which doesn't have a value before I use it.

4      

I'm wondering if you are passing managedObjectContext into the sheet's own environment like:

.sheet(isPresented: $showingAddScreen) {
            AddBookView().environment(\.managedObjectContext, self.moc)
        }

This is how I usually handle sheets, so I am not sure if the sheet can sometimes pull the managedObjectContext from ContextView's environment and sometimes not.

This is discussed here: https://www.hackingwithswift.com/books/ios-swiftui/creating-books-with-core-data

Specifically this part:

You see, when we place an object into the environment for a view, it becomes accessible to that view and any views that can call it an ancestor. So, if we have View A that contains inside it View B, anything in the environment for View A will also be in the environment for View B. Taking this a step further, if View A happens to be a NavigationView, any views that are pushed onto the navigation stack have that NavigationView as their ancestor so they share the same environment.

Now think about sheets – those are full-screen pop up windows on iOS. Yes, one screen might have caused them to appear, but does that mean the presented view can call the original its ancestor? SwiftUI has an answer, and it’s “no”, which means that when we present a new view as a sheet we need to explicitly pass in a managed object context for it to use. As the new AddBookView will be shown as a sheet from ContentView, we need to add a managed object context property to ContentView so it can be passed in.

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.