axiom-sqlitedata-migration by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-sqlitedata-migration┌─────────────────────────────────────────────────────────┐
│ 我应该从 SwiftData 切换到 SQLiteData 吗? │
├─────────────────────────────────────────────────────────┤
│ │
│ 处理 1 万条以上记录时遇到性能问题? │
│ 是 → SQLiteData(大数据集性能提升 10-50 倍) │
│ │
│ 需要 CloudKit 记录共享(不仅仅是同步)? │
│ 是 → SQLiteData(SwiftData 无法共享记录) │
│ │
│ 需要跨多个表的复杂查询? │
│ 是 → SQLiteData + 必要时使用原始 GRDB │
│ │
│ 需要适用于 Swift 6 并发模型的 Sendable 模型? │
│ 是 → SQLiteData(值类型,而非类) │
│ │
│ 测试 @Model 类很痛苦? │
│ 是 → SQLiteData(纯结构体,易于模拟) │
│ │
│ 对 SwiftData 的简单 CRUD 操作感到满意? │
│ 是 → 继续使用 SwiftData(对基础应用更简单) │
│ │
└─────────────────────────────────────────────────────────┘
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| SwiftData | SQLiteData |
|---|---|
@Model class Item | @Table nonisolated struct Item |
@Attribute(.unique) | @Column(primaryKey: true) 或 SQL UNIQUE |
@Relationship var tags: [Tag] | var tagIDs: [Tag.ID] + 连接查询 |
@Query var items: [Item] | @FetchAll var items: [Item] |
@Query(sort: \.title) | @FetchAll(Item.order(by: \.title)) |
@Query(filter: #Predicate { $0.isActive }) | @FetchAll(Item.where(\.isActive)) |
@Environment(\.modelContext) | @Dependency(\.defaultDatabase) |
context.insert(item) | Item.insert { Item.Draft(...) }.execute(db) |
context.delete(item) | Item.find(id).delete().execute(db) |
try context.save() | 在 database.write { } 块中自动执行 |
ModelContainer(for:) | prepareDependencies { $0.defaultDatabase = } |
SwiftData(迁移前)
import SwiftData
@Model
class Task {
var id: UUID
var title: String
var isCompleted: Bool
var project: Project?
init(title: String) {
self.id = UUID()
self.title = title
self.isCompleted = false
}
}
struct TaskListView: View {
@Environment(\.modelContext) private var context
@Query(sort: \.title) private var tasks: [Task]
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
func addTask(_ title: String) {
let task = Task(title: title)
context.insert(task)
}
func deleteTask(_ task: Task) {
context.delete(task)
}
}
SQLiteData(迁移后)
import SQLiteData
@Table
nonisolated struct Task: Identifiable {
let id: UUID
var title = ""
var isCompleted = false
var projectID: Project.ID?
}
struct TaskListView: View {
@Dependency(\.defaultDatabase) var database
@FetchAll(Task.order(by: \.title)) var tasks
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
func addTask(_ title: String) {
try database.write { db in
try Task.insert {
Task.Draft(title: title)
}
.execute(db)
}
}
func deleteTask(_ task: Task) {
try database.write { db in
try Task.find(task.id).delete().execute(db)
}
}
}
主要区别:
class → 使用 nonisolated 的 struct@Model → @Table@Query → @FetchAll@Environment(\.modelContext) → @Dependency(\.defaultDatabase)database.write { } 代码块.Draft 类型进行插入@Relationship → 显式外键 + 连接SwiftData 支持 CloudKit 同步,但不支持共享。SQLiteData 是 Apple 原生框架中唯一支持记录共享的选项。
// 1. 设置带共享功能的 SyncEngine
prepareDependencies {
$0.defaultDatabase = try! appDatabase()
$0.defaultSyncEngine = try SyncEngine(
for: $0.defaultDatabase,
tables: Task.self, Project.self
)
}
// 2. 共享记录
@Dependency(\.defaultSyncEngine) var syncEngine
@State var sharedRecord: SharedRecord?
func shareProject(_ project: Project) async throws {
sharedRecord = try await syncEngine.share(record: project) { share in
share[CKShare.SystemFieldKey.title] = "加入我的项目!"
}
}
// 3. 呈现原生共享界面
.sheet(item: $sharedRecord) { record in
CloudSharingView(sharedRecord: record)
}
共享功能支持: 协作列表、共享工作空间、家庭共享、团队功能。
| 操作 | SwiftData | SQLiteData | 性能提升 |
|---|---|---|---|
| 插入 5 万条记录 | ~4 分钟 | ~45 秒 | 5 倍 |
| 带谓词的 1 万条记录查询 | ~2 秒 | ~50 毫秒 | 40 倍 |
| 内存占用(1 万个对象) | ~80MB | ~20MB | 减少 4 倍 |
| 冷启动(大型数据库) | ~3 秒 | ~200 毫秒 | 15 倍 |
基准测试为近似值,因设备和数据结构而异。
重要提示: 仅进行架构迁移会丢失所有用户数据。您必须从 SwiftData 导出数据并导入到 SQLiteData。
// 1. 从 SwiftData 的后端存储读取所有记录
func migrateExistingData(from modelContext: ModelContext, to database: any DatabaseWriter) throws {
// 获取所有 SwiftData 记录
let descriptor = FetchDescriptor<SwiftDataTask>()
let existingTasks = try modelContext.fetch(descriptor)
// 2. 批量插入到 SQLiteData
try database.write { db in
for task in existingTasks {
try SQLiteTask.insert {
SQLiteTask.Draft(
id: task.id,
title: task.title,
isCompleted: task.isCompleted,
projectID: task.project?.id
)
}
.execute(db)
}
}
// 3. 验证迁移结果
let count = try database.read { db in
try SQLiteTask.fetchCount(db)
}
assert(count == existingTasks.count, "迁移数量不匹配!")
}
迁移检查清单:
您不必一次性迁移所有内容:
// SwiftData:隐式关系
@Relationship var tasks: [Task]
// SQLiteData:显式列 + 查询
// 在子表中:var projectID: Project.ID
// 查询时:Task.where { $0.projectID.eq(#bind(project.id)) }
// SwiftData:@Relationship(deleteRule: .cascade)
// SQLiteData:在 SQL 架构中定义
// "REFERENCES parent(id) ON DELETE CASCADE"
// SwiftData:@Relationship(inverse: \Task.project)
// SQLiteData:手动查询两个方向
let tasks = Task.where { $0.projectID.eq(#bind(project.id)) }
let project = Project.find(task.projectID)
相关技能:
axiom-sqlitedata — 完整的 SQLiteData API 参考axiom-swiftdata — 如果继续使用 Apple 框架时的 SwiftData 模式axiom-grdb — 用于复杂查询的原始 GRDB历史记录: 查看 git log 了解变更
每周安装量
100
代码仓库
GitHub 星标数
606
首次出现
2026 年 1 月 21 日
安全审计
已安装于
opencode82
codex78
cursor76
claude-code76
gemini-cli74
github-copilot69
┌─────────────────────────────────────────────────────────┐
│ Should I switch from SwiftData to SQLiteData? │
├─────────────────────────────────────────────────────────┤
│ │
│ Performance problems with 10k+ records? │
│ YES → SQLiteData (10-50x faster for large datasets) │
│ │
│ Need CloudKit record SHARING (not just sync)? │
│ YES → SQLiteData (SwiftData cannot share records) │
│ │
│ Complex queries across multiple tables? │
│ YES → SQLiteData + raw GRDB when needed │
│ │
│ Need Sendable models for Swift 6 concurrency? │
│ YES → SQLiteData (value types, not classes) │
│ │
│ Testing @Model classes is painful? │
│ YES → SQLiteData (pure structs, easy to mock) │
│ │
│ Happy with SwiftData for simple CRUD? │
│ YES → Stay with SwiftData (simpler for basic apps) │
│ │
└─────────────────────────────────────────────────────────┘
| SwiftData | SQLiteData |
|---|---|
@Model class Item | @Table nonisolated struct Item |
@Attribute(.unique) | @Column(primaryKey: true) or SQL UNIQUE |
@Relationship var tags: [Tag] | var tagIDs: [Tag.ID] + join query |
@Query var items: [Item] |
SwiftData (Before)
import SwiftData
@Model
class Task {
var id: UUID
var title: String
var isCompleted: Bool
var project: Project?
init(title: String) {
self.id = UUID()
self.title = title
self.isCompleted = false
}
}
struct TaskListView: View {
@Environment(\.modelContext) private var context
@Query(sort: \.title) private var tasks: [Task]
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
func addTask(_ title: String) {
let task = Task(title: title)
context.insert(task)
}
func deleteTask(_ task: Task) {
context.delete(task)
}
}
SQLiteData (After)
import SQLiteData
@Table
nonisolated struct Task: Identifiable {
let id: UUID
var title = ""
var isCompleted = false
var projectID: Project.ID?
}
struct TaskListView: View {
@Dependency(\.defaultDatabase) var database
@FetchAll(Task.order(by: \.title)) var tasks
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
func addTask(_ title: String) {
try database.write { db in
try Task.insert {
Task.Draft(title: title)
}
.execute(db)
}
}
func deleteTask(_ task: Task) {
try database.write { db in
try Task.find(task.id).delete().execute(db)
}
}
}
Key differences:
class → struct with nonisolated@Model → @Table@Query → @FetchAll@Environment(\.modelContext) → @Dependency(\.defaultDatabase)database.write { } block.Draft type for insertsSwiftData supports CloudKit sync but NOT sharing. SQLiteData is the only Apple-native option for record sharing.
// 1. Setup SyncEngine with sharing
prepareDependencies {
$0.defaultDatabase = try! appDatabase()
$0.defaultSyncEngine = try SyncEngine(
for: $0.defaultDatabase,
tables: Task.self, Project.self
)
}
// 2. Share a record
@Dependency(\.defaultSyncEngine) var syncEngine
@State var sharedRecord: SharedRecord?
func shareProject(_ project: Project) async throws {
sharedRecord = try await syncEngine.share(record: project) { share in
share[CKShare.SystemFieldKey.title] = "Join my project!"
}
}
// 3. Present native sharing UI
.sheet(item: $sharedRecord) { record in
CloudSharingView(sharedRecord: record)
}
Sharing enables: Collaborative lists, shared workspaces, family sharing, team features.
| Operation | SwiftData | SQLiteData | Improvement |
|---|---|---|---|
| Insert 50k records | ~4 minutes | ~45 seconds | 5x |
| Query 10k with predicate | ~2 seconds | ~50ms | 40x |
| Memory (10k objects) | ~80MB | ~20MB | 4x smaller |
| Cold launch (large DB) | ~3 seconds | ~200ms | 15x |
Benchmarks approximate, vary by device and data shape.
Critical : Schema migration alone loses all user data. You must export from SwiftData and import into SQLiteData.
// 1. Read all records from SwiftData's backing store
func migrateExistingData(from modelContext: ModelContext, to database: any DatabaseWriter) throws {
// Fetch all SwiftData records
let descriptor = FetchDescriptor<SwiftDataTask>()
let existingTasks = try modelContext.fetch(descriptor)
// 2. Bulk insert into SQLiteData
try database.write { db in
for task in existingTasks {
try SQLiteTask.insert {
SQLiteTask.Draft(
id: task.id,
title: task.title,
isCompleted: task.isCompleted,
projectID: task.project?.id
)
}
.execute(db)
}
}
// 3. Verify migration
let count = try database.read { db in
try SQLiteTask.fetchCount(db)
}
assert(count == existingTasks.count, "Migration count mismatch!")
}
Migration checklist:
You don't have to migrate everything at once:
// SwiftData: implicit relationship
@Relationship var tasks: [Task]
// SQLiteData: explicit column + query
// In child: var projectID: Project.ID
// To fetch: Task.where { $0.projectID.eq(#bind(project.id)) }
// SwiftData: @Relationship(deleteRule: .cascade)
// SQLiteData: Define in SQL schema
// "REFERENCES parent(id) ON DELETE CASCADE"
// SwiftData: @Relationship(inverse: \Task.project)
// SQLiteData: Query both directions manually
let tasks = Task.where { $0.projectID.eq(#bind(project.id)) }
let project = Project.find(task.projectID)
Related Skills:
axiom-sqlitedata — Full SQLiteData API referenceaxiom-swiftdata — SwiftData patterns if staying with Apple's frameworkaxiom-grdb — Raw GRDB for complex queriesHistory: See git log for changes
Weekly Installs
100
Repository
GitHub Stars
606
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode82
codex78
cursor76
claude-code76
gemini-cli74
github-copilot69
SQL查询优化指南:PostgreSQL、Snowflake、BigQuery高性能SQL编写技巧与方言参考
998 周安装
@FetchAll var items: [Item]@Query(sort: \.title) | @FetchAll(Item.order(by: \.title)) |
@Query(filter: #Predicate { $0.isActive }) | @FetchAll(Item.where(\.isActive)) |
@Environment(\.modelContext) | @Dependency(\.defaultDatabase) |
context.insert(item) | Item.insert { Item.Draft(...) }.execute(db) |
context.delete(item) | Item.find(id).delete().execute(db) |
try context.save() | Automatic in database.write { } block |
ModelContainer(for:) | prepareDependencies { $0.defaultDatabase = } |
@Relationship → Explicit foreign key + join