Claude-skill-registry axiom-icloud-drive-ref
Use when implementing 'iCloud Drive', 'ubiquitous container', 'file sync', 'NSFileCoordinator', 'NSFilePresenter', 'isUbiquitousItem', 'NSUbiquitousKeyValueStore', 'ubiquitous file sync' - comprehensive file-based iCloud sync reference
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/axiom-icloud-drive-ref" ~/.claude/skills/majiayu000-claude-skill-registry-axiom-icloud-drive-ref && rm -rf "$T"
skills/data/axiom-icloud-drive-ref/SKILL.mdiCloud Drive Reference
Purpose: Comprehensive reference for file-based iCloud sync using ubiquitous containers Availability: iOS 5.0+ (basic), iOS 8.0+ (iCloud Drive), iOS 11.0+ (modern APIs) Context: File-based cloud storage, not database (use CloudKit for structured data)
When to Use This Skill
Use this skill when:
- Implementing document-based iCloud sync
- Syncing user files across devices
- Building document-based apps (like Pages, Numbers)
- Coordinating file access across processes
- Handling iCloud file conflicts
- Using NSUbiquitousKeyValueStore for preferences
NOT for: Structured data with relationships (use
axiom-cloudkit-ref instead)
Overview
iCloud Drive is for FILE-BASED sync, not structured data.
Use when:
- User creates/edits documents
- Files need to sync like Dropbox
- Document picker integration
Don't use when:
- Need queryable structured data (use CloudKit)
- Need relationships between records (use CloudKit)
- Small key-value preferences (use NSUbiquitousKeyValueStore)
Ubiquitous Containers
Getting Ubiquitous Container URL
// ✅ CORRECT: Get iCloud container func getICloudContainerURL() -> URL? { // nil = use first container in entitlements return FileManager.default.url( forUbiquityContainerIdentifier: nil ) } // ✅ Check if iCloud is available if let iCloudURL = getICloudContainerURL() { print("iCloud available: \(iCloudURL)") } else { print("iCloud not available (not signed in or no entitlement)") }
Container Structure
iCloud Container/ ├── Documents/ # User-visible files (Files app) │ └── MyApp/ # Your app's documents ├── Library/ # Hidden from user │ ├── Application Support/ │ └── Caches/
Saving to iCloud Drive
// ✅ CORRECT: Save document to iCloud func saveToICloud(data: Data, filename: String) throws { guard let iCloudURL = FileManager.default.url( forUbiquityContainerIdentifier: nil ) else { throw iCloudError.notAvailable } let documentsURL = iCloudURL.appendingPathComponent("Documents") // Create directory if needed try FileManager.default.createDirectory( at: documentsURL, withIntermediateDirectories: true ) let fileURL = documentsURL.appendingPathComponent(filename) // Use file coordination for safe access let coordinator = NSFileCoordinator() var error: NSError? coordinator.coordinate( writingItemAt: fileURL, options: .forReplacing, error: &error ) { newURL in try? data.write(to: newURL) } if let error = error { throw error } }
File Coordination (Critical for Safety)
Always use NSFileCoordinator when accessing iCloud files. This prevents:
- Race conditions with sync
- Data corruption
- Lost updates
Reading Files
// ✅ CORRECT: Coordinated read func readICloudFile(url: URL) throws -> Data { let coordinator = NSFileCoordinator() var data: Data? var coordinationError: NSError? coordinator.coordinate( readingItemAt: url, options: [], error: &coordinationError ) { newURL in data = try? Data(contentsOf: newURL) } if let error = coordinationError { throw error } guard let data = data else { throw fileError.readFailed } return data }
Writing Files
// ✅ CORRECT: Coordinated write func writeICloudFile(data: Data, to url: URL) throws { let coordinator = NSFileCoordinator() var coordinationError: NSError? coordinator.coordinate( writingItemAt: url, options: .forReplacing, error: &coordinationError ) { newURL in try? data.write(to: newURL) } if let error = coordinationError { throw error } }
Moving Files
// ✅ CORRECT: Coordinated move func moveFile(from sourceURL: URL, to destURL: URL) throws { let coordinator = NSFileCoordinator() var coordinationError: NSError? coordinator.coordinate( writingItemAt: sourceURL, options: .forMoving, writingItemAt: destURL, options: .forReplacing, error: &coordinationError ) { newSource, newDest in try? FileManager.default.moveItem(at: newSource, to: newDest) } if let error = coordinationError { throw error } }
URL Resource Values for iCloud
Checking iCloud Status
// ✅ Check if file is in iCloud func isInICloud(url: URL) -> Bool { let values = try? url.resourceValues(forKeys: [.isUbiquitousItemKey]) return values?.isUbiquitousItem ?? false } // ✅ Check download status func getDownloadStatus(url: URL) -> String { let values = try? url.resourceValues(forKeys: [ .ubiquitousItemDownloadingStatusKey, .ubiquitousItemIsDownloadingKey, .ubiquitousItemDownloadingErrorKey ]) if let downloading = values?.ubiquitousItemIsDownloading, downloading { return "Downloading..." } if let status = values?.ubiquitousItemDownloadingStatus { switch status { case .current: return "Downloaded" case .notDownloaded: return "Not downloaded (iCloud only)" case .downloaded: return "Downloaded" @unknown default: return "Unknown" } } return "Unknown" } // ✅ Check upload status func isUploading(url: URL) -> Bool { let values = try? url.resourceValues(forKeys: [.ubiquitousItemIsUploadingKey]) return values?.ubiquitousItemIsUploading ?? false } // ✅ Check for conflicts func hasConflicts(url: URL) -> Bool { let values = try? url.resourceValues(forKeys: [ .ubiquitousItemHasUnresolvedConflictsKey ]) return values?.ubiquitousItemHasUnresolvedConflicts ?? false }
Downloading Files
// ✅ CORRECT: Request download func downloadFromICloud(url: URL) throws { try FileManager.default.startDownloadingUbiquitousItem(at: url) } // ✅ Monitor download progress let query = NSMetadataQuery() query.predicate = NSPredicate(format: "%K == %@", NSMetadataItemURLKey, url as NSURL) query.searchScopes = [NSMetadataQueryUbiquitousDataScope] NotificationCenter.default.addObserver( forName: .NSMetadataQueryDidUpdate, object: query, queue: .main ) { notification in // Check progress if let item = query.results.first as? NSMetadataItem { if let percent = item.value(forAttribute: NSMetadataUbiquitousItemPercentDownloadedKey) as? Double { print("Downloaded: \(percent)%") } } } query.start()
Conflict Resolution
Detecting Conflicts
// ✅ Get conflict versions func getConflictVersions(for url: URL) -> [NSFileVersion]? { return NSFileVersion.unresolvedConflictVersionsOfItem(at: url) }
Resolving Conflicts
// ✅ CORRECT: Resolve conflicts func resolveConflicts(at url: URL, keepingVersion: ConflictResolution) throws { guard let conflicts = NSFileVersion.unresolvedConflictVersionsOfItem(at: url), !conflicts.isEmpty else { return // No conflicts } let current = try NSFileVersion.currentVersionOfItem(at: url) switch keepingVersion { case .current: // Keep current version, discard others for conflict in conflicts { conflict.isResolved = true } case .other(let chosenVersion): // Replace current with chosen conflict version try chosenVersion.replaceItem(at: url, options: []) chosenVersion.isResolved = true // Mark other conflicts as resolved for conflict in conflicts where conflict != chosenVersion { conflict.isResolved = true } case .manual: // App merges manually, then marks resolved let mergedData = mergeConflicts(current: current, conflicts: conflicts) try mergedData.write(to: url) for conflict in conflicts { conflict.isResolved = true } } // Remove resolved versions try NSFileVersion.removeOtherVersionsOfItem(at: url) } enum ConflictResolution { case current case other(NSFileVersion) case manual }
NSUbiquitousKeyValueStore (Preferences Sync)
For small preferences only (<1 MB total, <1024 keys)
// ✅ CORRECT: Sync small preferences let store = NSUbiquitousKeyValueStore.default // Set values store.set(true, forKey: "darkModeEnabled") store.set(2.0, forKey: "textSizeMultiplier") store.set(["en", "es"], forKey: "selectedLanguages") // Synchronize store.synchronize() // Read values let darkMode = store.bool(forKey: "darkModeEnabled") let textSize = store.double(forKey: "textSizeMultiplier") // Listen for changes from other devices NotificationCenter.default.addObserver( forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: store, queue: .main ) { notification in // Update UI with new values updatePreferences() }
Limitations:
- Total storage: 1 MB
- Max keys: 1024
- Max value size: 1 MB
- Use only for preferences, not data
Entitlements
<!-- iCloud capability --> <key>com.apple.developer.icloud-services</key> <array> <string>CloudDocuments</string> </array> <!-- Ubiquitous containers --> <key>com.apple.developer.ubiquity-container-identifiers</key> <array> <string>iCloud.com.example.app</string> </array> <!-- Key-value store (if using) --> <key>com.apple.developer.ubiquity-kvstore-identifier</key> <string>$(TeamIdentifierPrefix)com.example.app</string>
Common Patterns
Pattern 1: Document Picker Integration
// ✅ Present iCloud document picker import UniformTypeIdentifiers let picker = UIDocumentPickerViewController( forOpeningContentTypes: [.pdf, .plainText] ) picker.delegate = self picker.allowsMultipleSelection = false // Enable iCloud picker.directoryURL = getICloudContainerURL() present(picker, animated: true)
Pattern 2: Monitor Directory for Changes
// ✅ Monitor iCloud directory class ICloudMonitor { let query = NSMetadataQuery() func startMonitoring(directory: URL) { query.predicate = NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, directory.path) query.searchScopes = [NSMetadataQueryUbiquitousDataScope] NotificationCenter.default.addObserver( forName: .NSMetadataQueryDidUpdate, object: query, queue: .main ) { [weak self] _ in self?.processResults() } query.start() } func processResults() { for item in query.results { if let metadataItem = item as? NSMetadataItem, let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as? URL { print("File: \(url.lastPathComponent)") } } } }
Quick Reference
| Task | API | Notes |
|---|---|---|
| Get iCloud URL | | Returns nil if unavailable |
| Check if in iCloud | resource value | Bool |
| Download file | | Async, monitor with NSMetadataQuery |
| Check download status | | current/notDownloaded/downloaded |
| Check for conflicts | | Bool |
| Resolve conflicts | | Manual merge or choose version |
| Sync preferences | | <1 MB total |
| File coordination | | Always use for iCloud files |
Related Skills
— Choose iCloud Drive vs CloudKitaxiom-storage
— For structured data syncaxiom-cloudkit-ref
— Debug iCloud sync issuesaxiom-cloud-sync-diag
Last Updated: 2025-12-12 Skill Type: Reference Minimum iOS: 5.0 (basic), 8.0 (iCloud Drive), 11.0 (modern APIs)