Ios 如何在获取api响应并存储在coredata中然后在UItableview中显示后消除重复数据

Ios 如何在获取api响应并存储在coredata中然后在UItableview中显示后消除重复数据,ios,swift,xcode,core-data,duplicates,Ios,Swift,Xcode,Core Data,Duplicates,我解析了Api并存储在CoreData中,但当我在UITableView中显示结果时,它会显示两次图像、名称和电子邮件。如何消除重复并仅显示唯一数据。我还添加了一个约束和“context.mergePolicy=NSMergeByPropertyObjectTrumpMergePolicy,它在每次运行后都停止了数据合并,但现在将所有数据显示两次。” 任何帮助都将是非常可观的 import UIKit import CoreData class DatabaseHandle{

我解析了Api并存储在CoreData中,但当我在UITableView中显示结果时,它会显示两次图像、名称和电子邮件。如何消除重复并仅显示唯一数据。我还添加了一个约束和“context.mergePolicy=NSMergeByPropertyObjectTrumpMergePolicy,它在每次运行后都停止了数据合并,但现在将所有数据显示两次。”

任何帮助都将是非常可观的

import UIKit
import CoreData

class DatabaseHandle{
    
    private var viewContext:NSManagedObjectContext
    static let shared = DatabaseHandle()
    init() {
        viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    }
    func add<T: NSManagedObject>(_ type: T.Type)-> T?{
        guard let entityName = T.entity().name else {return nil}
        guard let entity = NSEntityDescription.entity(forEntityName: entityName, in: viewContext) else{return nil}
        let object = T(entity: entity, insertInto: viewContext)
        return object
        }
    
    func fetch<T:NSManagedObject>(_ type: T.Type) -> [T] {
        let request = T.fetchRequest()
        do{
            let result = try viewContext.fetch(request)
            
            return result as! [T]
        }catch{
            print(error.localizedDescription)
            return []
        }
        
    }
    
    func save(){
        do{
            try viewContext.save()
        }catch{
            print(error.localizedDescription)
        }
    }
    
    func delete<T:NSManagedObject>(object: T){
        viewContext.delete(object)
        save()
    }
}
视图控制器

import UIKit
import Kingfisher

class ViewController: UIViewController {

    @IBOutlet weak var textName: UITextField!
    @IBOutlet weak var textAge: UITextField!
    @IBOutlet weak var tableView: UITableView!
    
    let database = DatabaseHandle.shared
    var users: [User]?{
        didSet{
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //tableView.register(UserTableViewCell.self, forCellReuseIdentifier: "UserTableViewCell")
        tableView.tableFooterView = UIView(frame: .zero)
    }
    override func viewWillAppear(_ animated: Bool) {
        users = database.fetch(User.self)
        
        //print(users)
    }
    override func viewDidAppear(_ animated: Bool) {
        ApiHandler.shared.syncUser {
            self.users = self.database.fetch(User.self)
        }
    }
    
}
extension ViewController: UITableViewDataSource,UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return users?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UserTableViewCell
        
        cell.user = users?[indexPath.row]
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        guard let user = users?[indexPath.row] else {return}
        tableView.beginUpdates()
        self.database.delete(object: user)
        users?.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        tableView.endUpdates()
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60
    }
}
**养蜂人**

import UIKit

class ApiHandler{
    
    static let shared = ApiHandler()
    
    func syncUser(completion: @escaping (() -> Void)){
        var req = URLRequest(url: URL(string: "https://reqres.in/api/users?page=2")!)
        req.httpMethod = "GET"
        let session = URLSession.shared
        
        let task = session.dataTask(with: req, completionHandler: { data,response,error -> Void in
            print(response!)
            
            do{
                let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary <String,AnyObject>
                let model = try JSONDecoder().decode(ApiResponse<[UserServerModel]>.self, from: data!)
                model.data.forEach({$0.store()})
                print(json)
                completion()
            }catch{
                print(error.localizedDescription)
                completion()
            }
            
            
        })
        task.resume()
        
    }
}

public struct ApiResponse<T: Codable>: Codable {
    public let total_pages: Int
    public let per_page: Int
    public let data: T
    public let page: Int
    public let total: Int
    
}
导入UIKit
类ApiHandler{
静态let shared=apichandler()
func syncUser(完成:@escaping(()->Void)){
var req=URLRequest(url:url(字符串):https://reqres.in/api/users?page=2")!)
req.httpMethod=“获取”
让session=URLSession.shared
让task=session.dataTask(带:req,completionHandler:{data,response,error->Void in)
打印(响应!)
做{
让json=try JSONSerialization.jsonObject(使用:data!)作为!字典
让model=try JSONDecoder().decode(ApiResponse.self,from:data!)
model.data.forEach({$0.store()})
打印(json)
完成()
}抓住{
打印(错误。本地化描述)
完成()
}
})
task.resume()
}
}
公共结构ApiResponse:可编码{
公共出租总页数:Int
公共出租每页:Int
公共let数据:T
公共页:Int
公共租赁总额:整数
}
数据库句柄

import UIKit
import CoreData

class DatabaseHandle{
    
    private var viewContext:NSManagedObjectContext
    static let shared = DatabaseHandle()
    init() {
        viewContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    }
    func add<T: NSManagedObject>(_ type: T.Type)-> T?{
        guard let entityName = T.entity().name else {return nil}
        guard let entity = NSEntityDescription.entity(forEntityName: entityName, in: viewContext) else{return nil}
        let object = T(entity: entity, insertInto: viewContext)
        return object
        }
    
    func fetch<T:NSManagedObject>(_ type: T.Type) -> [T] {
        let request = T.fetchRequest()
        do{
            let result = try viewContext.fetch(request)
            
            return result as! [T]
        }catch{
            print(error.localizedDescription)
            return []
        }
        
    }
    
