I ended up getting this to workout nicely.
1. Core Data model in its own Framework
I ended up putting my .momd & corresponding NSManagedObjects into an embedded framework.
1.1 In my case I added a new project into my workspace
This is just so that I dont have too many targets in my main project. Also I have more flexbility in the future to move the framework out of my workspace and into its own. It also gets its own independent identifier
So I had 3 projects in my workspace
- Main project with my iOS, app extensions and watchKit
- My ChronicKit (with my frameworks)
- Pods project
2.2 I added an iOS & Watch framework targets
Make sure to add these targets to the new project within your workspace. You just hit target and on the iOS tab, select the cocoa touch framework. Do the same for the Watch framework. You will then have two targets. You can't name them the same so I named one ChronicKit and the other ChronicKitWatch
OPTIONAL
2.3 I also managed to get both targets product name to be ChronicKit
This is an important step if you want both your frameworks to be named XXXXXX.framework (ie the same) If you don't have a watch target and are not planning to share your .momd with it, this and adding the watch target above are unnesseccary. There is a setting in the build settings that you can set. The following two settings for BOTH targets need to be the same as the name of your framework should be.
Just in cae the image below doesnt appear, I'll write it out here: Product Module Name & Product Name
This is neccessary becaue when you share your framework to a file/class that has both iOS and Watch as targets, the import statement will not clash.
import ChronicKit
2. Moved my Core Data database to an App Group shared location
This was also a little tricky but doable and only neccessary on the iOS side so that my app extension have access to the database. To this you need to
2.1 Enable "App Group" capbability on the iOS target and app extensions
2.2 Migrate your database to a new location
This is the tricky part. What I did was check if a database exists in the old location, if so migrate it. This happens only once.
2.2.1 Define your old and new location
static let oldLocationURL = sharedInstance.applicationDocumentsDirectory.appendingPathComponent("XXXXXX.sqlite")
static let newLocationURL = sharedInstance.applicationGroupDocumentDirectory.appendingPathComponent("XXXXXX.sqlite")
2.2.2 Make your persistentStoreCoordinator point to the new location
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: newLocationURL, options: options)
2.2.3 Add the following two functions to whereever you handle core data stuff (wherever your persistentStoreCoordinator and stuff is)
func checkIfMigrationRequired(oldLocationURL: URL, newLocationURL: URL) -> (needsMigration: Bool, targetURL: URL) {
var needsMigration: Bool = false
var targetURL: URL = DataAccess.newLocationURL
if FileManager.default.fileExists(atPath: oldLocationURL.path) {
needsMigration = true
targetURL = DataAccess.oldLocationURL
}
if FileManager.default.fileExists(atPath: newLocationURL.path) {
needsMigration = false
targetURL = DataAccess.newLocationURL
}
print(needsMigration ,targetURL)
return (needsMigration ,targetURL)
}
func migrateCoreDataStore(from oldLocationURL: URL, to newLocationURL: URL) {
if FileManager.default.fileExists(atPath: oldLocationURL.path) {
do {
if let newStore = self.persistentStoreCoordinator.persistentStore(for: newLocationURL) {
try self.persistentStoreCoordinator.remove(newStore)
}
let oldStore = try self.persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: oldLocationURL, options: DataAccess.options)
try self.persistentStoreCoordinator.migratePersistentStore(oldStore, to: newLocationURL, options: DataAccess.options, withType: NSSQLiteStoreType)
try FileManager.default.removeItem(at: oldLocationURL)
print("CoreData store moved")
}
catch let error {
print(error)
}
}
}
2.2.4 do the migration once in the didFinishLaunchingWithOptions
let (needsMigration, _) = DataAccess.sharedInstance.checkIfMigrationRequired(oldLocationURL: DataAccess.oldLocationURL, newLocationURL: DataAccess.newLocationURL)
if needsMigration {
DataAccess.sharedInstance.migrateCoreDataStore(from: DataAccess.oldLocationURL, to: DataAccess.newLocationURL)
}