Working with Unwind Segues Programmatically in Swift

Updated on September 20, 2016 – Xcode 8 & Swift 3.0

Navigating between screens is a critical component to building iOS applications. The mechanism for navigation in Storyboard-based applications is the segue. Using segues, we can travel ahead to the next screen, which is extremely common. We can also travel backward in the screen “navigation stack” by programming a special kind of segue called an unwind segue.

Embedding a view controller inside a navigation controller gives us some built-in forward and backward navigation, so you may be asking, “What’s the need for an unwind segue??”

Well, suppose that we need to programmatically trigger the backward navigation, based on an interaction with something other than the default “back” button on the navigation bar. How would you do it? Yep – you’ve got it: by using an unwind segue.

This is a walk-through of how to work with unwind segues programmatically in Swift.

Note: Code in the main article below is written in Swift 3.0, but code examples for Swift 2.3 are found in the example project.

Example

An example app called “Roasters on the Go” has been created for this walk-through to help give you some context. It’s a mock mobile order system for purchasing green, un-roasted coffee beans (so that you can roast them yourself)!

Resources
  • We’ll start at a list of coffees categorized by region
  • Tapping an coffee origin country will take you to the order screen
  • Pressing the ‘Order Now’ button will simulate the placement of an order
  • Tapping “OK” on the alert will trigger the unwind segue (which is the goal of this walk-through)

Here’s a sample of what the fake app does:
Unwind Segue Demo

The goal is to go back to the list of coffee origins after ‘OK’ is pressed on the alert. Let’s look at the steps to make this happen.

1 – Create an unwindTo___ IBAction

The first step is to create an IBAction that we can wire up the unwind segue to.

Supposing that you’ve got two view controller: The MenuViewController which lists out the menu of coffees to purchase, and the OrderViewController which allows your app users to buy that particular coffee.

If we’re wanting to go from OrderViewController to MenuViewController, we need to create the IBAction in the MenuViewController:

Create an Unwind Action

Placing the IBAction code in the right spot is critical to the functioning of the unwind segue. In this example where we want to go from Order to Menu, if you place the IBAction in the OrderViewController, the Storyboard will let you wire it up just fine, but the transition back to the menu screen will never happen at runtime.

Therefore, it’s important to remember: Place the “unwindTo___” IBAction function in the view controller source file for the screen you’re unwinding back to.

2 – Wire up the unwind segue

Next up is to wire connect the view controller to the unwind segue IBAction that we just created.

Here, you’re going to be looking at the screen that you’re going to unwind from. In the running example, this will be the Order View Controller’s scene in the Storyboard.

To wire up the OrderViewController to its unwind segue, you need to right-click and drag (or control + drag) from the View Controller icon to the Exit icon in the Storyboard Scene:
Drag to Create Segue

You’ll be presented with list of IBActions to connect to. You’ll choose the unwind segue action that was created in step 1:
Create Segue

3 – Specify a segue identifier

For this step, you’ll want to make sure that the Document Outline of the Storyboard is expanded. This will allow you to easily select the unwind segue in the outline, and specify its identifier in the Attributes Inspector of the Utilities Pane:
Specify Segue Identifier

Since we want to take the user back to the menu when placing an order is finished, we’ll give it the identifier of “unwindToMenu”.

4 – Trigger unwind segue programmatically

The final step is to write a bit of code to trigger the unwind segue at the appropriate time.

In our example, we want to trigger it when the user taps on the ‘OK’ button of the alert. Here’s a snippet of code that will accomplish that task:

Wrapping up

