I parsed the Api and stored in CoreData but when i display the results in UITableView its displaying the image, name and email twice. How to eliminate the duplicate and display only unique data. I also added a constraint and "context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy which stopped the merging of data after each run but now its displaying all the data twice."
Any help Will be highly appreciable
ViewController
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
}
DatabaseHandle
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()
}
}
2
Answers
when you’re calling
I’m not sure if store is a custom function, but perhaps you’re not first checking if it already exists in coredata, so you end up just saving it twice.
As the others have mentioned you need to check the persistence layer for the existence of records before adding a new one. I’d suggest implementing a protocol so that your other API data structures can be persisted as well.
I’m not a fan of mixing repository logic with my models so I’d create some sort of a bridge between your API and persistence models.
You’ll need to add a predicate parameter to your fetch method so you can constrain your results:
Finally, add some persistence logic to your ApiHandler and you’re good to go: