Core Data + CKSyncEngine with Swift 6 — concurrency, Sendable, and best practices validation

Hi everyone,

I’ve been working on migrating my app (SwimTimes, which helps swimmers track their times) to use Core Data + CKSyncEngine with Swift 6.
After many iterations, forum searches, and experimentation, I’ve created a focused sample project that demonstrates the architecture I’m using.

The good news:
👉 I believe the crashes I was experiencing are now solved, and the sync behavior is working correctly.
👉 The demo project compiles and runs cleanly with Swift 6.

However, before adopting this as the final architecture, I’d like to ask the community (and hopefully Apple engineers) to validate a few critical points, especially regarding Swift 6 concurrency and Core Data contexts.


Architecture Overview

  • Persistence layer: Persistence.swift sets up the Core Data stack with a main viewContext and a background context for CKSyncEngine.
  • Repositories: All Core Data access is abstracted into repository classes (UsersRepository, SwimTimesRepository), with async/await methods.
  • SyncEngine: Wraps CKSyncEngine, handles system fields, sync tokens, and bridging between Core Data entities and CloudKit records.
  • ViewModels: Marked @MainActor, exposing @Published arrays for SwiftUI. They never touch Core Data directly, only via repositories.
  • UI: Simple SwiftUI views bound to the ViewModels.

Entities:

  • UserEntity → represents swimmers.
  • SwimTimeEntity → times linked to a user (1-to-many).

Current Status

The project works and syncs across devices. But there are two open concerns I’d like validated:

  1. Concurrency & Memory Safety

    • Am I correctly separating viewContext (main/UI) vs. background context (used by CKSyncEngine)?
    • Could there still be hidden risks of race conditions or memory crashes that I’m not catching?
  2. Swift 6 Sendable Compliance

    • Currently, I still need @unchecked Sendable in the SyncEngine and repository layers.
    • What is the recommended way to fully remove these workarounds and make the code safe under Swift 6’s stricter concurrency rules?

Request

  • Please review this sample project and confirm whether the concurrency model is correct.
  • Suggest how I can remove the @unchecked Sendable annotations safely.
  • Any additional code improvements or best practices would also be very welcome — the intention is to share this as a community resource.

I believe once finalized, this could serve as a good reference demo for Core Data + CKSyncEngine + Swift 6, helping others migrate safely.


Environment

  • iOS 18.5
  • Xcode 16.4
  • macOS 15.6
  • Swift 6

Sample Project

Here is the full sample project on GitHub:
👉 [https://github.com/jarnaez728/coredata-cksyncengine-swift6]


Thanks a lot for your time and for any insights!
Best regards,
Javier Arnáez de Pedro

Thanks for writing the post and being willing to share knowledge with the community. I probably don't have enough cycles to review the project. Also, providing best practices on the whole thing will be hard, because synchronization is complicate, and there are corner cases that need to be handled carefully.

I think I can start with some specific aspects though, with the hope that other folks can jump in.

In general, using viewContext for UI and background contexts for background synchronization is the right pattern. I don't have more to say before thoroughly reading your code or seeing an issue.

If you can elaborate a bit about the concrete cases where you need to use @unchecked Sendable and why, I may be able to comment.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Core Data + CKSyncEngine with Swift 6 — concurrency, Sendable, and best practices validation
 
 
Q