Putting your CoreData stack into a Swift Package

I'm really excited at how easy it has become to divide a project up into local Swift Packages. Indeed it's becoming best-practice to do so:

I found there were a few hitches when adapting the default Xcode "Swift with CoreData" template to place the persistence aspects in its own Swift Package.

Assuming the starting state of a new Xcode project with "Use Core Data" checked, here's what you must do to shift the CoreData stack into a Persistence package:

File -> New -> Package, and create a Persistence package in your project:

Don't forget to add Persistence to your main app target in the "Frameworks, Libraries, and Embedded Content" panel:

Next, replace the boilerplate Persistence.swift file that Xcode added to the package with the CoreData boilerplate file. Also, move the CoreData model (e.g. Example.xcdatamodeld) into the same folder.

If you build now, there will be some compile errors about ContentView.swift:88:59 Cannot find 'PersistenceController' in scope etc. This is because we need to make the members (var, func etc) of our Persistence.swift file public, so that they're visible outside the Persistence module.

Build again - ah, you also need to add import Persistence to your ContentView.swift and ExampleApp.swift.

That gets rid of all build errors, except one:

This stumped me for a while because all the boilerplate code we started with is now linked up, yet Item (the boilerplate model class) apparently no longer conforms to Identifiable.

Actually, the Model class that Xcode automatically generates for your CoreData entities apparently gets generated with Identifiable conformance when it's used in a SwiftUI project, yet does not when it's sitting inside a Swift package. And because of that firewall between the main app target and the swift package, it doesn't seem to know to do that now.

Simple enough to solve though, just add an empty Identifiable conformance to Item:

extension Item: Identifiable {}

Great, it compiles! Let's run it. Oh.

CoreData: error:  Failed to load model named Example
Swift/UnsafeRawBufferPointer.swift:946: Fatal error: UnsafeRawBufferPointer with negative count

There's one extra change to make. Change the following code in Persistence.swift from

container = NSPersistentContainer(name: "Example")

to

let persistentContainerName = "Example"
let managedObjectModel = NSManagedObjectModel(contentsOf: Bundle.module.url(forResource: persistentContainerName, withExtension: "momd")!)
container = NSPersistentContainer(name: persistentContainerName, managedObjectModel: managedObjectModel!)

Now run again..

Hurrah! The Xcode CoreData template is working again.

Sample code available here.

p.s. One other tweak I made was moving static var preview: PersistenceController {} into a PersistenceController extension in the main app target instead, as it didn't seem core to the purpose of the Persistence package.