You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中CoreData加载/保存出现重复条目,请求排查问题

CoreData重复条目问题排查:Swift加载保存时去重判断失效

我在开发iOS应用时,使用Swift实现了CoreData的加载与保存功能,但遇到了重复条目问题——我已尝试添加去重判断,但并未生效。恳请各位帮忙排查问题所在,谢谢!

ViewController类代码

import UIKit 
import CoreData 

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 
    @IBOutlet weak var headerLabel:UILabel! 
    @IBOutlet weak var myTableView: UITableView! 
    var lenders = [LenderData]() 
    var lendersTemp = [LenderData]() 

    override func viewDidLoad() { 
        super.viewDidLoad() 
        self.myTableView.rowHeight = 90 
        myTableView.delegate = self 
        myTableView.dataSource = self 

        let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest() 
        do { 
            let lenders = try PersistenceService.context.fetch(fetchRequest) 
            self.lenders = lenders 
        } catch { 
            // Who cares.... 
        } 

        downloadJSON { 
            for tempLender in self.lendersTemp { 
                if !self.lenders.contains(where: {$0.id == tempLender.id}) { 
                    self.lenders.append(tempLender) 
                } 
            } 
            self.lendersTemp.removeAll() 
            PersistenceService.saveContext() 
            self.myTableView.reloadData() 
        } 
    } 

    func downloadJSON(completed: @escaping () -> ()) { 
        let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json") 
        let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in 
            if error != nil { 
                print("JSON not downloaded") 
            } else { 
                if let content = data { 
                    do { 
                        let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 
                        var imageID:Int64 = -1 
                        var country:String = "N/A" 
                        var latLongPair:String = "0.000000 0.000000" 
                        var town:String = "N/A" 

                        if let loans = myJSONData["loans"] as? NSArray { 
                            for i in 0...loans.count-1 { 
                                if let lender = loans[i] as? NSDictionary { 
                                    if let imageData = lender["image"] as? NSDictionary { 
                                        imageID = imageData["id"] as! Int64 
                                    } 
                                    if let countryData = lender["location"] as? NSDictionary { 
                                        country = countryData["country"] as! String 
                                        town = countryData["town"] as! String 
                                        if let geo = countryData["geo"] as? NSDictionary { 
                                            latLongPair = geo["pairs"] as! String 
                                        } 
                                    } 

                                    let newLender = LenderData(context: PersistenceService.context) 
                                    newLender.id = lender["id"] as! Int64 
                                    newLender.name = lender["name"] as? String 
                                    newLender.image_id = imageID 
                                    newLender.activity = lender["activity"] as? String 
                                    newLender.use = lender["use"] as? String 
                                    newLender.loan_amount = lender["loan_amount"] as! Int32 
                                    newLender.funded_amount = lender["funded_amount"] as! Int32 
                                    newLender.country = country 
                                    newLender.town = town 
                                    newLender.geo_pairs = latLongPair 
                                    self.lendersTemp.append(newLender) 
                                } 
                            } 
                        } 
                        DispatchQueue.main.async { 
                            completed() 
                        } 
                    } catch { 
                        print("Error occured \(error)") 
                    } 
                } 
            } 
        } 
        task.resume() 
    } 
}

编辑说明

补充了填充lendersTemp数组的代码部分的内容


嘿,我看了你遇到的CoreData重复问题,咱们一步步来捋清楚问题出在哪:

核心问题:你提前把实例加入了CoreData上下文

你现在的逻辑是先创建newLender = LenderData(context: PersistenceService.context),这行代码会直接把新实例添加到CoreData的上下文中——哪怕你之后通过数组判断没把它加入lenders,最后调用saveContext()的时候,上下文里所有的对象(包括这些你以为“被过滤掉”的重复实例)都会被写入数据库,这就是为什么去重判断完全失效的原因!

解决方案:先检查再创建,避免提前加入上下文

我们需要调整逻辑:在创建新的LenderData实例之前,先检查CoreData数据库里是否已经存在对应ID的条目,只有不存在的时候才创建并添加到上下文。

修改downloadJSON里的循环部分代码如下:

if let loans = myJSONData["loans"] as? NSArray {
    for i in 0...loans.count-1 {
        if let lender = loans[i] as? NSDictionary {
            // 先取出当前贷款的ID
            let loanID = lender["id"] as! Int64
            
            // 构造查询请求,检查数据库中是否已有该ID的条目
            let checkFetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
            checkFetchRequest.predicate = NSPredicate(format: "id == %lld", loanID)
            
            do {
                let existingEntries = try PersistenceService.context.fetch(checkFetchRequest)
                // 只有不存在对应条目时,才创建新实例
                if existingEntries.isEmpty {
                    var imageID:Int64 = -1
                    var country:String = "N/A"
                    var latLongPair:String = "0.000000 0.000000"
                    var town:String = "N/A"
                    
                    if let imageData = lender["image"] as? NSDictionary {
                        imageID = imageData["id"] as! Int64
                    }
                    if let countryData = lender["location"] as? NSDictionary {
                        country = countryData["country"] as! String
                        town = countryData["town"] as! String
                        if let geo = countryData["geo"] as? NSDictionary {
                            latLongPair = geo["pairs"] as! String
                        }
                    }
                    
                    let newLender = LenderData(context: PersistenceService.context)
                    newLender.id = loanID
                    newLender.name = lender["name"] as? String
                    newLender.image_id = imageID
                    newLender.activity = lender["activity"] as? String
                    newLender.use = lender["use"] as? String
                    newLender.loan_amount = lender["loan_amount"] as! Int32
                    newLender.funded_amount = lender["funded_amount"] as! Int32
                    newLender.country = country
                    newLender.town = town
                    newLender.geo_pairs = latLongPair
                    
                    // 直接添加到数据源数组
                    self.lenders.append(newLender)
                }
            } catch {
                print("检查重复条目出错: \(error)")
            }
        }
    }
}

同时,你可以把lendersTemp数组删掉了——现在我们只在需要的时候创建实例,直接加入lenders即可,不需要临时数组中转。

额外保障:给ID字段添加唯一约束

为了避免以后代码逻辑出错导致重复数据,建议给CoreData模型里的LenderData实体的id字段设置唯一约束:

  1. 打开你的CoreData模型文件(.xcdatamodeld)
  2. 选中LenderData实体
  3. 在右侧的Data Model Inspector里找到“Constraints”区域
  4. 添加一行约束,属性选择id,类型选择Unique

这样即使代码里的判断漏了,CoreData在保存时也会抛出错误,阻止重复数据写入。

最后调整viewDidLoad里的回调逻辑

原来的回调代码可以简化成这样:

downloadJSON { 
    PersistenceService.saveContext() 
    self.myTableView.reloadData() 
}

因为现在我们已经在创建实例时直接添加到lenders数组了,不需要再遍历临时数组做判断。


内容的提问来源于stack exchange,提问作者IncrediblePony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:00:12