This is the single FAQ for SwiftSync.
Source-of-truth docs:
docs/project/parent-scope.md(parent-scoped sync/query behavior)docs/project/property-mapping-contract.md(mapping/import/export semantics)docs/project/reactive-reads.md(@SyncQuery/@SyncModelmental model)docs/project/relationship-integrity.md(many-to-many inverse anchor rule)docs/project/backend-contract.md(recommended backend shape)
If an answer needs more than a short explanation, this FAQ points to the source-of-truth doc instead of duplicating it.
No. Use only:
import SwiftSyncYes, if the model uses parent-scoped identity.
ParentScopedModeldefaults to.scopedByParent- explicit parent-relationship sync on non-
ParentScopedModeltypes defaults to.global @Attribute(.unique)on rawidstill enforces global uniqueness at the store level
See docs/project/parent-scope.md.
Because SwiftSync must know which child->parent relationship defines the sync scope (especially for scoped delete/diff).
- parent-scoped sync always passes an explicit
relationship:key path
See docs/project/parent-scope.md.
Use this mental rule:
relationship:+relationshipID:= relationship-scoped query by IDrelationship:always names the relationship path used by the querypredicate:= custom business filters or scalar-only filters
See docs/project/reactive-reads.md.
It expands what related model changes should invalidate/refetch the query.
Use it when the UI reads related data that is not part of the query model's own scalar fields.
See docs/project/reactive-reads.md.
No.
- missing key => no-op (
absent = ignore) - explicit
null=> clear - explicit
[]for to-many => clear membership
See docs/project/property-mapping-contract.md and docs/project/backend-contract.md.
Strict by type.
IntFK links toIntidentityStringFK links toStringidentity- no cross-type coercion at relationship-link step
Scalar attribute coercion is broader; relationship FK linking is intentionally stricter.
See docs/project/property-mapping-contract.md.
Not as ordered relationship semantics.
Treat to-many sync as unordered membership and store an explicit scalar order field if UI order matters.
See docs/project/property-mapping-contract.md and docs/project/backend-contract.md.
Convention-first.
- inbound key style is configured once at
SyncContainer(.snakeCasedefault,.camelCaseoptional) @RemoteKeyoverrides conventions- import/export follow the same mapping contract
See docs/project/property-mapping-contract.md.
Yes for @Syncable models: description, hashValue.
Use names like descriptionText / hashValueRaw, and map backend keys with @RemoteKey if needed.
See docs/project/property-mapping-contract.md.
Yes. Use relationshipOperations (default: .all).
Use this when you want row updates but need to narrow relationship-side work for a specific sync pass.
Later payload rows win (payload order is applied in order).
SwiftSync deduplicates local identity collisions during sync and keeps one logical row per identity.
That row is skipped for matching/diffing. Sync continues for valid rows.
- optional scalar ->
nil - non-optional primitive scalar -> default fallback (
"",0,false, epoch date, zero UUID)
See docs/project/property-mapping-contract.md.
SwiftSync serializes sync calls per ModelContainer.
- same
ModelContainer=> queued, no overlap - different stores => can run concurrently
This is enforced by an internal per-container sync lease, not by app code needing to add its own lock.
Use Swift Concurrency task cancellation (task.cancel()).
SwiftSync cooperatively cancels and rolls back unsaved in-memory work for that run.
Recommended pattern: no.
- views collect intent and display local reactive state
- a domain/service layer performs backend mutations and syncs local storage
- views re-render from
@SyncQuery/@SyncModel
See docs/project/reactive-reads.md ("App Best Practices").
Recommended default: no.
- pass IDs/scalars
- child view owns/queries the data it needs
- avoid model-object handoff across navigation/sheet boundaries
This keeps view ownership explicit and avoids stale retained-reference assumptions.
See docs/project/reactive-reads.md ("App Best Practices").
The corrected rule is narrower than what we first documented:
- this is a many-to-many issue (not a general all-to-many issue)
- one-to-many relationships work fine without explicit inverses
- many-to-many pairs should have one explicit inverse anchor (
@Relationship(inverse: ...)) on either side - do not force both sides if SwiftData throws the circular
@Relationshipmacro compiler error
We removed the earlier broad @Syncable warning/allowlist because it encoded the wrong rule.
See docs/project/relationship-integrity.md.