@FetchRequest使用UUID属性做谓词过滤时更新不可靠如何解决?
问题原因
- 谓词参数类型不匹配:你代码中NSPredicate的第二个比对参数传入的是String类型的固定UUID字符串,但是Core Data的Item实体的
uuid属性是UUID类型。静态查询存储在本地持久化库的数据时,Core Data会自动做隐式类型转换,所以重启App后可以正常查到匹配数据;但运行时监听托管上下文变更做动态谓词匹配时,类型不匹配会导致匹配逻辑失效,仅满足uuid条件的新增对象不会被判定为匹配谓词,因此不会触发@FetchRequest的更新通知。 - iOS14.x版本的底层缺陷:该版本的NSFetchedResultsController(@FetchRequest的底层实现依赖该组件)对UUID类型属性的谓词动态匹配逻辑存在已知bug,即使参数类型正确,部分小版本也可能出现不触发更新的问题,该问题在iOS15及以上系统已被官方修复。
解决方案
方案1:修正谓词参数类型(优先推荐)
将谓词中的比对参数改为UUID类型,不要直接传入字符串,修改后的@FetchRequest代码如下:
@FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.number, ascending: true)], predicate: NSPredicate(format: "number == %@ OR uuid == %@", NSNumber(5), UUID(uuidString: filterUUID)! as CVarArg)) var items : FetchedResults<Item>
绝大多数场景下修改完参数类型即可解决动态更新异常的问题。
方案2:强制刷新FetchRequest
如果修改参数类型后仍有异常(仅出现在部分iOS14小版本),可以给FetchRequest绑定一个动态id,每次新增数据完成后修改id强制重新执行查询:
struct ItemList: View { @State private var fetchId = UUID() // 其他属性... @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.number, ascending: true)], predicate: NSPredicate(format: "number == %@ OR uuid == %@", NSNumber(5), UUID(uuidString: ItemList.filterUUID)! as CVarArg)) var items : FetchedResults<Item> var body: some View { NavigationView { // 原有列表代码... } .id(fetchId) // 给根视图绑定id } private func addItem(number: Int) { viewContext.perform { let newItem = Item(context: viewContext) newItem.uuid = UUID(uuidString: ItemList.filterUUID) newItem.number = Int16(number) try! viewContext.save() // 保存完成后刷新id,强制重新执行查询 DispatchQueue.main.async { fetchId = UUID() } } } }
方案3:修改Core Data属性类型
如果需要兼容更低版本系统,也可以直接把Item实体的uuid属性类型改为String,存储时直接保存UUID的字符串值,谓词匹配时直接传入字符串即可,彻底避免类型转换带来的匹配异常问题。
内容的提问来源于stack exchange,提问作者4D45
相关产品推荐
相关产品推荐

