🔑

Designing Models for CloudKit Sync: Core Data & SwiftData Rules

(Updated on )

TL;DR: Enabling CloudKit sync is easy, but keeping it stable is difficult. The vast majority of sync errors following model adjustments stem from violating hard physical limitations of the data model. It is crucial to adhere to these rules during the design phase.

When enabling CloudKit synchronization for Core Data or SwiftData, the distributed nature of CloudKit imposes much stricter requirements on the local data model (Schema) than a purely local database. If these limitations are not fully considered during the modeling phase, subsequent adjustments can be incredibly painful.

Mandatory Data Model Rules

The following rules are compulsory. Violating any of them will result in sync failures or application crashes.

1. No Unique Constraints

CloudKit does not support atomic uniqueness checks across devices.

  • Core Data: Do not configure Unique constraints in the Entity Configuration.
  • SwiftData: Do not use the @Attribute(.unique) macro on properties.

2. Attributes Must Be Optional or Have Defaults

CloudKit needs to handle “partial data” synchronization scenarios.

  • Forbidden: Non-Optional attributes without a default value.
  • Allowed:
    • Optional types (?).
    • Non-Optional types with a Default Value.
    • Optional types with a Default Value.

3. Type Limitations

  • Forbidden: Using the Core Data Undefined type.

4. Strict Relationship Requirements

This is the most common source of errors:

  • Must Be Optional: All Relationships must be marked as Optional.
  • Must Have Inverse: All Relationships must have an Inverse relationship defined. While SwiftData macros often handle this automatically, it must be specified manually in the Core Data editor.
  • No ‘Deny’ Delete Rules: The Delete Rule cannot be set to Deny.
  • No Ordered Relationships: You cannot check Ordered (Arrangement) in Core Data. SwiftData does not support this option either.

Migration Strategy: Add-Only

Once CloudKit is enabled, schema changes are heavily restricted. You are effectively limited to Lightweight Migration.

Strict Modification Limits

Once your app is live (and the Schema has been pushed to the Production environment), you must adhere to the “Add-Only, No-Delete, No-Change” principle:

  1. No Deletions: Do not delete existing Entities or Attributes. Even if you no longer use them, keep them in the model definition.
  2. No Renaming: Do not rename Entities or Attributes. CloudKit interprets a rename as “deleting the old one and adding a new one,” resulting in data loss for the old field and sync inconsistencies.
  3. No Type Changes: Do not change the data type of an attribute (e.g., changing a String to an Int).

If you must modify the model:

  • Add New Attributes: This is the safest approach.
  • Deprecate Old Attributes: Stop using the old attributes in your code, but leave the model definition intact.

Further Reading

Related Tips

Subscribe to Fatbobman

Weekly Swift & SwiftUI highlights. Join developers.

Subscribe Now