USB-Meter / Documentation / CloudKit-Sync /
README

CloudKit Sync Documentation

Complete reference for USB Meter's multi-device synchronization system using CloudKit.


Quick Start

Problem: How do settings sync across my iPhone, iPad, and Mac?

Answer: 1. When you connect a meter on iPhone → Meter.swift calls appData.publishMeterConnection() 2. This updates a DeviceSettings record in Core Data 3. NSPersistentCloudKitContainer automatically syncs it to iCloud 4. Your iPad + Mac receive a notification → refresh UI 5. Within 2-5 seconds, all devices show "Connected on iPhone"


Documentation Files

1. SCHEMA.md — Data Model & Structure

  • DeviceSettings entity attributes
  • CloudKit record mapping
  • Why uniquenessConstraint was removed
  • Version history (v1 → v2 → v3)

Read this if: You need to understand the data structure, add fields, or debug data corruption.

2. MECHANISM.md — How Sync Works

  • Complete architecture diagram
  • Step-by-step local write flow
  • Remote change reception flow
  • Core AppData methods explained
  • Connection lifecycle (TTL, expiry, locking)
  • Error handling & recovery

Read this if: You're troubleshooting sync issues, understanding threading, or extending functionality.

3. TROUBLESHOOTING.md — Debugging Guide

  • Configuration verification checklist
  • Common issues & fixes
  • Real-time monitoring code
  • Performance benchmarks
  • Log analysis

Read this if: Sync isn't working, you want to add debug UI, or need production troubleshooting.


Key Concepts

DeviceSettings Record

Represents a single Bluetooth meter device with its metadata:

macAddress: "aa:bb:cc:dd:ee:ff"  ← identifies the meter
meterName: "Kitchen Meter"        ← user-friendly name
modelType: "TC66C"                ← hardware type
connectedByDeviceID: "UUID"       ← which device is using it now
connectedExpiryAt: Date           ← connection lock TTL (120s)
updatedAt: Date                   ← last sync timestamp

Sync Heartbeat

  • Connect: publishMeterConnection() → saves record → syncs
  • Disconnect: clearMeterConnection() → clears record → syncs
  • Refresh: Periodically calls reloadSettingsFromCloudStore() to check for remote changes

Conflict Resolution

If Device A and B edit the same record simultaneously: - Policy: NSMergeByPropertyStoreTrumpMergePolicy - Rule: CloudKit's version always wins - Edge case: User might see their local change revert (~1s after)


Architecture Summary

Meter UI ──→ BT Connection ──→ AppData ──→ CloudDeviceSettingsStore ──→ Core Data
                                  ↓                ↓
                          [CloudKit Sync]   [persisted locally]
                                  ↓
                          [iCloud Servers]
                                  ↓
                          Other Devices ──→ UI Updates

Critical Files in Codebase

File Role
AppDelegate.swift Creates NSPersistentCloudKitContainer (line 85-127)
SceneDelegate.swift Calls activateCloudDeviceSync() at startup
AppData.swift Orchestrates sync: publishMeterConnection, clearMeterConnection, reloadSettingsFromCloudStore
Meter.swift Line 109, 118, 132: Calls appData methods on state changes
CKModel.xcdatamodeld/USB_Meter 2.xcdatamodel/contents Core Data schema (no uniqueness constraint)
USB Meter.entitlements iCloud container + CloudKit permissions

Debugging Checklist

  • [ ] CloudKit account status = .available?
  • [ ] Entitlements file includes iCloud.ro.xdev.USB-Meter?
  • [ ] Model version is USB_Meter 2.xcdatamodel or later?
  • [ ] cloudStoreRebuildVersion ≥ 3?
  • [ ] Remote change observer registered in setupRemoteChangeNotificationObserver()?
  • [ ] Merge policy = NSMergeByPropertyStoreTrumpMergePolicy?

Common Workflows

"Settings won't sync to my other device"

  1. Check CloudKit account (TROUBLESHOOTING.md § 1)
  2. Verify entitlements (TROUBLESHOOTING.md § 2)
  3. Force reload: appData.reloadSettingsFromCloudStore()
  4. Check logs for NSPersistentStoreRemoteChange notifications

"I see duplicate meter entries"

  1. Verify cloudStoreRebuildVersion = 3 deployed
  2. Check rebuildCanonicalStoreIfNeeded ran (UserDefaults cloudStoreRebuildVersion.3)
  3. If duplicates persist, see TROUBLESHOOTING.md § Issue 2

"Connection lock doesn't expire"

  1. Check system clock on Device A (ParentalControls?)
  2. Manual unlock: appData.clearMeterConnection(mac: "...")
  3. Verify TTL is 120s in setConnection()

"I need to add a new sync field"

  1. Edit USB_Meter 2.xcdatamodel/contents → add attribute (optional!)
  2. Bump Core Data version if needed (lightweight migration)
  3. Update CloudDeviceSettingsStore to populate it
  4. Sync resumes automatically

Performance

  • Local write latency: ~10ms
  • CloudKit network push: ~1-2s (typical)
  • Remote notification delivery: ~2-5s
  • UI update after notification: <100ms
  • Full reload (10 records): ~50ms

Total end-to-end for a setting change: ~5-8 seconds (typical, good network)


Testing Tips

Use two devices on same iCloud account:

  1. Connect meter on iPhone → Check iPad within 5s for "Connected on iPhone"
  2. Rename meter on iPhone → Name updates on iPad instantly (local) after sync
  3. Airplane mode iPhone → Changes queue locally
  4. Reconnect iPhone → Changes resync automatically
  5. Switch iCloud accounts → No data loss (encrypted in CloudKit)

References


Questions?

Refer to specific sections above by file, or check the troubleshooting checklist in TROUBLESHOOTING.md.