如何在获取 api 响应并存储在 coredata 中然后在 UItableview 中显示后消除重复数据
Posted
技术标签:
【中文标题】如何在获取 api 响应并存储在 coredata 中然后在 UItableview 中显示后消除重复数据【英文标题】:How to eliminate duplicate data after fetching api response and stored in coredata then display in UItableview 【发布时间】:2020-10-30 18:14:10 【问题描述】:我解析了 Api 并存储在 CoreData 中,但是当我在 UITableView 中显示结果时,它会显示图像、姓名和电子邮件两次。如何消除重复并仅显示唯一数据。我还添加了一个约束和“context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy,它在每次运行后停止合并数据,但现在它显示所有数据两次。”
任何帮助都将不胜感激
视图控制器
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
**ApiHandler **
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
数据库句柄
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) elsereturn 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()
【问题讨论】:
【参考方案1】:当你打电话时
model.data.forEach($0.store())
我不确定 store 是否是一个自定义函数,但也许你没有首先检查它是否已经存在于 coredata 中,所以你最终只是保存了两次。
【讨论】:
是的 store 是一个自定义函数。import Foundation struct UserServerModel:Codable var avatar: String var email: String var first_name: String var last_name: String var id: Int static let database = DatabaseHandle.shared func store() guard let user = UserServerModel.database.add(User.self) else return user.avatar = avatar user.email = email user.first_name = first_name user.last_name = last_name user.id = Int16(id) UserServerModel.database.save()
为什么要在guard语句中添加用户?
除非我在用户中使用保护声明,否则我必须解开 user.avatar = avatar user.email = email user.first_name = first_name user.last_name = last_name user.id = Int16(id)
某处我必须添加一个条件来检查重复元素并在存储到 coredata 之前删除它,我不想维护冗余数据,然后在获取时过滤。
在添加之前不检查重复项,听起来像是问题的根源。【参考方案2】:
正如其他人所提到的,您需要在添加新记录之前检查持久层是否存在记录。我建议实现一个协议,以便您的其他 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
您需要在 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 []
最后,为您的 ApiHandler 添加一些持久性逻辑,一切顺利:
extension ApiHandler
func persist<T: Persistable>(response: ApiResponse<[T]>)
response.data.forEach
PersistenceBridge.updateOrCreate(model: $0)
【讨论】:
以上是关于如何在获取 api 响应并存储在 coredata 中然后在 UItableview 中显示后消除重复数据的主要内容,如果未能解决你的问题,请参考以下文章