Synchronizing App Preferences with iCloud
At a glance
| Item | Summary |
|---|---|
| Purpose | Keep a selected background color synchronized across iOS and macOS through iCloud key-value storage. |
| Architecture | Platform-specific MVC screens share a ViewController extension that observes cloud changes and synchronizes UserDefaults with NSUbiquitousKeyValueStore. |
| Local truth | Reads come from UserDefaults; one computed-property setter writes both stores and refreshes the platform UI. |
Project structure
├── Shared/
│ ├── Colors.swift
│ └── ViewController+KVS.swift
├── PrefsInCloud_iOS/
│ ├── ViewController.swift
│ └── ColorsTableViewController.swift
└── PrefsInCloud_macOS/ViewController.swift
The two targets deliberately declare their own ViewController; the same shared extension is compiled against either platform type.
Overall architecture
flowchart LR
IOS["iOS ViewController"] --> Shared["ViewController+KVS"]
Mac["macOS ViewController"] --> Shared
Shared --> Local["UserDefaults"]
Shared --> Cloud["NSUbiquitousKeyValueStore"]
Cloud --> Notice["external-change notification"]
Notice --> Shared
Shared --> UI["platform updateUserInterface"]
Reference code
Shared/ViewController+KVS.swift:134 — all preference writes pass through one local/cloud synchronization gateway.
var chosenColorValue: Int {
// ...
}Ownership and state
classDiagram
ViewController --> UserDefaults : reads and writes
ViewController --> NSUbiquitousKeyValueStore : writes and observes
NotificationCenter --> ViewController : external changes
ViewController --> ColorIndex : validates raw value
iOSViewController o-- ColorsTableViewController : selection flow
macOSViewController *-- NSPopUpButton : outlet
Ownership evidence
Shared/ViewController+KVS.swift:16 — each platform controller registers itself as the observer before forcing synchronization.
func prepareKeyValueStoreForUse() {
// ...
}The system stores own persisted values. The visible controller owns observation lifetime and UI refresh. The computed property is the app’s mutation authority; incoming cloud values are validated as ColorIndex before entering it.
Class and protocol design
| Type | Responsibility |
|---|---|
Platform ViewController types |
Build native controls and implement updateUserInterface. |
Shared ViewController extension |
Observe, validate, merge, and synchronize preference changes. |
ColorIndex |
Stable persisted raw values, localized names, and platform colors. |
ColorsTableViewController |
iOS-only selection presentation. |
PlatformSpecificColor alias |
Compile-time bridge from one shared enum to NSColor or UIColor. |
No app protocol is used; the shared extension relies on both target-specific controllers having the same name and method surface.
Access control
| Boundary | Effect and rationale |
|---|---|
macOS popup outlet and action are private |
Only the macOS controller manipulates its native control. |
Shared key and extension methods are implicit internal |
Both target builds can use them; external modules cannot. |
ColorIndex and platform alias are internal |
Persisted representation stays an app implementation detail. |
No public API |
The sample consists of executable targets, not a synchronization library. |
| Cloud values are validated before mutation | A data-integrity boundary complements language access control. |
PrefsInCloud_macOS/ViewController.swift:12 shows private UI state; Shared/Colors.swift:10 shows the compile-time platform alias.
Logic ownership and placement
| Logic | Owner |
|---|---|
| Register defaults and render native UI | Platform ViewController |
| Observe cloud changes and handle account changes | Shared KVS extension |
| Dual-store write | chosenColorValue setter |
| Persisted-value validation | ColorIndex(rawValue:) in notification handler |
| Platform color mapping | ColorIndex.color |
Design patterns
| Pattern | Evidence | Purpose |
|---|---|---|
| Shared extension | Shared/ViewController+KVS.swift:14 |
Reuses synchronization behavior across two native controllers. |
| Observer | Shared/ViewController+KVS.swift:22 |
Receives changes made by other devices/accounts. |
| Mutation gateway | Shared/ViewController+KVS.swift:134 |
Keeps local and cloud stores synchronized on every app write. |
| Platform adapter | Shared/Colors.swift:10 |
Maps one preference enum to AppKit or UIKit color types. |
Naming conventions
chosenColorValuenames the persisted scalar, whileColorIndexnames its validated domain representation.- The
+KVSfilename announces a capability extension. - Both targets use
ViewControllerandupdateUserInterface, enabling the shared source to compile unchanged.
Architecture takeaways
- Keep a local preference copy even when cloud synchronization is enabled.
- Route all app-originated writes through one dual-store mutation gateway.
- Validate externally synchronized raw values before applying them.
- Same-name target types can share a focused extension, but a reusable library would prefer an explicit protocol.
Source map
| Source | Role |
|---|---|
Shared/ViewController+KVS.swift:14 |
Synchronization behavior |
Shared/Colors.swift:18 |
Persisted color model |
PrefsInCloud_iOS/ViewController.swift:10 |
UIKit owner |
PrefsInCloud_iOS/ColorsTableViewController.swift:10 |
iOS selection UI |
PrefsInCloud_macOS/ViewController.swift:10 |
AppKit owner |