CloudKit CoreData not syncing on WatchOS

I have a watchOS extension that I'm trying to sync to their iPhone using CoreData and CloudKit, but it doesn't seem to want to sync.

I've tried a few solutions (CloudKit + CoreData on iOS and watchOS sync not working, Core data + CloudKit - sharing between iOS and watchOS companion app), but it's still not syncing.

Here's my shared (it's loading in both targets) persistence code:

Code Block
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
return result
}()
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "My-app")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
let description = container.persistentStoreDescriptions.first
description?.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.mydomain.my-app")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.automaticallyMergesChangesFromParent = true
}
}


Should I have a different code for the watch? Right now both targets share all the files.

Both targets share the same xcdatamodeld file. I used to have all the ManagedObject classes automatically generated (Codegen: Class Definition), but someone suggested manually generating them, but that didn't seem to change anything. Maybe the classes need to be different for each target (but what should be different)?

Both targets have iCloud and background notifications enabled.

I don't know if this matters, but I'm using SwiftUI. Here's the view model and the relevant code that loads the data (same file for both targets):


Code Block
import Foundation
import CoreData
import SwiftUI
final class ProjectListViewModel : ObservableObject {
private var viewContext : NSManagedObjectContext
@Published var settings : Settings?
@Published var coreDataHasError = false
@Published var projects : [Project]
init(viewContext : NSManagedObjectContext, settings : Settings? = nil) {
self.projects = [Project]()
self.viewContext = viewContext
self.settings = settings
if settings != nil {
updateList()
}
}
func updateList() {
var orderBy : NSSortDescriptor = NSSortDescriptor(keyPath: \Project.dateModified, ascending: false)
if settings != nil {
switch settings!.orderBy {
case Settings.OrderBy.dateCreated.rawValue:
orderBy = NSSortDescriptor(keyPath: \Project.dateCreated, ascending: false)
case Settings.OrderBy.alphabetical.rawValue:
orderBy = NSSortDescriptor(keyPath: \Project.name, ascending: true)
default:
orderBy = NSSortDescriptor(keyPath: \Project.dateModified, ascending: false)
}
}
let request : NSFetchRequest<Project> = Project.fetchRequest()
request.predicate = NSPredicate(format: "status == %d", Project.ProjectStatus.active.rawValue)
request.sortDescriptors = [orderBy]
projects = try! viewContext.fetch(request)
}
}

And the view (this one is only used by the watch):

Code Block
import SwiftUI
import CoreData
struct ProjectListView: View {
@ObservedObject var viewModel : ProjectListViewModel
@ObservedObject var settings : Settings
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Environment(\.managedObjectContext) private var viewContext
init(viewContext: NSManagedObjectContext, settings: Settings) {
self.viewModel = ProjectListViewModel(viewContext: viewContext, settings: settings)
self.settings = settings
}
var body: some View {
if quickCountProject.count == 0 {
viewModel.addScratchProject()
}
return
ScrollView {
VStack {
if viewModel.projects.count >= 1 {
ZStack {
VStack {
Text(NSLocalizedString("Title", comment: "Page title"))
.TitleStyle()
List {
ForEach(viewModel.projects) { project in
NavigationLink(destination:
ProjectView(project: project, settings: settings, viewModel: ProjectListViewModel(viewContext: viewContext), viewContext: viewContext)
.environmentObject(self.settings)
.environment(\.managedObjectContext, viewContext))
{
HStack {
Image(systemName: "plus.app.fill").foregroundColor(.main).imageScale(.large)
Text(project.name ?? "").font(.headline).padding(.bottom, 5).padding(.top, 5)
}
.accessibilityHint(Text(NSLocalizedString("View project details", comment: "")))
}
}
.listRowBackground(Color.lightGray)
.padding(0)
}
.frame(minHeight: (45 * CGFloat(viewModel.projects.count)))
}
.padding(0)
}
} else {
Text(NSLocalizedString("To start, add a new project on you phone or iPad. Or use the button below to do a quick count.", comment: ""))
.fixedSize(horizontal: false, vertical: true)
}
}
}
.onAppear(perform: {
viewModel.updateList()
})
}
}


I’m not sure if you resolved this yourself in the end, but I was experiencing a similar issue with roughly the same setup (Core Data, CloudKit, and SwiftUI): my iOS changes were syncing with CloudKit, but nothing seemed to change on watchOS.

I ended up changing the Core Data codegen for my model(s) from “Class Definition” to “Manual/None” in Xcode’s inspector. I then navigated to “Editor → Create NSManagedObject Subclass…” to create the data model classes, resolved build errors, and committed the changes.

It’s a shame that the automatically generated class definitions don’t seem to work as expected; I hope this solution works for you!

CloudKit CoreData not syncing on WatchOS
 
 
Q