第 12 章:错误处理与鲁棒性
do-catch 块处理持久化错误
为什么需要错误处理
在 SwiftData 中进行数据持久化操作时,可能会遇到多种潜在错误场景:
- 磁盘空间不足导致保存失败
- 数据模型版本不匹配引发的迁移错误
- 违反唯一性约束或关系规则的无效操作
- 并发访问冲突
通过 do-catch 结构捕获这些错误,可以增强应用的稳定性并提供有意义的用户反馈。
基本错误处理模式
do {
try modelContext.save()
} catch {
print("保存失败: \(error.localizedDescription)")
// 执行恢复逻辑或通知用户
}
常见错误类型
SwiftData 可能抛出以下典型错误(通过 ModelError 类型):
- 验证错误:属性值不符合约束条件
- 并发冲突:多个线程同时修改同一对象
- 迁移失败:自动迁移无法完成模型变更
- 存储空间不足:设备存储已满
精细化错误处理
do {
try modelContext.save()
} catch let error as ModelError {
switch error {
case .validationFailed(let message):
showAlert(title: "数据无效", message: message)
case .mergeConflict(let conflicts):
handleConflicts(conflicts)
default:
showGenericError(error)
}
} catch {
showGenericError(error)
}
最佳实践建议
- 关键操作必须处理错误:所有
save()、delete()和迁移操作都应包裹在do-catch中 - 提供上下文信息:错误日志应包含操作类型和受影响的对象信息
- 区分用户可恢复错误:如验证错误可提示用户修正,而磁盘空间错误需要系统级处理
- 避免过度捕获:不要静默吞噬所有错误(如开发阶段应让致命错误崩溃)
调试技巧
在开发阶段可启用详细错误日志:
catch {
print("完整错误信息: \(error)")
dump((error as NSError).userInfo) // 查看附加调试信息
fatalError("未处理的错误: \(error)") // 开发时强制中断
}
示例:批量操作错误处理
func deleteCompletedTasks() {
do {
let tasks = try modelContext.fetch(
FetchDescriptor<Task>(predicate: #Predicate { $0.isCompleted })
tasks.forEach { modelContext.delete($0) }
try modelContext.save()
} catch {
print("批量删除失败: \(error)")
// 回滚未保存的更改
modelContext.rollback()
}
}
通过系统的错误处理机制,可以构建出更健壮的 SwiftData 应用程序。
