UITableView Swipe to Delete Workflow in Swift

Data management applications, by which I mean an app where you’re allowing users to add, edit, and delete bits of data as part of your app’s core function, very likely use a table view (or two) to visualize lists of information that users of the app can interact with.

Making changes to the information listed in the table view and signaling those changes in a fluent way becomes a top concern for these types of apps. How do we allow users to add or remove “records” to the system? Furthermore, how do we signal that those changes were effective and refresh the view of the data in the UI?

The primary concern I want to focus in on in this article is the ever-common paradigm of “swipe to delete” when using a table view. What could the workflow of deleting a row from the table view (and its data source) look like? How could it be implemented in Swift? Let’s explore…

Workflow

The workflow of “swipe to delete” that I typically use in my own applications is this:

  • Swipe a table view row and have the Delete button appear.
  • Press the delete button, which triggers a confirmation: “Do you really want to delete this?”
  • Based on the user’s response to the confirmation, delete the object from the data source and remove it from the table view, or cancel.

The confirmation step is atypical for iOS it seems. Swiping to delete an e-mail or a reminder or just about anything else in Apple’s own apps simply deletes the item right away.

There may be a good reason for this, but I like to give folks an out if they didn’t mean to do it. It’s fair enough to say, “Well, they went to the effort of swiping the row and pressing the button… surely they mean to do it!”. I still feel more comfortable if I get the opportunity to cancel something like a delete operation. Feel free to disagree there – for this article, I’ll assume you want to include that into your delete workflow, and will demonstrate a simple way to implement all three steps of the strategy.

Implementation

To demonstrate the implementation of this workflow, suppose that we’ve got a table view listing out all of the planets in our solar system (all of the news on Pluto this week has me thinking in this direction, so I just went with it). Feel free to grab the example project over at GitHub:

The table view looks like this once its data source is all configured:

Table View with Planets

Swipe

What we want is to be able to swipe one of the rows and delete the planet from the data source and, consequently, remove it from the table view in a nice, fluid way:

Table View - Delete Button Revealed

To accomplish this, we need to do a couple of things.

First, your view controller needs to conform to the UITableViewDelegate protocol, because the swipe to delete functionality is encapsulated by one of the table view delegate methods. It’s a simple change at the class declaration:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { ... }

