CloudKit Integration
CloudKit Integration
Section titled “CloudKit Integration”NSPersistentCloudKitContainer syncs Core Data with CloudKit, enabling seamless data synchronization across devices.
Basic Setup
Section titled “Basic Setup”import CoreDataimport CloudKit
let container = NSPersistentCloudKitContainer(name: "Model")
container.loadPersistentStores { description, error in if let error = error { fatalError("Failed to load store: \(error)") }}Configure CloudKit Container
Section titled “Configure CloudKit Container”In Xcode:
- Add CloudKit capability
- Select or create CloudKit container
- Enable “Use CloudKit” in Core Data model
Schema Design Limitations
Section titled “Schema Design Limitations”CloudKit has restrictions Core Data doesn’t:
Not Supported:
- Unique constraints on entities
Undefinedattribute typeObjectIDattribute type- Non-optional relationships (must be optional)
- Relationships without inverse
- Deny deletion rule
Supported:
- Adding new fields to record types
- Adding new record types
Important: Production schema is immutable. Plan carefully!
Schema Initialization
Section titled “Schema Initialization”Development Environment
Section titled “Development Environment”// First run initializes schema in Developmentcontainer.loadPersistentStores { description, error in // Schema created automatically}Promoting to Production
Section titled “Promoting to Production”- Test thoroughly in Development
- Open CloudKit Dashboard
- Deploy schema to Production
- Cannot modify after deployment!
Monitoring Sync
Section titled “Monitoring Sync”Observe Events
Section titled “Observe Events”NotificationCenter.default.addObserver( self, selector: #selector(storeDidChange), name: NSPersistentCloudKitContainer.eventChangedNotification, object: container)
@objc func storeDidChange(_ notification: Notification) { guard let event = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event else { return }
switch event.type { case .setup: print("Setup: \(event.succeeded ? "succeeded" : "failed")") case .import: print("Import: \(event.succeeded ? "succeeded" : "failed")") case .export: print("Export: \(event.succeeded ? "succeeded" : "failed")") @unknown default: break }
if let error = event.error { print("Error: \(error)") }}Testing Sync
Section titled “Testing Sync”func testSync() { let expectation = XCTestExpectation(description: "Export")
// Create expectation for export let observer = NotificationCenter.default.addObserver( forName: NSPersistentCloudKitContainer.eventChangedNotification, object: container, queue: nil ) { notification in guard let event = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event else { return }
if event.type == .export && event.endDate != nil { expectation.fulfill() } }
// Make changes let article = Article(context: container.viewContext) article.name = "Test" try? container.viewContext.save()
wait(for: [expectation], timeout: 60) NotificationCenter.default.removeObserver(observer)}Cross-Version Compatibility
Section titled “Cross-Version Compatibility”Strategy 1: Incremental Fields
Section titled “Strategy 1: Incremental Fields”Add new fields, keep old ones:
// V1: name// V2: name, subtitle (new)// Old versions see records but not subtitleStrategy 2: Version Attribute
Section titled “Strategy 2: Version Attribute”// Add version attributearticle.schemaVersion = 2
// Filter in fetch requestsfetchRequest.predicate = NSPredicate(format: "schemaVersion <= %d", currentVersion)Strategy 3: New Container
Section titled “Strategy 3: New Container”let options = NSPersistentCloudKitContainerOptions( containerIdentifier: "iCloud.com.example.app.v2")
let description = NSPersistentStoreDescription(url: storeURL)description.cloudKitContainerOptions = optionsCaution: Large datasets take time to upload.
Debugging
Section titled “Debugging”System Logs
Section titled “System Logs”Monitor these processes:
- Application - Core Data activity
- dasd - Scheduling decisions
- cloudd - CloudKit operations
- apsd - Push notifications
Using log stream
Section titled “Using log stream”# Application logslog stream --predicate 'process == "YourApp"'
# CloudKit logslog stream --predicate 'process == "cloudd" AND message CONTAINS "your.container.id"'
# Push notificationslog stream --predicate 'process == "apsd"'
# Schedulinglog stream --predicate 'process == "dasd" AND message CONTAINS "YourApp"'CloudKit Logging Profile
Section titled “CloudKit Logging Profile”- Download from Apple Developer Portal
- Install on device
- Reboot device
- Reproduce issue
- Collect sysdiagnose
Collecting Diagnostics
Section titled “Collecting Diagnostics”sysdiagnose:
- iOS: Volume Up + Volume Down + Power (hold)
- macOS: Shift + Control + Option + Command + Period
Common Issues
Section titled “Common Issues”Schema Mismatch
Section titled “Schema Mismatch”Problem: Local schema doesn’t match CloudKit schema.
Solution:
- Delete app
- Reinstall
- Let schema reinitialize
Sync Not Working
Section titled “Sync Not Working”Checklist:
- CloudKit capability enabled
- Signed in to iCloud
- Network connection available
- CloudKit container configured
- Schema initialized in Development
- Schema promoted to Production
Large Initial Sync
Section titled “Large Initial Sync”Problem: First sync takes too long.
Solutions:
- Use background fetch
- Show progress indicator
- Implement data generators for testing
Best Practices
Section titled “Best Practices”- Test in Development first - Schema is mutable
- Plan schema carefully - Production is immutable
- Make relationships optional - Required by CloudKit
- Add inverse relationships - Required by CloudKit
- Version your data - For cross-version compatibility
- Monitor sync events - Detect and handle errors
- Test with multiple devices - Verify sync behavior
- Handle conflicts - Use appropriate merge policy
- Collect diagnostics - For debugging sync issues
- Consider data size - Large datasets take time to sync
Summary
Section titled “Summary”- Use
NSPersistentCloudKitContainerfor CloudKit sync - Schema has limitations (optional relationships, no constraints)
- Production schema is immutable
- Monitor sync with event notifications
- Test thoroughly in Development before promoting
- Plan for cross-version compatibility
- Use system logs for debugging
- Collect sysdiagnose for complex issues