axiom-icloud-drive-ref

iCloud Drive Reference

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "axiom-icloud-drive-ref" with this command: npx skills add fotescodev/ios-agent-skills/fotescodev-ios-agent-skills-axiom-icloud-drive-ref

iCloud 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 FileManager.default.url(forUbiquityContainerIdentifier:)

Returns nil if unavailable

Check if in iCloud .isUbiquitousItemKey resource value Bool

Download file startDownloadingUbiquitousItem(at:)

Async, monitor with NSMetadataQuery

Check download status .ubiquitousItemDownloadingStatusKey

current/notDownloaded/downloaded

Check for conflicts .ubiquitousItemHasUnresolvedConflictsKey

Bool

Resolve conflicts NSFileVersion.unresolvedConflictVersionsOfItem(at:)

Manual merge or choose version

Sync preferences NSUbiquitousKeyValueStore.default

<1 MB total

File coordination NSFileCoordinator

Always use for iCloud files

Related Skills

  • axiom-storage — Choose iCloud Drive vs CloudKit

  • axiom-cloudkit-ref — For structured data sync

  • axiom-cloud-sync-diag — Debug iCloud sync issues

Last Updated: 2025-12-12 Skill Type: Reference Minimum iOS: 5.0 (basic), 8.0 (iCloud Drive), 11.0 (modern APIs)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

axiom-avfoundation-ref

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-swiftui-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-testflight-triage

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

axiom-swiftui-debugging-diag

No summary provided by upstream source.

Repository SourceNeeds Review