【问题标题】:Fetching, parsing JSON objects, then saving to Core Data produces duplicated records获取、解析 JSON 对象,然后保存到 Core Data 会产生重复的记录
【发布时间】:2021-08-06 02:34:36
【问题描述】:

作为 Swift 新手,我正在尝试获取 JSON 对象列表,将它们保存到 Core Data,然后显示在 SwiftUI 列表中:

JSON 对象有一个唯一的数字 uid(在我的 PostgreSQL 后端中充当 PRIMARY KEY)。

我将它们映射到我的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?
}

然后我有一个单例DownloadManager.swift,它会下载 JSON 列表并将对象存储到核心数据中:

func getTopsCombine() {
    guard let url = URL(string: "https://slova.de/ws/top") 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)?

【问题讨论】:

    标签: ios swift core-data swiftui primary-key


    【解决方案1】:

    在 Core Data 中,您必须检查条目是否已存在,创建谓词并获取具有唯一 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
        
    }
    

    【讨论】:

    • 谢谢,但是 - 哎呀,这看起来像下面的许多 SQLite 请求。我的JSON list 实际上有大约 700-1000 名玩家...
    • 或者通过一个(字典结果类型)请求获取所有 UID,将其转换为 Set 并使用交集 API。这很有效。
    【解决方案2】:

    如果您不使用 CloudKit,您可以对 Core Data 实施唯一约束。如果是,则必须根据 Id 自己进行比较。

    要使用唯一约束,您只需将它们添加到受约束实体的数据模型检查器的“约束”部分。请注意,此图像显示了检查器的前一个图标,但它仍然是最右侧的图标:

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

    【讨论】:

    • 我点击了“约束”中的“+”并在那里写了“uid”,这会强制它的唯一性吗? (即当我尝试添加具有相同 uid 值的实体时会抛出错误,对吗?)
    • 我想我现在看到了错误:约束唯一违规
    • 它将做什么取决于您在设置 Core Data 时设置的合并策略。现在可以编译了吗?
    猜你喜欢
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 2012-09-02
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    相关资源
    最近更新 更多