Whenever you need to programmatically trigger backward navigation in your app, using an unwind segue can be a great feature to take advantage of. This walk-through took you step-by-step through working with unwind segues in Swift.

  • Tom Lynch

    Andrew, Thanks for this post. Very helpful. I can now successfully unwind. However, I am running into a new issue.

    My original VC is a Map; user can click on a custom search button that takes them to the a custom search VC option. The unwind takes user back to the map and successfully passes the search data to query cloudkit.

    Everything works fine until user clicks on an annotation which takes them to a AVPlayer controller to play a video associated with that annotation. That works fine except that after returning back to the map the custom search VC no longer works properly. I am hoping you can guide me in how to problem solve this?

    • Andrew Bancroft

      Hey Tom!

      Glad the post helped some. Can you describe more about what’s breaking on the custom search VC after the visit to the AVPlayer controller? Not sure if you’re still having issues with this or not since I’m a week late in responding (so sorry!)

      • Tom Lynch

        Andrew,
        Thanks. Basically I have VC1 that calls VC2 displaying a map. VC2 queries cloudkit which downloads records associated with that location of the map within a certain distance. From there the user can customize the search by segueing to VC3 which allows user to add in specific dates, distance ranges, and specific locations. Once User clicks search it ‘unwinds back to VC2 displaying new annotations. Works great back and forth especially with memory since before I added the unwind VC3 instantiated a new VC2 so there was a long line. My problem comes in when after the custom search VC3 back to VC2 if I tap on the annotation it pulls the associated video and segues to VC4 which pulls the CKAsset from cloud, converts to url, and then plays video. Once I return back via the back button on navigation controller to VC2 the VC3 won’t actually perform the search anymore. Actually, the same is true for the VC2 which had a refresh button. In order to get it working I have to return to VC1 and then move again to VC 2. It’s like it was uninstantiated. Any advice?

        • Andrew Bancroft

          I’m not 100% sure, but I wonder if something in that whole process is switching your execution to another thread (ie, not the main UI thread), so that when you actually do return to the UI, nothing is executing for you because the app is still on that background thread? Maybe? Can you look at maybe once the video is done playing, wrapping your code in that section in a dispatch_async(dispatch_get_main_queue(), { })? I’m not sure really what’s involved in CloudKit asset downloads or with the video playing. I’m not even quite sure what code you’d wrap in the dispatch_async block. Is there a dismissal function that goes along with the video playing or anything?

          Sorry I’m not more helpful on this one, Tom.

          • Tom Lynch

            Andrew,
            Just wanted to close this out. I figured it out. Turns out pretty simple mistake on my part. Turns out that following that specific sequence missed a call to clear my array used for capturing the annotations. in the download I had an if array.isEmpty check so it was missing that all together. Easy fix. Thanks for your help,
            Tom

  • Hi Andrew, Thanks for the great post.
    I have a situation here.
    I have a TableVC in a navigation stack which segues to load a DetailVC when an Item is selected.
    I wanna trigger an interstitial ad when navigation back is clicked from the DetailVC back to TableVC.

    This method of unwindToTableVC doesn’t seem to work. The unwindToTableVC does not get called when the back button is clicked from the DetailVC.

    I have tried so many other methods i found online.
    Could you please advice…

    • Andrew Bancroft

      Hi Adetunji – Question: Is the “Back” button the one at the top of, say, a Navigation Controller’s navigation bar? Or is it a button you added to your view that you’re trying to get to programmatically take you back to the TableVC? Just wanting to make sure I have the details of your situation down.

      • Thank Andrew, It is the back button at the top of the Navigation Controllers’s navigation bar.

        • Andrew Bancroft

          So that’s a little bit different of a situation than what I’m describing in this post. I found a Stack Overflow question/answer that I think might address your needs more: http://stackoverflow.com/questions/27713747/execute-action-when-back-bar-button-of-uinavigationcontroller-is-pressed

          Essentially, you need to add your own back button up to the top as the selected answer describes, or you need to hook in to the viewDidDisappear method and implement your logic there as this answer describes: http://stackoverflow.com/a/27715660.

          Hopefully this points you in the right direction – if I get the chance to write about it in more detail, I’ll let you know.

          • Thanks a lot Andrew!
            The idea was to load up an interstitial ad when the back button is clicked from the DetailVC.

            I solved this by implementing the ad in the TableVC and loading it up on viewWillAppear() after navigation back must have happened.

            Seems to solve the challenge and works fine.

            I really appreciate your time and efforts… 🙂

  • Eduardo Almeida

    Hi there, I have a similar situation. However, my code is based on a button in Swift 2.0. So, until your step 3, it was fine. However, in step 4:

    @IBAction func btnFeedbackAction(sender: AnyObject) {
    viewFeedback.hidden = true

    if (quizEnded){
    self.performSegueWithIdentifier(“unwindtoLogin”, sender: self)
    self.presentViewController(, animated: true, completion: nil)

    } else{
    nextQuestion()
    }
    }

    so, how can I call self.presentViewController? what is the first parameter?

    thanks

    • Andrew Bancroft

      Hey Eduardo!

      Okay, so with my example in step 4, all I’m doing is presenting an alert controller to simulate some kind of “Hey! This completed!” situation. When the user taps “OK” on the alert, the performSegueWithIdentifier("unwind...") is called. So the call to self.presentViewController(...) is simply the way to show the UIAlertController with the OK button with the unwind segue trigger attached.

      In your example, it doesn’t look like you need a self.presentViewController (you’re not presenting an alert like I was). Rather, it looks like if you’re observing that the quiz has ended, you want to perform the unwind segue.

      Does that make any more sense?

  • Bron Davies

    What a disaster…. The SDK should just have a “goBack”, or “finish” method which is standard for view controllers. This just makes the views more tightly coupled and even worse – in an unobvious way. Thanks for the help though!

    • Andrew Bancroft

      Oh my goodness, you’re not kidding – I remember going “really??” when I was first working with this. Which then led me to think, “I’m never gonna remember this if I don’t write it down”. Which led to the blog post. :]

    • Martyn

      The SDK does have a ‘goBack’ method.

      It’s
      dismiss(animated: true, completion: nil)

      Unwind seques are only necessary if you want to go back to something other than the calling view controller.

  • Ahmad Ismail

    Thank you Andrew! That was really helpful 🙂

    • Andrew Bancroft

      Awesome – Glad it was helpful, Ahmad!

  • Rafa Cosin

    Clear, sort and useful, thanks

    • Andrew Bancroft

      Glad to help, Rafa!

  • James Docherty

    Thanks for the clear instructions (esp. the Skitch notes on your images)…solved my problems 🙂

    • Andrew Bancroft

      So glad it helped, James!

  • My boss introduced this to me, but I had to search for it again because I wasn’t able to picked all the information he said to me lol. I didn’t know about this because I came from objective-c/hard coded UI.

    • Muhammad Rizwan Mughal

      lol keep searching!!

  • MXBrando

    This is very good. A lot about segues is counter-intuitive. You’ve made the unwind quite clear. Well done!

  • ridwan

    please help me.

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let destinationVC = segue.destinationViewController as! DetailMemoViewController
    let selectedIndexPatch = tableView.indexPathForSelectedRow!
    destinationVC.detailSender = allList[selectedIndexPatch.row] –> error cannot assign value of type ‘[[String:AnyObject]]’ to type ‘String!’
    }

  • Payal. Maniyar

    Thanks. Very well explained…

  • Lukas Mueller

    Worked perfectly fine. Thank you for your help, Andrew! 🙂

  • JamesgDev

    How would you unwind to multiple different views?

  • Pretorian

    Hi, if i “kill” app in multitasking and launch again. Segue don’t working. Why?

  • Matt O

    This doesn’t seem to work in Xcode 8 anymore

  • sunds alharbi

    Great tutorial
    Many thanks

  • bernie1953

    Thank YOU!

  • David Birkhead

    Not working in iOS 10 for me. Done it exactly as shown here.

    If i alter the segue name it says no segue with identifier “identifier” when trying to call it. When it is correct, Xcode calls it (I set a breakpoint and confirmed it is being hit), but nothing happens.

    Any ideas?

    • Jackery

      Did you name the segue’s identifier “identifier” in the attributes inspector?

  • Adem Acar

    thank you

  • Pingback: Core Data: Solving Ambiguous Type / Redeclaration of Type - Andrew Bancroft()

  • YiuChung Danty Wong

    The very last step requires the performSegue(withIdentifier: sender:) method to be triggered by a button. Is there a way to implement the method into the native NavigationController back button (without using a customised back button)?
    Thanks

  • Neil Billingham

    Awesome, nicely explained, thanks 🙂