CoreData唯一约束冲突检测及相关技术问题咨询
CoreData唯一约束冲突的异常处理问题
我有一个简单的CoreData数据模型,包含name、timestamp、ident字段,其中ident为唯一约束。通过以下示例代码,尝试插入重复数据或修改现有对象为重复项时,会抛出异常:
struct CV: View { @Environment(\.managedObjectContext) private var moc @FetchRequest(sortDescriptors: []) private var persons: FetchedResults<PersonCD> var body: some View { VStack{ Button("add Person A"){ addPerson(id: "A") } Button("set first Person A"){ persons[0].ident = "A" } Button("save Context"){ saveContext() } } } private func saveContext(){ do { try moc.save() } catch { let nsError = error as NSError print(type(of: error)) print("Unresolved error \(nsError), \(nsError.userInfo)") print("d:\(nsError.domain)") print("c: \(nsError.code)") print("ue: \(nsError.underlyingErrors)") print("cn \(nsError.className)") print("ds \(nsError.description)") } } private func addPerson(id: String) { let newPerson = PersonCD(context: moc) newPerson.timestamp = Date() newPerson.ident = id } }
问题
- 仅能获取
NSError时,如何最简单地区分该异常与其他异常并做出响应? - 是否可以获取描述中提及的冲突对象信息,得到重复对象的引用?
解决方案
问题1:区分唯一约束冲突异常
CoreData的错误域固定为NSCocoaErrorDomain,唯一约束冲突对应的错误码是NSManagedObjectValidationError(数值为1600),同时可以通过错误用户信息里的NSValidationErrorKey判断是否是ident字段引发的冲突。
直接的判断逻辑如下:
private func saveContext(){ do { try moc.save() } catch { let nsError = error as NSError // 精准判断ident字段的唯一约束冲突 if nsError.domain == NSCocoaErrorDomain, nsError.code == NSManagedObjectValidationError, let errorKeys = nsError.userInfo[NSValidationErrorKey] as? [String], errorKeys.contains("ident") { // 这里处理唯一约束冲突的逻辑,比如提示用户 print("ident字段重复,请检查输入") } else { // 处理其他类型的CoreData错误或系统错误 print("保存失败:\(nsError.localizedDescription)") } } }
问题2:获取冲突对象引用
可以从NSError的userInfo字典中提取NSValidationErrorObject值,这就是触发冲突的当前对象;如果需要找到与之重复的已有对象,可以通过CoreData查询获取。
示例代码:
private func saveContext(){ do { try moc.save() } catch { let nsError = error as NSError if nsError.domain == NSCocoaErrorDomain, nsError.code == NSManagedObjectValidationError, let errorKeys = nsError.userInfo[NSValidationErrorKey] as? [String], errorKeys.contains("ident") { // 获取触发冲突的对象(新插入或被修改的对象) if let conflictedObject = nsError.userInfo[NSValidationErrorObject] as? PersonCD, let conflictIdent = conflictedObject.ident { print("触发冲突的对象ident:\(conflictIdent)") // 查询上下文内已存在的重复对象 let fetchRequest: NSFetchRequest<PersonCD> = PersonCD.fetchRequest() fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ NSPredicate(format: "ident == %@", conflictIdent), NSPredicate(format: "self != %@", conflictedObject) ]) if let existingObjects = try? moc.fetch(fetchRequest), !existingObjects.isEmpty { let duplicateObject = existingObjects.first! print("已存在的重复对象:\(duplicateObject.ident ?? "")") // 这里可以对重复对象做进一步处理,比如合并数据或提示用户 } } } } }
- 如果是插入新对象导致重复,
conflictedObject是新创建的对象,查询到的是数据库中已存在的旧对象; - 如果是修改现有对象导致和其他对象重复,
conflictedObject是被修改的对象,查询到的是与之冲突的另一个已有对象。
内容的提问来源于stack exchange,提问作者mica
相关产品推荐
相关产品推荐

