BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

CloudKit CoreData not syncing on WatchOS

Forums > watchOS

I have a watchOS extension that I'm trying to sync to the 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:

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? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                Typical reasons for an error here include:
                * The parent directory does not exist, cannot be created, or disallows writing.
                * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                * The device is out of space.
                * The store could not be migrated to the current model version.
                Check the error message to determine what the actual problem was.
                */
                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 most 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):

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):

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 {

        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()
            })
    }
}

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.