SwiftData如何插入新项且不更新已有数据、不产生重复?
解决SwiftData刷新数据时覆盖本地自定义字段的问题
核心思路是不要直接插入API返回的新对象,而是先检查本地是否已有该电影,有则只更新API返回的公共字段(不碰favorite这类本地自定义字段),没有再插入新对象。
具体实现步骤:
- 遍历API返回的电影列表,对每个电影通过唯一标识
code查询本地数据库 - 如果本地存在该电影:只更新
name等API返回的字段,保留原有的favorite值 - 如果本地不存在:直接插入新创建的电影对象(此时
favorite默认是false,符合需求)
基础实现代码
func syncMovies(from apiMovies: [Movie], using context: ModelContext) { for apiMovie in apiMovies { // 通过唯一code查询本地是否存在该电影 let predicate = #Predicate<Movie> { $0.code == apiMovie.code } let fetchDescriptor = FetchDescriptor<Movie>(predicate: predicate) if let localMovie = try? context.fetch(fetchDescriptor).first { // 只更新API返回的字段,跳过favorite localMovie.name = apiMovie.name // 其他需要同步的字段(比如简介、上映日期等)也在这里更新 } else { // 本地没有该电影,插入新对象 context.insert(apiMovie) } } // 保存上下文 do { try context.save() } catch { print("同步电影失败:\(error)") } }
优化查询效率的版本
如果本地电影数量较多,可以先批量获取所有本地电影的code集合,避免循环内重复查询数据库:
func syncMovies(from apiMovies: [Movie], using context: ModelContext) { // 先获取本地所有电影的code集合 let allLocalCodes = try? context.fetch(FetchDescriptor<Movie>()).map { $0.code } let localCodeSet = Set(allLocalCodes ?? []) for apiMovie in apiMovies { if localCodeSet.contains(apiMovie.code) { // 本地存在,查询并更新字段 let predicate = #Predicate<Movie> { $0.code == apiMovie.code } if let localMovie = try? context.fetch(FetchDescriptor<Movie>(predicate: predicate)).first { localMovie.name = apiMovie.name // 其他需要同步的字段更新 } } else { context.insert(apiMovie) } } try? context.save() }
这样既可以保证API数据正常刷新,又不会覆盖用户标记的favorite状态,同时新电影默认的favorite为false也符合需求。
内容的提问来源于stack exchange,提问作者Kawameicha
相关产品推荐
相关产品推荐

