axiom-icloud-drive-ref

安装量: 116
排名: #7396

安装

npx skills add https://github.com/charleswiltgen/axiom --skill 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

com.apple.developer.icloud-services CloudDocuments

com.apple.developer.ubiquity-container-identifiers iCloud.com.example.app

com.apple.developer.ubiquity-kvstore-identifier $(TeamIdentifierPrefix)com.example.app

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)

返回排行榜