axiom-storage by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-storage目的:所有存储决策的导航中心——数据库与文件、本地与云端、特定位置 iOS 版本:iOS 17+(最新功能需 iOS 26+) 背景:整合了 SwiftData(WWDC 2023)、CKSyncEngine(WWDC 2023)和文件管理最佳实践的完整存储决策框架
✅ 在以下情况使用此技能:
❌ 请勿将此技能用于:
axiom-swiftdata 技能)axiom-sqlitedata 或 axiom-grdb 技能)axiom-cloudkit-ref 技能)axiom-file-protection-ref 技能)广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
相关技能:
axiom-swiftdata, axiom-sqlitedata, axiom-grdbaxiom-file-protection-ref, axiom-storage-management-ref, axiom-storage-diagaxiom-cloudkit-ref, axiom-icloud-drive-ref, axiom-cloud-sync-diag“为你的数据形态选择合适的工具。然后选择合适的位置。”
存储决策有两个维度:
选错格式会导致需要变通方案。选错位置会导致数据丢失或备份臃肿。
What is the shape of your data?
├─ STRUCTURED DATA (queryable records, relationships, search)
│ Examples: User profiles, task lists, notes, contacts, transactions
│ → Continue to "Structured Data Path" below
│
└─ FILES (documents, images, videos, downloads, caches)
Examples: Photos, PDFs, downloaded content, thumbnails, temp files
→ Continue to "File Storage Path" below
// ✅ CORRECT: SwiftData for modern structured persistence
import SwiftData
@Model
class Task {
var title: String
var isCompleted: Bool
var dueDate: Date
init(title: String, isCompleted: Bool = false, dueDate: Date) {
self.title = title
self.isCompleted = isCompleted
self.dueDate = dueDate
}
}
// Query with type safety
@Query(sort: \Task.dueDate) var tasks: [Task]
为何选择 SwiftData:
axiom-swiftdata 获取实现细节何时不应使用 SwiftData:
// ✅ CORRECT: SQLiteData or GRDB for advanced features
import SQLiteData
// Full-text search, custom indices, raw SQL when needed
let results = try db.prepare("SELECT * FROM users WHERE name MATCH ?", "John")
在以下情况使用 SQLiteData:
axiom-sqlitedata 获取现代 SQLite 模式在以下情况使用 GRDB:
axiom-grdb 获取高级模式// ❌ LEGACY: Core Data (avoid for new projects)
import CoreData
// NSManagedObject, NSFetchRequest, NSPredicate...
仅在以下情况使用 Core Data:
What kind of file is it?
├─ USER-CREATED CONTENT (documents, photos created by user)
│ Where: Documents/ directory
│ Backed up: ✅ Yes (iCloud/iTunes)
│ Purged: ❌ Never
│ Visible in Files app: ✅ Yes
│ Example: User's edited photos, documents, exported data
│ → See "Documents Directory" section below
│
├─ APP-GENERATED DATA (not user-visible, must persist)
│ Where: Library/Application Support/
│ Backed up: ✅ Yes
│ Purged: ❌ Never
│ Visible in Files app: ❌ No
│ Example: Database files, user settings, downloaded assets
│ → See "Application Support Directory" section below
│
├─ RE-DOWNLOADABLE / REGENERABLE CONTENT
│ Where: Library/Caches/
│ Backed up: ❌ No (set isExcludedFromBackup)
│ Purged: ✅ Yes (under storage pressure)
│ Example: Thumbnails, API responses, downloaded images
│ → See "Caches Directory" section below
│
└─ TEMPORARY FILES (can be deleted anytime)
Where: tmp/
Backed up: ❌ No
Purged: ✅ Yes (aggressive, even while app running)
Example: Image processing intermediates, export staging
→ See "Temporary Directory" section below
// ✅ CORRECT: User-created content in Documents
func saveUserDocument(_ data: Data, filename: String) throws {
let documentsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
)[0]
let fileURL = documentsURL.appendingPathComponent(filename)
// Enable file protection
try data.write(to: fileURL, options: .completeFileProtection)
}
关键规则:
使用技能:axiom-file-protection-ref 获取加密选项
// ✅ CORRECT: App data in Application Support
func getAppDataURL() -> URL {
let appSupportURL = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
)[0]
// Create app-specific subdirectory
let appDataURL = appSupportURL.appendingPathComponent(
Bundle.main.bundleIdentifier ?? "AppData"
)
try? FileManager.default.createDirectory(
at: appDataURL,
withIntermediateDirectories: true
)
return appDataURL
}
用于存储:
// ✅ CORRECT: Re-downloadable content in Caches
func cacheDownloadedImage(data: Data, for url: URL) throws {
let cacheURL = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let filename = url.lastPathComponent
let fileURL = cacheURL.appendingPathComponent(filename)
try data.write(to: fileURL)
// Mark as excluded from backup (explicit, though Caches is auto-excluded)
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try fileURL.setResourceValues(resourceValues)
}
关键规则:
使用技能:axiom-storage-management-ref 获取清理策略和磁盘空间管理
// ✅ CORRECT: Truly temporary files in tmp
func processImageWithTempFile(image: UIImage) throws {
let tmpURL = FileManager.default.temporaryDirectory
let tempFileURL = tmpURL.appendingPathComponent(UUID().uuidString + ".jpg")
// Write temp file
try image.jpegData(compressionQuality: 0.8)?.write(to: tempFileURL)
// Process...
processImage(at: tempFileURL)
// Clean up (though system will auto-clean eventually)
try? FileManager.default.removeItem(at: tempFileURL)
}
关键规则:
Does this data need to sync across user's devices?
├─ NO → Use local storage (paths above)
│
└─ YES → What kind of data?
│
├─ STRUCTURED DATA (queryable, relationships)
│ → Use CloudKit
│ → See "CloudKit Path" below
│
├─ FILES (documents, images)
│ → Use iCloud Drive (ubiquitous containers)
│ → See "iCloud Drive Path" below
│
└─ SMALL PREFERENCES (<1 MB, key-value pairs)
→ Use NSUbiquitousKeyValueStore
→ See "Key-Value Store" below
// ✅ CORRECT: SwiftData with CloudKit sync (iOS 17+)
import SwiftData
let container = try ModelContainer(
for: Task.self,
configurations: ModelConfiguration(
cloudKitDatabase: .private("iCloud.com.example.app")
)
)
三种 CloudKit 方法:
SwiftData + CloudKit(推荐,iOS 17+):
axiom-swiftdata 获取细节CKSyncEngine(自定义持久化,iOS 17+):
axiom-cloudkit-ref 获取 CKSyncEngine 模式原始 CloudKit API(遗留):
axiom-cloudkit-ref 获取原始 API 参考// ✅ CORRECT: iCloud Drive for file-based sync
func saveToICloud(_ data: Data, filename: String) throws {
// Get ubiquitous container
guard let iCloudURL = FileManager.default.url(
forUbiquityContainerIdentifier: nil
) else {
throw StorageError.iCloudUnavailable
}
let documentsURL = iCloudURL.appendingPathComponent("Documents")
try FileManager.default.createDirectory(
at: documentsURL,
withIntermediateDirectories: true
)
let fileURL = documentsURL.appendingPathComponent(filename)
try data.write(to: fileURL)
}
何时使用 iCloud Drive:
使用技能:axiom-icloud-drive-ref 获取实现细节
// ✅ CORRECT: Small synced preferences
let store = NSUbiquitousKeyValueStore.default
store.set(true, forKey: "darkModeEnabled")
store.set(2.0, forKey: "textSize")
store.synchronize()
限制:
// ✅ CORRECT: Structured data → SwiftData
@Model
class Note {
var title: String
var content: String
var tags: [Tag] // Relationships
}
// ✅ CORRECT: Files → FileManager + proper directory
let imageData = capturedPhoto.jpegData(compressionQuality: 0.9)
try imageData?.write(to: documentsURL.appendingPathComponent("photo.jpg"))
// ❌ WRONG: Storing queryable data as JSON files
let tasks = [Task(...), Task(...), Task(...)]
let jsonData = try JSONEncoder().encode(tasks)
try jsonData.write(to: appSupportURL.appendingPathComponent("tasks.json"))
// Why it's wrong:
// - Can't query individual tasks
// - Can't filter or sort efficiently
// - No relationships
// - Entire file loaded into memory
// - Concurrent access issues
// ✅ CORRECT: Use SwiftData instead
@Model class Task { ... }
// ❌ WRONG: Downloaded images in Documents (bloats backup!)
func downloadProfileImage(url: URL) throws {
let data = try Data(contentsOf: url)
let documentsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
)[0]
try data.write(to: documentsURL.appendingPathComponent("profile.jpg"))
}
// ✅ CORRECT: Use Caches instead
func downloadProfileImage(url: URL) throws {
let data = try Data(contentsOf: url)
let cacheURL = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let fileURL = cacheURL.appendingPathComponent("profile.jpg")
try data.write(to: fileURL)
// Mark excluded from backup
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try fileURL.setResourceValues(resourceValues)
}
// ❌ WRONG: Storing files as CKAssets with manual sync
let asset = CKAsset(fileURL: documentURL)
let record = CKRecord(recordType: "Document")
record["file"] = asset
// ... manual upload, conflict handling, etc.
// ✅ CORRECT: Use iCloud Drive for files
// Files automatically sync via ubiquitous container
try data.write(to: iCloudDocumentsURL.appendingPathComponent("doc.pdf"))
| 数据类型 | 格式 | 本地位置 | 云端同步 | 使用技能 |
|---|---|---|---|---|
| 用户任务、笔记 | 结构化 | 应用程序支持 | SwiftData + CloudKit | axiom-swiftdata → axiom-cloudkit-ref |
| 用户照片(创建) | 文件 | 文档 | iCloud Drive | axiom-file-protection-ref → axiom-icloud-drive-ref |
| 下载的图片 | 文件 | 缓存 | 无(重新下载) | axiom-storage-management-ref |
| 缩略图 | 文件 | 缓存 | 无(重新生成) | axiom-storage-management-ref |
| 数据库文件 | 文件 | 应用程序支持 | CKSyncEngine(如果自定义) | axiom-sqlitedata → axiom-cloudkit-ref |
| 临时处理 | 文件 | tmp | 无 | N/A |
| 用户设置 | 键值 | UserDefaults | NSUbiquitousKeyValueStore | N/A |
tvOS 没有持久化的本地存储。 这会让每个 iOS 开发者措手不及。
| 目录 | tvOS 行为 |
|---|---|
| 文档 | 不存在 |
| 应用程序支持 | 应用未运行时系统可以删除 |
| 缓存 | 系统随时删除 |
| tmp | 系统随时删除 |
| UserDefaults | 500 KB 限制(iOS 上约为 4 MB) |
每次应用启动之间,每个本地文件都可能消失。 你的 tvOS 应用必须能够从零开始。
推荐:使用 iCloud(CloudKit、NSUbiquitousKeyValueStore 或 iCloud Drive)作为主要存储。将本地文件仅视为缓存。完整 tvOS 存储模式请参见 axiom-tvos。
文件消失:
axiom-storage-diag备份过大:
isExcludedFromBackupaxiom-storage-management-ref数据不同步:
axiom-cloud-sync-diagaxiom-icloud-drive-ref, axiom-cloud-sync-diag更改存储方案时:
数据库到数据库(例如,Core Data → SwiftData):
文件到数据库:
本地到云端:
最后更新:2025-12-12 技能类型:学科 相关 WWDC 会议:
每周安装次数
97
仓库
GitHub 星标数
606
首次出现
Jan 21, 2026
安全审计
安装于
opencode80
codex75
claude-code75
gemini-cli74
cursor73
github-copilot71
Purpose : Navigation hub for ALL storage decisions — database vs files, local vs cloud, specific locations iOS Version : iOS 17+ (iOS 26+ for latest features) Context : Complete storage decision framework integrating SwiftData (WWDC 2023), CKSyncEngine (WWDC 2023), and file management best practices
✅ Use this skill when :
❌ Do NOT use this skill for :
axiom-swiftdata skill)axiom-sqlitedata or axiom-grdb skills)axiom-cloudkit-ref skill)axiom-file-protection-ref skill)Related Skills :
axiom-swiftdata, axiom-sqlitedata, axiom-grdbaxiom-file-protection-ref, axiom-storage-management-ref, axiom-storage-diagaxiom-cloudkit-ref, axiom-icloud-drive-ref, axiom-cloud-sync-diag"Choose the right tool for your data shape. Then choose the right location."
Storage decisions have two dimensions:
Getting the format wrong forces workarounds. Getting the location wrong causes data loss or backup bloat.
What is the shape of your data?
├─ STRUCTURED DATA (queryable records, relationships, search)
│ Examples: User profiles, task lists, notes, contacts, transactions
│ → Continue to "Structured Data Path" below
│
└─ FILES (documents, images, videos, downloads, caches)
Examples: Photos, PDFs, downloaded content, thumbnails, temp files
→ Continue to "File Storage Path" below
// ✅ CORRECT: SwiftData for modern structured persistence
import SwiftData
@Model
class Task {
var title: String
var isCompleted: Bool
var dueDate: Date
init(title: String, isCompleted: Bool = false, dueDate: Date) {
self.title = title
self.isCompleted = isCompleted
self.dueDate = dueDate
}
}
// Query with type safety
@Query(sort: \Task.dueDate) var tasks: [Task]
Why SwiftData :
axiom-swiftdata for implementation detailsWhen NOT to use SwiftData :
// ✅ CORRECT: SQLiteData or GRDB for advanced features
import SQLiteData
// Full-text search, custom indices, raw SQL when needed
let results = try db.prepare("SELECT * FROM users WHERE name MATCH ?", "John")
Use SQLiteData when :
axiom-sqlitedata for modern SQLite patternsUse GRDB when :
axiom-grdb for advanced patterns// ❌ LEGACY: Core Data (avoid for new projects)
import CoreData
// NSManagedObject, NSFetchRequest, NSPredicate...
Only use Core Data if :
What kind of file is it?
├─ USER-CREATED CONTENT (documents, photos created by user)
│ Where: Documents/ directory
│ Backed up: ✅ Yes (iCloud/iTunes)
│ Purged: ❌ Never
│ Visible in Files app: ✅ Yes
│ Example: User's edited photos, documents, exported data
│ → See "Documents Directory" section below
│
├─ APP-GENERATED DATA (not user-visible, must persist)
│ Where: Library/Application Support/
│ Backed up: ✅ Yes
│ Purged: ❌ Never
│ Visible in Files app: ❌ No
│ Example: Database files, user settings, downloaded assets
│ → See "Application Support Directory" section below
│
├─ RE-DOWNLOADABLE / REGENERABLE CONTENT
│ Where: Library/Caches/
│ Backed up: ❌ No (set isExcludedFromBackup)
│ Purged: ✅ Yes (under storage pressure)
│ Example: Thumbnails, API responses, downloaded images
│ → See "Caches Directory" section below
│
└─ TEMPORARY FILES (can be deleted anytime)
Where: tmp/
Backed up: ❌ No
Purged: ✅ Yes (aggressive, even while app running)
Example: Image processing intermediates, export staging
→ See "Temporary Directory" section below
// ✅ CORRECT: User-created content in Documents
func saveUserDocument(_ data: Data, filename: String) throws {
let documentsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
)[0]
let fileURL = documentsURL.appendingPathComponent(filename)
// Enable file protection
try data.write(to: fileURL, options: .completeFileProtection)
}
Key rules :
Use skill : axiom-file-protection-ref for encryption options
// ✅ CORRECT: App data in Application Support
func getAppDataURL() -> URL {
let appSupportURL = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
)[0]
// Create app-specific subdirectory
let appDataURL = appSupportURL.appendingPathComponent(
Bundle.main.bundleIdentifier ?? "AppData"
)
try? FileManager.default.createDirectory(
at: appDataURL,
withIntermediateDirectories: true
)
return appDataURL
}
Use for :
// ✅ CORRECT: Re-downloadable content in Caches
func cacheDownloadedImage(data: Data, for url: URL) throws {
let cacheURL = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let filename = url.lastPathComponent
let fileURL = cacheURL.appendingPathComponent(filename)
try data.write(to: fileURL)
// Mark as excluded from backup (explicit, though Caches is auto-excluded)
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try fileURL.setResourceValues(resourceValues)
}
Key rules :
Use skill : axiom-storage-management-ref for purge policies and disk space management
// ✅ CORRECT: Truly temporary files in tmp
func processImageWithTempFile(image: UIImage) throws {
let tmpURL = FileManager.default.temporaryDirectory
let tempFileURL = tmpURL.appendingPathComponent(UUID().uuidString + ".jpg")
// Write temp file
try image.jpegData(compressionQuality: 0.8)?.write(to: tempFileURL)
// Process...
processImage(at: tempFileURL)
// Clean up (though system will auto-clean eventually)
try? FileManager.default.removeItem(at: tempFileURL)
}
Key rules :
Does this data need to sync across user's devices?
├─ NO → Use local storage (paths above)
│
└─ YES → What kind of data?
│
├─ STRUCTURED DATA (queryable, relationships)
│ → Use CloudKit
│ → See "CloudKit Path" below
│
├─ FILES (documents, images)
│ → Use iCloud Drive (ubiquitous containers)
│ → See "iCloud Drive Path" below
│
└─ SMALL PREFERENCES (<1 MB, key-value pairs)
→ Use NSUbiquitousKeyValueStore
→ See "Key-Value Store" below
// ✅ CORRECT: SwiftData with CloudKit sync (iOS 17+)
import SwiftData
let container = try ModelContainer(
for: Task.self,
configurations: ModelConfiguration(
cloudKitDatabase: .private("iCloud.com.example.app")
)
)
Three approaches to CloudKit :
SwiftData + CloudKit (Recommended, iOS 17+):
axiom-swiftdata for detailsCKSyncEngine (Custom persistence, iOS 17+):
axiom-cloudkit-ref for CKSyncEngine patternsRaw CloudKit APIs (Legacy):
axiom-cloudkit-ref for raw API reference// ✅ CORRECT: iCloud Drive for file-based sync
func saveToICloud(_ data: Data, filename: String) throws {
// Get ubiquitous container
guard let iCloudURL = FileManager.default.url(
forUbiquityContainerIdentifier: nil
) else {
throw StorageError.iCloudUnavailable
}
let documentsURL = iCloudURL.appendingPathComponent("Documents")
try FileManager.default.createDirectory(
at: documentsURL,
withIntermediateDirectories: true
)
let fileURL = documentsURL.appendingPathComponent(filename)
try data.write(to: fileURL)
}
When to use iCloud Drive :
Use skill : axiom-icloud-drive-ref for implementation details
// ✅ CORRECT: Small synced preferences
let store = NSUbiquitousKeyValueStore.default
store.set(true, forKey: "darkModeEnabled")
store.set(2.0, forKey: "textSize")
store.synchronize()
Limitations :
// ✅ CORRECT: Structured data → SwiftData
@Model
class Note {
var title: String
var content: String
var tags: [Tag] // Relationships
}
// ✅ CORRECT: Files → FileManager + proper directory
let imageData = capturedPhoto.jpegData(compressionQuality: 0.9)
try imageData?.write(to: documentsURL.appendingPathComponent("photo.jpg"))
// ❌ WRONG: Storing queryable data as JSON files
let tasks = [Task(...), Task(...), Task(...)]
let jsonData = try JSONEncoder().encode(tasks)
try jsonData.write(to: appSupportURL.appendingPathComponent("tasks.json"))
// Why it's wrong:
// - Can't query individual tasks
// - Can't filter or sort efficiently
// - No relationships
// - Entire file loaded into memory
// - Concurrent access issues
// ✅ CORRECT: Use SwiftData instead
@Model class Task { ... }
// ❌ WRONG: Downloaded images in Documents (bloats backup!)
func downloadProfileImage(url: URL) throws {
let data = try Data(contentsOf: url)
let documentsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
)[0]
try data.write(to: documentsURL.appendingPathComponent("profile.jpg"))
}
// ✅ CORRECT: Use Caches instead
func downloadProfileImage(url: URL) throws {
let data = try Data(contentsOf: url)
let cacheURL = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let fileURL = cacheURL.appendingPathComponent("profile.jpg")
try data.write(to: fileURL)
// Mark excluded from backup
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try fileURL.setResourceValues(resourceValues)
}
// ❌ WRONG: Storing files as CKAssets with manual sync
let asset = CKAsset(fileURL: documentURL)
let record = CKRecord(recordType: "Document")
record["file"] = asset
// ... manual upload, conflict handling, etc.
// ✅ CORRECT: Use iCloud Drive for files
// Files automatically sync via ubiquitous container
try data.write(to: iCloudDocumentsURL.appendingPathComponent("doc.pdf"))
| Data Type | Format | Local Location | Cloud Sync | Use Skill |
|---|---|---|---|---|
| User tasks, notes | Structured | Application Support | SwiftData + CloudKit | axiom-swiftdata → axiom-cloudkit-ref |
| User photos (created) | File | Documents | iCloud Drive | axiom-file-protection-ref → axiom-icloud-drive-ref |
| Downloaded images | File | Caches | None (re-download) |
tvOS has no persistent local storage. This catches every iOS developer.
| Directory | tvOS Behavior |
|---|---|
| Documents | Does not exist |
| Application Support | System can delete when app is not running |
| Caches | System deletes at any time |
| tmp | System deletes at any time |
| UserDefaults | 500 KB limit (vs ~4 MB on iOS) |
Every local file can vanish between app launches. Your tvOS app must survive starting from zero.
Recommended : Use iCloud (CloudKit, NSUbiquitousKeyValueStore, or iCloud Drive) as primary storage. Treat local files as cache only. See axiom-tvos for full tvOS storage patterns.
Files disappeared :
axiom-storage-diagBackup too large :
isExcludedFromBackup is set on large filesaxiom-storage-management-refData not syncing :
axiom-cloud-sync-diagaxiom-icloud-drive-ref, axiom-cloud-sync-diagWhen changing storage approach:
Database to Database (e.g., Core Data → SwiftData):
Files to Database :
Local to Cloud :
Last Updated : 2025-12-12 Skill Type : Discipline Related WWDC Sessions :
Weekly Installs
97
Repository
GitHub Stars
606
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode80
codex75
claude-code75
gemini-cli74
cursor73
github-copilot71
SQL查询优化指南:PostgreSQL、Snowflake、BigQuery高性能SQL编写技巧与方言参考
951 周安装
Symfony API Platform序列化指南:合约设计、安全防护与渐进式披露
135 周安装
PostgreSQL只读查询技能 - 安全连接AI助手执行数据库查询,支持SSL加密与权限控制
135 周安装
Next.js服务端与客户端组件选择指南:TypeScript最佳实践与性能优化
135 周安装
项目会话管理器 (PSM) - 使用 Git Worktrees 和 Tmux 自动化隔离开发环境
135 周安装
Memory技能:统一知识图谱记忆管理工具 - 搜索、加载、可视化决策历史
135 周安装
AI会议纪要生成器 - 自动转录稿转结构化纪要,支持发言人识别与多轮审查
135 周安装
axiom-storage-management-ref |
| Thumbnails | File | Caches | None (regenerate) | axiom-storage-management-ref |
| Database file | File | Application Support | CKSyncEngine (if custom) | axiom-sqlitedata → axiom-cloudkit-ref |
| Temp processing | File | tmp | None | N/A |
| User settings | Key-Value | UserDefaults | NSUbiquitousKeyValueStore | N/A |