    func save(){
        do{
            try viewContext.save()
        }catch{
            print(error.localizedDescription)
        }
    }
    
    func delete<T:NSManagedObject>(object: T){
        viewContext.delete(object)
        save()
    }
}
导入UIKit
导入CoreData
类数据库句柄{
私有变量viewContext:NSManagedObjectContext
静态let shared=DatabaseHandle()
init(){
viewContext=(UIApplication.shared.delegate为!AppDelegate)。persistentContainer.viewContext
}
func add(uype:T.type)->T{
guard let entityName=T.entity().name else{return nil}
guard let entity=NSEntityDescription.entity(forEntityName:entityName,in:viewContext)else{return nil}
let object=T(实体:实体,插入到:viewContext)
回波信号
}
func fetch(utype:T.type)->[T]{
let request=T.fetchRequest()
做{
let result=尝试viewContext.fetch(请求)
返回结果为![T]
}抓住{
打印(错误。本地化描述)
返回[]
}
}
func save(){
做{
请尝试viewContext.save()
}抓住{
打印(错误。本地化描述)
}
}
func delete(对象:T){
viewContext.delete(对象)
保存()
}
}
当你打电话时

model.data.forEach({$0.store()})

我不确定store是否是一个自定义函数,但可能您没有首先检查它是否已经存在于coredata中,因此您只需保存它两次。

正如其他人所提到的,在添加新记录之前,您需要检查持久层是否存在记录。我建议实现一个协议,以便您的其他API数据结构也可以持久化

protocol Persistable {
    associatedtype Persisted: NSManagedObject
    var predicate: NSPredicate? { get }
    func persist(into object: Persisted)
}

extension NSPredicate {
    static func userId(equalTo id: Int16) -> NSPredicate {
        return .init(format: "id == %d", id)
    }
}

extension UserServerModel: Persistable {

    var predicate: NSPredicate? { .userId(equalTo: Int16(self.id)) }

    func persist(into object: User) {
        object.avatar = avatar
        object.email = email
        object.first_name = first_name
        object.last_name = last_name
        object.id = Int16(id)
    }
}
我不喜欢将存储库逻辑与模型混合在一起,所以我会在API和持久性模型之间建立某种桥梁

enum PersistenceBridge {

    @discardableResult
    static func updateOrCreate<T: Persistable>(model: T) -> T.Persisted? {
        return update(model: model) ?? create(model: model)
    }

    @discardableResult
    static func update<T: Persistable>(model: T) -> T.Persisted? {
        let object = DatabaseHandle.shared.fetch(T.Persisted.self, predicate: model.predicate).first
        return persist(model: model, into: object)
    }

    @discardableResult
    static func create<T: Persistable>(model: T) -> T.Persisted? {
        let object = DatabaseHandle.shared.add(T.Persisted.self)
        return persist(model: model, into: object)
    }

    static func persist<T: Persistable>(model: T, into object: T.Persisted?) -> T.Persisted? {
        if let object = object { model.persist(into: object) }
        return object
    }
}
enum PersistenceBridge{
@可丢弃结果
静态函数updateOrCreate(模型:T)->T。是否持久化{
返回更新(模型:模型)??创建(模型:模型)
}
@可丢弃结果
静态函数更新(型号:T)->T。是否持久化{
让object=DatabaseHandle.shared.fetch(T.persistend.self,谓词:model.predicate)
返回persist(model:model,into:object)
}
@可丢弃结果
静态函数create(model:T)->T.是否持久化{
让对象=DatabaseHandle.shared.add(T.persistend.self)
返回persist(model:model,into:object)
}
静态函数持久化(模型:T,进入对象:T.持久化?->T.持久化{
如果让object=object{model.persist(into:object)}
回波信号
}
}
您需要向fetch方法添加一个谓词参数,以便约束结果:

extension DatabaseHandle {

    func fetch<T:NSManagedObject>(_ type: T.Type, predicate: NSPredicate? = nil) -> [T] {
        let request = T.fetchRequest()
        request.predicate = predicate
        do{
            let result = try viewContext.fetch(request)

            return result as! [T]
        }catch{
            print(error.localizedDescription)
            return []
        }
    }
}
扩展数据库句柄{ func fetch(u类型:T.type,谓词:NSPredicate?=nil)->[T]{ let request=T.fetchRequest() request.predicate=谓词 做{ let result=尝试viewContext.fetch(请求) 返回结果为![T] }抓住{ 打印(错误。本地化描述) 返回[] } } } 最后,向ApiHandler添加一些持久性逻辑,就可以开始了:

extension ApiHandler {
    func persist<T: Persistable>(response: ApiResponse<[T]>) {
        response.data.forEach {
            PersistenceBridge.updateOrCreate(model: $0)
        }
    }
}
扩展处理程序{ func持久化(响应:ApiResponse){ response.data.forEach{ PersistenceBridge.updateOrCreate(模型:$0) } } }
yes存储是一项自定义功能<代码>导入基础结构SubServer模型:可合并{var avaar:字符串VaR第一名称:StrasVAR LASTHYND:String VAR ID:Int静态允许数据库= DabaseAsHelle。共享FUNC存储(){HeaveEnter = UsServer模型。数据库。user.avatar=avatar user.email=email user.first\u name=first\u name user.last\u name=last\u name user.id=Int16(id)UserServerModel.database.save()}为什么要在guard语句中添加用户?除非我在user中使用guard语句,否则我必须打开应用
user.avatar=avatar user.email=email user.first\u name=first\u name user.last\u name=last\u name user.id=Int16(id)
在某个地方,我必须添加一个条件来检查重复元素,并在存储到coredata之前删除该元素,我不需要