macOS to macOS SwiftData iCloud Sync Problems

I am a novice developer, so please be kind. 😬

I am developing a simple macOS app backed with SwiftData and trying to set up iCloud sync so data syncs between two Macs running the app. I have added the iCloud capability, checked the CloudKit box, and selected an iCloud Container. Per suggestion of Paul Hudson, my model properties have either default values or are marked as optional, and the only relationship in my model is marked as optional.

@Model
final class Project {
    // Stable identifier used for restoring selected project across launches.
    var uuid: UUID?
    var name: String = ""
    var active: Bool = true
    var created: Date = Foundation.Date(timeIntervalSince1970: 0)
    var modified: Date = Foundation.Date(timeIntervalSince1970: 0)

    // CloudKit requires to-many relationships to be optional in this schema.
    @Relationship
    var timeEntries: [TimeEntry]?

    init(name: String, active: Bool = true, uuid: UUID? = UUID()) {
        self.uuid = uuid
        self.name = name
        self.active = active
        self.created = .now
        self.modified = .now
        self.timeEntries = []
    }
@Model
final class TimeEntry {
    // Core timing fields.
    var start: Date = Foundation.Date(timeIntervalSince1970: 0)
    var end: Date = Foundation.Date(timeIntervalSince1970: 0)
    var codeRawValue: String?
    var activitiesRawValue: String = ""

    // Inverse relationship back to the owning project.
    @Relationship(inverse: \Project.timeEntries)
    var project: Project?

    init(
        start: Date = .now,
        end: Date = .now.addingTimeInterval(60 * 60),
        code: BillingCode? = nil,
        activities: [ActivityType] = []
    ) {
        self.start = start
        self.end = end
        self.codeRawValue = code?.rawValue
        self.activitiesRawValue = Self.serializeActivities(activities)
    }

I have set up the following in the AppDelegate for registering for remote notifications as well as some logging to console that the remote notification token was received and to be notified when when I am receiving remote notifications.

private final class TimeTrackerAppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ notification: Notification) {
        print("📡 [Push] Registering for remote notifications")
        NSApplication.shared.registerForRemoteNotifications()
    }

    func application(_ application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenPreview = deviceToken.map { String(format: "%02x", $0) }.joined().prefix(16)
        print("✅ [Push] Registered for remote notifications (token prefix: \(tokenPreview)...)")
    }

    func application(_ application: NSApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        let nsError = error as NSError
        print("❌ [Push] Failed to register for remote notifications: \(nsError.domain) (\(nsError.code)) \(nsError.localizedDescription)")
    }

    func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String: Any]) {
        print("📬 [Push] Received remote notification: \(userInfo)")
    }
}

In testing, I run the same commit from Xcode on two different Macs logged into the same iCloud account.

My problem is that sync is not reliably working. Starting up the app on both Macs shows that the app successfully registered for remote notifications.

  • Sometimes, making an edit on Mac 1 is immediately reflected in Mac 2 UI along with didReceiveRemoteNotification message (all occurring while the Mac 2 app remains in foreground). Sometimes, the Mac 2 app needs to be backgrounded and re-foregrounded before the UI shows the updated data.
  • Sometimes, an edit on Mac 2 will show on Mac 1 only after re-foregrounded but not show any didReceiveRemoteNotification on the Mac 1 console.
  • Sometimes, an edit on Mac 2 will not show at all on Mac 1 even after re-foregrounding the app.
  • Sometimes, no edits sync between either Mac.

I had read about how a few years back, there was a bug in macOS where testing iCloud sync between Macs did not work while running from Xcode but would work in TestFlight. For me, running my app in TestFlight on both Macs has never been able to sync any edits between the Macs.

Any idea where I might be going wrong. It seems this should not be this hard and should not be failing so inconsistently. Wondering what I might be doing wrong here.

Answered by DTS Engineer in 876090022

When using SwiftData + CloudKit integration, which uses NSPersistentCloudKitContainer under the hood, the synchronization typically doesn't happen immediately. To better understand the synchronization, consider going through the following technotes:

In your case, the first three bullets are pretty much as-designed. The last one, "no edits sync between either Mac," if lasting for ever, which means the synchronization is broken, will be an issue. In that case, you can figure out what happens by capturing and analyzing a sysdiagnose, as described in TN3163.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Forgot to mention

    1. Mac 1: macOS 15.7.2, Xcode 26.2
    1. Mac 2: macOS 15.7.3, Xcode 26.2

When using SwiftData + CloudKit integration, which uses NSPersistentCloudKitContainer under the hood, the synchronization typically doesn't happen immediately. To better understand the synchronization, consider going through the following technotes:

In your case, the first three bullets are pretty much as-designed. The last one, "no edits sync between either Mac," if lasting for ever, which means the synchronization is broken, will be an issue. In that case, you can figure out what happens by capturing and analyzing a sysdiagnose, as described in TN3163.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Accepted Answer

I was able to get things syncing reliably between Macs today running internal TestFlight builds after adding CloudKit.Framework to the app target's "Frameworks, Libraries, and Embedded Content" section as described in this thread.. Once that was set, creating/editing a Project record would sync between the TestFlight apps.

I then noticed that creating/editing a related TimeEntry record would cause syncing to completely stop in either direction. I then discovered that only my Project records type had been deployed to Development on the iCloud Console; while TimeEntry had not been deployed for some reason. I re-deployed schema changes from development to production on iCloud Console, and now Project and TimeEntry record types show in iCloud Console in production. Syncing now works for creating/editing/deleting Project and TimeEntry records. So all is syncing well in my Internal TestFlight builds now.

Thank you, Ziqiao, for the links. I'm sure those would be helpful if I was still having issues.

Also, in the situation of running the apps from Xcode on each Mac, it seems that edits on one Mac do not reliably show very quickly from one Mac to another when the second Mac app is just sitting/wating in foreground. But, if I background and then foreground the app on the second Mac, the edits do then sync and display in the UI. Perhaps this is some limitation of when apps can trigger/receive push from iCloud while running in Debug from Xcode?

macOS to macOS SwiftData iCloud Sync Problems
 
 
Q