Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI CoreData NSManagedObjectContext in the Environment

I have an issue exposing my NSManagedObjectContext to SwiftUI's Environment. Here's my code:

extension SceneDelegate: UIWindowSceneDelegate {

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = scene as? UIWindowScene else { return }

        let window = UIWindow(windowScene: windowScene)
        let context = PersistentContainer.shared.viewContext

        let rootView = TabBarView().environment(\.managedObjectContext, context)
        window.rootViewController = UIHostingController(rootView: rootView)

        self.window = window
        window.makeKeyAndVisible()
    }
}

class PersistentContainer: NSPersistentContainer {
    static let shared = PersistentContainer()

    private convenience init() {
        self.init(name: "App")

        viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyStoreTrumpMergePolicyType)
        viewContext.automaticallyMergesChangesFromParent = true

        loadPersistentStores { description, error in
            if let error = error {
                fatalError("Unable to load persistent stores: \(error)")
            }
        }
    }
}

struct CategoriesView: View {
    @Environment(\.managedObjectContext) var context

    @FetchRequest(entity: CoreCategory.entity(),
                  sortDescriptors: [
                    NSSortDescriptor(keyPath: \CoreCategory.createdAt, ascending: true)
                  ]
                 ) var categories: FetchedResults<CoreCategory>
}

My CategoriesView is my root view, so the context is accessed on app launch. I get the following error when my app launches ...

[error] warning:  View context accessed for persistent container App with no stores loaded

... yet the view is able to display results fine. Furthermore, if I present a modal view that contains a SwiftUI @FetchRequest in the same format as my CategoriesView, the app crashes with the following error:

[SwiftUI] Context in environment is not connected to a persistent store coordinator: <NSManagedObjectContext: 0x600002348b60>

From all the tutorials I found online I'm exposing my NSManagedObjectContext the recommended way. I have a feeling it's related to loadPersistentStores being asynchronous, but the context being set in the Environment synchronously. Anyone else able to get CoreData to work in SwiftUI?

like image 712
Mark Avatar asked Jan 18 '20 21:01

Mark


People also ask

Is Core Data thread safe?

Core Data is designed to work in a multithreaded environment. However, not every object under the Core Data framework is thread safe. To use Core Data in a multithreaded environment, ensure that: Managed object contexts are bound to the thread (queue) that they are associated with upon initialization.

What is NSManagedObjectContext in Core Data?

An object space to manipulate and track changes to managed objects.

How do I use Core Data?

Use Core Data to save your application's permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.


1 Answers

Figured out what's causing both error messages here. The first error is coming from my PersistentContainer singleton. I moved the two lines configuring the viewContext into the loadPersistentStores completion block, and those warnings went away:

private convenience init() {
    self.init(name: "App")

    loadPersistentStores { description, error in
        viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyStoreTrumpMergePolicyType)
        viewContext.automaticallyMergesChangesFromParent = true

        if let error = error {
            fatalError("Unable to load persistent stores: \(error)")
        }
    }
}

The second issue is apparently an issue with SwiftUI since the betas. There's a thread about it on the Apple Dev Forums here. You can do this as a workaround:

.sheet(isPresented: $isPresentingCategoryPicker) {
    CategoriesView()
        .environment(\.managedObjectContext, self.context)
}

It seems the environment gets cleared for modally presented view controllers.

like image 193
Mark Avatar answered Nov 13 '22 19:11

Mark