K Owen
K Owen

Reputation: 1250

How to update the model after tableView was edited

I am learning Swift by writing a single table app view which lists the content of a Core Data table (entity) upon start-up. Then the user can reorder the rows in the table view.

I need to be able to save the newly ordered rows such that they replace the previous database table, so when the user starts the app again, the new order is shown.

The editing (re-ordering) feature is activated by a long press and calls

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    self.projectTableView.moveRow(at: sourceIndexPath, to: destinationIndexPath)
}

A second long press then inactivates the editing feature:

    // Called when long press occurred
    @objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer){
    if gestureRecognizer.state == .ended {
        let touchPoint = gestureRecognizer.location(in: self.projectTableView)
        if let indexPath = projectTableView.indexPathForRow(at: touchPoint) {
            if self.projectTableView.isEditing == true {
                self.projectTableView.isEditing = false
                db.updateAll()  //this is a stub
            } else {
                self.projectTableView.isEditing = true
            }
        }
    }
}

The call to db.updateAll() in 'handleLongPress' above is just a blank, and I don't know how to update the database. Is there a way to read the content of the tableView in the new sequence into an array, then replace the table in the db? Feels a little "brute force" but can't see any other solution.

Upvotes: 0

Views: 777

Answers (2)

mojtaba al moussawi
mojtaba al moussawi

Reputation: 1398

Ok you can achieve that in several ways :

1- Using NSFetchedResultsController , here you can automatically synchronizing changes made to your core data persistence store with a table view, so quickly here are the steps :

  • Conform to NSFetchedResultsControllerDelegate
  • Declare an instance of NSFetchedResultsController with you core data model
  • Make an NSFetchRequest, call NSFetchedResultsController initializer with the request, then assign it to your instance declared before
  • call performFetch method on your instance
  • set the viewController to be the delegate
  • And now you can implement the delegates, here you want didChange , so something like that :

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
                didChange anObject: Any,
                at indexPath: IndexPath?,
                for type: NSFetchedResultsChangeType,
                newIndexPath: IndexPath?) {
         switch type {
         /*
           ....
          */
         case .move:
              if let deleteIndexPath = indexPath {
                 self.tableView.deleteRows(at: [deleteIndexPath], with: .fade)
               }
    
              if let insertIndexPath = newIndexPath {
                  self.tableView.insertRows(at: [insertIndexPath], with: .fade)
              }
         }
    

    }

2- Second option which personally i prefer it over the NSFetchedResultscontroller

You can add a property in your model (core data model). That can be an Int for example "orderNum". So when you fetch request you can order the result using this prperty.

So if your table view cell re-arranged, after implementing moveItem method you can update this property for all your objects(loop over them) and they will be as they are displayed.

try to save your managed object context now,

Next time when you want to fetch request you can use a sort descriptor to sort on the "orderNum".

Upvotes: 4

Sylvan D Ash
Sylvan D Ash

Reputation: 1117

Maybe updating your data source (by removing and re-inserting the item) when moveRowAt is called would be better?

So something like:

// assuming your data source is an array of names
var data = ["Jon", "Arya", "Tyrion", "Sansa", "Winterfell"]

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    self.projectTableView.moveRow(at: sourceIndexPath, to: destinationIndexPath)

    let item = self.data.remove(at: sourceIndexPath.row)
    if sourceIndexPath.row > destinationIndexPath.row {
        // "Sansa" was moved to be between "Jon" and "Arya"
        self.data.insert(item, at: destinationIndexPath.row
    } else {
        // if the new destination comes after previous location i.e. "Sansa"
        // was moved to the end of the list
        self.data.insert(item, at: destinationIndexPath.row - 1
    }
}

Upvotes: 1

Related Questions