Search code examples
iosswiftcore-data

I am using core data. but sequence of data is not maintained (on table view) when I select cell for edit and delete action


I am using core data. but sequence of data is not maintained (on table view at run time) when I select cell for edit and delete action.. after adding new data , data I being randomly stored on table without maintaining actual sequence (if I add data before selecting any particular cell then the sequence is maintained).

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    let tableView : UITableView = {
        let table = UITableView()
        table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return table
    }()
    
    private var models = [ToDoListItem]()
    override func viewDidLoad() {
        super.viewDidLoad()
        getAllItems()

        // Do any additional setup after loading the view.
        title = "To Do List"
        view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.frame = view.bounds
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(didTapAdd))
    }
    @objc private func didTapAdd(){
        let alert = UIAlertController(title: "New Item", message: "Enter New Item", preferredStyle: .alert)
        alert.addTextField(configurationHandler: nil)
        alert.addAction(UIAlertAction(title: "Submit", style: .cancel, handler: {[weak self] _ in
            guard let field = alert.textFields?.first, let text = field.text, !text.isEmpty else {
                return
            }
            self?.createItem(name: text)
        }))
        present(alert, animated: true)
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return models.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let model = models[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = model.name
        return cell
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let item = models[indexPath.row]
        let sheet = UIAlertController(title: "Edit", message: nil, preferredStyle: .actionSheet)
        sheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        sheet.addAction(UIAlertAction(title: "Edit", style: .default, handler: {_ in
            
            let alert = UIAlertController(title: "Edit Item", message: "Edit your Item", preferredStyle: .alert)
            alert.addTextField(configurationHandler: nil)
            
            alert.textFields?.first?.text = item.name
            alert.addAction(UIAlertAction(title: "Save", style: .cancel, handler: {[weak self] _ in
                guard let field = alert.textFields?.first, let newName = field.text, !newName.isEmpty else {
                    return
                }
                self?.updateItem(item: item, newName: newName)
            }))
            self.present(alert, animated: true)
        }))
        sheet.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: {[weak self] _ in
            self?.deleteItm(item: item)
        }))
        present(sheet, animated: true)
    }
    
    
    // core data
    func getAllItems(){
        do {
        models = try context.fetch(ToDoListItem.fetchRequest())
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        } catch {
        }
    }
    func createItem(name: String){
        let newItem = ToDoListItem(context: context)
        newItem.name = name
        newItem.createdAt = Date()
        do{
            getAllItems()
            try context.save()
        }catch{
        }
    }
    func deleteItm(item: ToDoListItem){
        context.delete(item)
        do{
            try context.save()
            getAllItems()
        }catch {
        }
    }
    func updateItem(item: ToDoListItem, newName: String){
        item.name = newName
        do{
            try context.save()
            getAllItems()
        }catch{
        }
    }
}

Solution

  • Let's break:

    models = try context.fetch(ToDoListItem.fetchRequest())
    

    into:

    let request = ToDoListItem.fetchRequest()
    models = try context.fetch(request)
    

    Now, request doesn't has a sorting order. If you add a sort, you'll guarantee the order of your items.

    It depends on which property you want to sort them, but:

    fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(ToDoListItem.name), ascending: true)]
    

    or

    fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(ToDoListItem.createdAt), ascending: true)]
    

    could be a solution.