Access iOS app CoreData in Swift extension?

This should be simple, but it's proving immensely difficult and annoying.

I have three targets in the project:

  • an iOS app written in Objective-C
  • a Watch App written in Swift/SwiftUI
  • a Widget Extension with my widgets and complications in, written in Swift/SwiftUI

I want to access the Core Data that's got all my app's data in it from the Widget Extension and Watch App. How do I do this? Every internet search result assumes all your targets are in Swift, and it's really not feasible for me to rewrite the entire app.

All three targets have the same App Group set up, and I can access UserDefaults in each target.

I have a CoreData.swift class in a shared folder, and it's a member of both the Watch App and Widget Extension targets. It has this in it:

lazy var persistentContainer: NSPersistentContainer = {
		let storeURL = URL.storeURL(for: kAppGroup, databaseName: kCoreDataModel)
		let storeDescription = NSPersistentStoreDescription(url: storeURL)
		let container = NSPersistentContainer(name: kCoreDataModel)
		container.persistentStoreDescriptions = [storeDescription]
		container.loadPersistentStores(completionHandler: { (storeDescription, error) in
			if let error = error as NSError? {
				fatalError("CoreData: Failed to initialise Managed Object Model from url: \(storeURL), with error: \(error), \(error.userInfo)")
			}
		})
		return container
	}()

and the storeURL extension is:

public extension URL {
	static func storeURL(for appGroup: String, databaseName: String) -> URL {
		guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
			fatalError("CoreData: Shared file container could not be created.")
		}
		return fileContainer.appendingPathComponent("\(kCoreDataModel).sqlite")
	}
}

To be clear, the main iOS app (in Objective-C) has created the Core Data model etc., and it all works fine. I just want to be able to access the data in that model from the other targets. (I only need read access.)

When I deploy to a device the stack looks like this:

NSURL *dataModelFolderURL = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kAppGroup] URLByAppendingPathComponent:kCoreDataFolder];

Is: /private/var/mobile/Containers/Shared/AppGroup/9F329A90-C897-4AA2-87DF-D98A9E85356A/data-model

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:kCoreDataModel withExtension:@"momd"];

Is: file:///private/var/containers/Bundle/Application/CA9E3697-C4C6-48CB-89EA-CC441A6F81AF/MyApp.app/TheDataModel.momd/

// Wait until we have the store, or fetches will sometimes return no data
[self performSelectorOnMainThread:@selector(getStore) withObject:nil waitUntilDone:YES];

storeURL = file:///private/var/mobile/Containers/Shared/AppGroup/9F329A90-C897-4AA2-87DF-D98A9E85356A/data-model/TheDataModel

I've tried getting the Swift targets to look in a bunch of different paths, but nothing works. When I try to get a count of objects in the Core Data I always get zero results.

The second of those outputs is from the bundle. I guess that's because the data model is held within the iOS app's structure? Does that make any difference? Should it be held somewhere else? If so, what should it be?

(Minor rant: There really should be an option in Xcode that says, "This core data is shared here, here and here, and all you need to do is type CoreData.getSomeData() and you're done", but no, we have to write boilerplate code for everything.)

Anyway... any help is appreciated.

Accepted Reply

Fixed it. The code I was using to access the store had .sqlite tacked onto the end, so it wasn't finding the correct store.

Replies

Fixed it. The code I was using to access the store had .sqlite tacked onto the end, so it wasn't finding the correct store.