The delegate method that causes the buttons to appear on swipe is called tableView(_:commitEditingStyle:forRowAtIndexPath

Its implementation for the example I’m working through looks like this:

So what’s happening here? The primary thing to notice is that we’re evaluating the editingStyle from the method’s parameter list. Comparing it to the .Delete UITableViewCellEditingStyle value is what allows us to know that the Delete button was tapped.

Since I have a confirmation step to take care of, I’ve chosen to store the indexPath of the row we’re wanting to delete in a class-viewable variable so that I can use it later on when we handle the deletion (if the user confirms it).

The final step of this function is to call confirmDelete(), which has its own explanation coming up…

Confirm

The next step is confirming that the user really wants to delete the particular planet they’ve initiated the delete action on:

Delete Confirmation

How is this implemented? It all boils down to using iOS 8’s new UIAlertController:

We’re asking the user if they’re sure they want to permanently delete the planet using the .ActionSheet style.

The next step of the function is to create a couple of UIAlertAction buttons: one for Delete and one for Cancel, with the appropriate style (.Destructive and .Cancel, respectively).

Finally, we provide a function to each UIAlertAction instance. The handlers, of course, could have been implemented with closures, but I chose to built out a couple of named functions, just to be able to step through it with you, and to separate out the logic of the steps a little more. Feel free to do whichever seems most natural to you.

Delete or cancel

This is the final step! The following code snippet is an implementation of the two handlers specified for the initialization step of the UIAlertAction buttons, which were handleDeletePlanet and cancelDeletePlanet:

Clearly, handleDeletePlanet() is the most involved. The essential process is this:

  • Call beginUpdates() on the table view instance to signal the start of UI updates to the table view.
  • Remove the planet from the data source using the deletePlanetIndexPath we set in the alert controller step.
  • Call deleteRowsAtIndexPaths() on the table view to remove the planet from the UI.
  • Reset the class-viewable deletePlanetIndexPath to nil.
  • Call endUpdates() on the table view instance to complete the UI updates.

There’s one gotcha, that I’ve tried to highlight by way of comment in the code snippet: Notice that within the call to deleteRowsAtIndexPaths (plural), I’ve wrapped the indexPath we’re removing in an array. It’s subtle, but this delegate method expects an array of indexPath instances, not a single index path instance. It’s simple enough, but it can catch you off guard if you’re in the mindset of removing a single row and come across this method, which is flexible enough to allow you to delete several at a time.

Wrapping up

Swipe -> confirm -> delete or cancel. Those are the steps we analyzed in this commonly needed workflow in data management applications.

  • http://dimpiax.com Dima Pilipenko

    For what beginUpdates()/endUpdates() if table view animation of insert, delete etc, works without it?

    • Andrew Bancroft

      You know, I put that in there out of habit – it helps with animating in more than one change to the table view. Since we’re just deleting one row at a time, it probably works either way.

      • http://dimpiax.com Dima Pilipenko

        Animations work fine for me without them.

        For example, in one thread:

  • D. Harvey

    Instead of the UIAlertAction, wouldn’t be be better to redisplay the Delete button with a question mark? (Not sure what Apple’s HIG would say?)

    • Andrew Bancroft

      When I was researching for what to do in my own app, I looked in the HIG for “Delete”, and there just wasn’t a lot of guidance, surprisingly. Unless I just missed it (which is entirely possible… my wife will attest to the fact that I miss things in front of my face all the time, haha).

      My solution to confirm the object to be deleted may well be off – I appreciate your perspective on it! I just always think, “If this action can’t be undone easily, or if I’m not providing a way for it to be undone, I want them to be really sure they want to delete it before it goes away.” – thus the UIAlertAction strategy. It gives the details of _what_ they’re deleting, along with a cancel button and a ‘destructive’ action button. Personally, I like and appreciate that, but it may be an extra step that’s not even necessary.

      If you’ve got other thoughts, feel free to share. Thanks!

  • Patrick

    Thanks for post. Great information! How would I go about protecting one row from the tableview? I use NSUserDefaults to setup some initial values that I’d like the user to not be able to delete. So, I want to disable swipe to delete for that row only.

    • Andrew Bancroft

      Great question that I’m not sure the answer to right now, Patrick. I just realized you asked this a month ago – I’m so sorry for just now replying with “I don’t know”. Your comment slipped off my radar.

      Did you ever figure this one out on your own by chance?

  • Hoàng Cửu Long

    Hi Andrew, when I build my app in ipad mini have error with AppDelegate.swift when I clicked the delete button.

    • Hoàng Cửu Long

      Terminating app due to uncaught exception ‘NSGenericException’, reason: ‘Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet. The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller’s popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.’

      • Andrew Bancroft

        Hi Hoàng!

        Thanks for bringing this bug to my attention! I’ve updated the repository and code in this walkthrough to include code for iPad support. I’m not 100% happy with how the positioning of the alert controller is on the iPad screen (maybe you’ll have better luck?), but I was able to get the crash to stop.

        Check out the latest repository and note the “// Support presentation in iPad” in the confirmDelete(_:) method.

        https://github.com/andrewcbancroft/SwipeToDeleteExample/

  • Bhupender Dhillon

    Hi Andrew, I have implemented the methods in Swift 2.0 however i cannot get the delete button to appear on the swipe action. I am able to swim left on the row so i am assuming that it is allowing the editing action however as i mentioned the actual delete button does not show. Any help would be appreciated thanks

  • lemohewe

    Could you explain the trailing closure in the handler parameter?

    How is that in the handler parameter the function is written like ‘handleDeletePlanet’ here: let DeleteAction = UIAlertAction(title: “Delete”, style: .Destructive, handler: handleDeletePlanet)

    and then the function takes a parameter ‘alertAction’ here: func handleDeletePlanet(alertAction: UIAlertAction!) -> Void ? How is that possible? Why?

    Cheers!

  • Imran Moolla

    Hi Andrew,

    Thanks for making this look so easy. I know it’s been a while, but…

    Just one question, If i click cancel, how do I get the cell to bounce back into place

    Thanks