Better Programming

Advice for programmers.

Follow publication

Adding Core Data in a Swift Package

Gerhard Schneider
Better Programming
Published in
3 min readJan 3, 2022

Photo by Markus Winkler on Unsplash

When creating an app, its architecture should be modular, so that dependencies can be recognized and managed clearly and as simple as possible.

Swift packages are a convenient way to separate concerns, for instance, by extracting the persistent store to a package. However, when talking of Core Data stores, this is not as trivial as it seems.

This article describes a method how this can succeed, avoiding errors like “model not found”.

Basic Requirements

There shall be a package “Persistence” that holds a Core Data manager, as well as the Core Data model.

Other packages shall be able to use that package, in order to access the managed objects, as well as the manager itself.

Additionally, it shall be possible to set up different managers:

  • A manager for a temporary store, for usage with tests or other purposes.
  • A manager for a local store, if no iCloud is active.
  • A manager for a cloud store, if iCloud is available.

Finally, there shall be not a singleton implementation.

The Implementation

After creating a new package “Persistence” in Xcode, we need a package configuration:

Next, we add a protocol where the manager variants can conform to:

The configuration helps us to provide the basic settings:

Subclassing the Container

Actually, we just have to use a NSPersistentContainer now to set up the store. However, according to Apple, this class should be subclassed in order to easier control where the store shall be stored.

Therefore, we’re creating two subclasses (for local or temporary storage, and for cloud storage). In these subclasses we override the defaultDirectoryURL method in order to put the stores in appropriate places:

Conforming to the Protocol

Now we create our first implementation of the protocol we defined:

Here, we set up a temporary store for a given configuration that provides the name of the model. There is an important piece that is hidden in the ?TemporaryPersistenceManager.model(for:) function:

Two things are relevant here:

  • First, the URL must be retrieved from the package, therefore Bundle.module.url(forResource:withExtension:) must be used.
  • Second, although usually the extension momd is used, it is necessary to use just mom here. Otherwise, the model will not be found correctly.

You may now want to implement a LocalPersistenceManager and a CloudPersistenceManager in a similar way, I will not point out this here.

Using our Managers

Now, after adding the package to our app, we can use the manager there. In our example here, we’re using Resolver for registering our MetaDataService<> services that use the managers:

The MetaDataService<> services are defined in a package Metaon their own. That package just references the Persistence package:

Conclusion

I struggled a lot with errors that occurred at runtime (not at build time) because the model was not found. The solution presented here is now building and running properly.

However, there is still one thing left: although the Xcode console shows an error when the store is set up the very first time (something like “cannot create file”), the store is set up correctly. If you have any idea why, please leave a comment.

Thanks for reading this article, I hope it can save you some troubleshooting time in similar situations.

Gerhard Schneider
Gerhard Schneider

Written by Gerhard Schneider

Helping humans and companies to evolve by opening new perspectives on the management of products and portfolios.

Responses (1)

Write a response