swift 获取、解析JSON对象,然后保存到Core Data会产生重复的记录

sc4hvdpw  于 2023-08-02  发布在  Swift
关注(0)|答案(2)|浏览(115)

作为一个Swift新手,我尝试获取一个JSON对象列表,将它们保存到Core Data,然后在SwiftUI列表中显示:
x1c 0d1x的数据
JSON对象有一个唯一的数字uid(在我的PostgreSQL后端中充当主键)。
我将它们Map到TopModel.swift中的id

struct Top: Codable, Identifiable {
    var id: Int { uid }
    let uid: Int
    let elo: Int
    let given: String
    let photo: String?
    let motto: String?
    let avg_score: Double?
    let avg_time: String?
}

字符串
然后我有一个singleton DownloadManager.swift,它下载JSON列表并将对象存储到Core Data中:

func getTopsCombine() {
    guard let url = URL(string: "https://wordsbyfarber.com/ru/top-all") else { return }
    URLSession.shared.dataTaskPublisher(for: url)
        .subscribe(on: DispatchQueue.global(qos: .background))
        .receive(on: DispatchQueue.main)
        .tryMap(handleOutput)
        .decode(type: TopResponse.self, decoder: JSONDecoder())
        .sink{ ( completion ) in
            print(completion)
        } receiveValue: { [weak self] (returnedTops) in
            for top in returnedTops.data {
                print(top)
                let topEntity = TopEntity(context: PersistenceController.shared.container.viewContext)
                topEntity.uid = Int32(top.id)
                topEntity.elo = Int32(top.elo)
                topEntity.given = top.given
                topEntity.motto = top.motto
                topEntity.photo = top.photo
                topEntity.avg_score = top.avg_score ?? 0.0
                topEntity.avg_time = top.avg_time
            }
            self?.save()
        }
        .store(in: &cancellables)
}


不幸的是,这会导致在Core Data中保存多个相同的记录(如您在上面的屏幕截图中所见)。
这里的标准iOS解决方案是什么?
如何实现唯一性(在我最初的Android/Room应用中,我使用@PrimaryKey public int uid;)?我在Xcode中没有看到这样的选项:



以及如何合并JSON对象的提取列表(在我的Android应用中,我使用DiffUtils)?

lf5gs5x2

lf5gs5x21#

在Core Data中,您必须检查条目是否已经存在,创建一个 predicate 并获取具有唯一ID的对象。如果没有项目,则创建一个。

for top in returnedTops.data {
    let context = PersistenceController.shared.container.viewContext
    let request : FetchRequest<TopEntity> = TopEntity.fetchRequest()
    request.predicate = NSPredicate(format: "uid == %ld", top.id)
    if try? context.fetch(request)?.first == nil {
        print(top)            
        let topEntity = TopEntity(context: context)
        topEntity.uid = Int32(top.id)
        topEntity.elo = Int32(top.elo)
        topEntity.given = top.given
        topEntity.motto = top.motto
        topEntity.photo = top.photo
        topEntity.avg_score = top.avg_score ?? 0.0
        topEntity.avg_time = top.avg_time
    }
}

字符串
您甚至可以更新项(如果它存在)

for top in returnedTops.data {
    let context = PersistenceController.shared.container.viewContext
    let request : FetchRequest<TopEntity> = TopEntity.fetchRequest()
    request.predicate = NSPredicate(format: "uid == %ld", top.id)
    let topEntity : TopEntity
    if let result = try? context.fetch(request)?.first {
       topEntity = result
    } else {
        print(top)            
        topEntity = TopEntity(context: context)
    }
    topEntity.uid = Int32(top.id)
    topEntity.elo = Int32(top.elo)
    topEntity.given = top.given
    topEntity.motto = top.motto
    topEntity.photo = top.photo
    topEntity.avg_score = top.avg_score ?? 0.0
    topEntity.avg_time = top.avg_time
    
}

xkrw2x1b

xkrw2x1b2#

如果您没有使用CloudKit,则可以对Core Data实施唯一约束。如果你是,你将不得不根据身份证自己进行比较。
对于使用唯一约束,您只需将它们添加到受约束实体的数据模型检查器的“约束”部分。请注意,此图像显示了检查器的前一个图标,但它仍然是最右边的图标:


的数据
如果您计划使用CloudKit,则不能使用约束,因为CloudKit中不允许使用约束。因此,您需要编写一个函数来唯一化数据,并在数据写入存储后调用该函数。这听起来像是因为你是跨平台,所以你不会使用CloudKit,所以上面的方法应该可以正常工作。

相关问题