Unwind Segue Doesn't Work

Here is my code below. I keep getting the error: "Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior". Everywhere online says this code will work. Thoughts on why it doesn't work?


let alert = UIAlertController(title: "Alert: iCloud Disabled", message: "Go into Settings -> iCloud -> iCloud Drive and switch iCloud Drive to On.", preferredStyle: UIAlertControllerStyle.Alert)

let OKAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {

(_)in

self.networkAlertDisplayed = true

self.performSegueWithIdentifier("unwindToController1", sender: self)

})

alert.addAction(OKAction)

self.presentViewController(alert, animated: true, completion: nil)



What do I need to do to get this code working?

Answered by psynixis in 124830022

Can you do the following test - it will hopefully show if you're doing something strange with your Storyboard:


1) Create a new project, and have two screens.

2) On the first screen create a button that segues to the second screen when tapped

3) On the second screen, create a button that shows your alert when tapped


Make the source code for the first view contoller be this:


import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
      
    }
    @IBAction func unwindToController1(segue: UIStoryboardSegue) {
        print("unwound")
    }
   
}



And make the code for the second view controller be what I posted in my previous comment.


4) Set up your unwind segue as you normally do.


Then, see if that test project works.

Does the selector "unwindToController1" actually exist?

"unwindToController1" is the name of the unwind segue "Identifier". The action itself is named myUnwindAction: and is indicated in as the "Action" when I click on the segue. The myUnwindAction method is present in the destination controller.

Sounds kind of like a UIKit bug if it happens in a simple case... if you can distill it down to a simple test project, it would be worth a bug report.


You could try some icky workaround such as a dispatch_async to the main queue so that the performSegueWithIdentifier is done on the next run loop or after a delay. But before starting with the hacks, do you know exactly which view controller the message is talking about whose view is being loaded while deallocating?

The console message is referring to the current view controller and not the destination view controller. I tried all sorts of dispatch_async combinations and nothing seemed to work.

I'm not sure what you're doing in terms of your Xcode Storyboards, but your source code works fine for me, as is. I just created a quick test project with two screens. In response to a button tap on the second screen, I show the alert using your exact code. When I dismiss the alert, everything unwinds back to the first screen.

That is exactly the behavior I want to happen for my code. Any suggestions for why it doesn't work for me? I have a navigation controller and the first view controller connects to a second view controller. I'm trying to execute the above code in the second view controller. Not sure why it's not working. Doesn't make any sense.

Not sure. Here's the entire source code of the test ViewController I did for the second screen. Not how you'd write a real-life class obviously, but I wanted the simplest possible test case for your exact code.. Maybe worth trying this, and seeing if it works.


import UIKit
class NewViewController: UIViewController {
  
    var networkAlertDisplayed = false

    override func viewDidLoad() {
        super.viewDidLoad()
       
    }
  
    @IBAction func showAlert(sender: AnyObject) {
  
        let alert = UIAlertController(title: "Alert: iCloud Disabled", message: "Go into Settings -> iCloud -> iCloud Drive and switch iCloud Drive to On.", preferredStyle: UIAlertControllerStyle.Alert)
      
        let OKAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {
            (_) in
          
            self.networkAlertDisplayed = true
            self.performSegueWithIdentifier("unwindToController1", sender: self)
        })
      
        alert.addAction(OKAction)
        self.presentViewController(alert, animated: true, completion: nil)
       
    }
  
}

I noticed above that showAlert is triggered by an IBAction. Does that mean you have a button triggering it? My function is triggered by an Observer. Would that make a difference?

Yes, like I say, I just put a button on the second screen to show the alert when I tap on it. I did that just to make the simplest possible test case. Might be worth trying my ViewController code as is, and checking it works for you.

It didn't work. Here are my console messages for full disclosure:


Presenting view controllers on detached view controllers is discouraged <AppTest.Controller1: 0x1015408b0>.

Warning: Attempt to present <UIAlertController: 0x1014d52f0> on <AppTest.Controller2: 0x1014a2250> which is already presenting (null)

Warning: Attempt to present <UIAlertController: 0x1015aec00> on <AppTest.Controller2: 0x1014a2250> which is already presenting (null)

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x1014d52f0>)

You're not trying to manually present any ViewControllers from a viewWillAppear or viewDidLoad method are you?

I can't find what is causing "Presenting view controllers on detached view controllers is discouraged". I'm not trying ot manually present anything in viewWillAppear or viewDidLoad. I was only adding my observers and loading some arrays in viewDidLoad. I moved all of that to viewDidAppear and I'm still getting the same console message. The console message only appears when the observer is called in that second viewcontroller. The "Presenting view controllers on detached view controllers is discouraged" references the first view controller everytime. Is it something about the way my storyboard is set up?

Accepted Answer

Can you do the following test - it will hopefully show if you're doing something strange with your Storyboard:


1) Create a new project, and have two screens.

2) On the first screen create a button that segues to the second screen when tapped

3) On the second screen, create a button that shows your alert when tapped


Make the source code for the first view contoller be this:


import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
      
    }
    @IBAction func unwindToController1(segue: UIStoryboardSegue) {
        print("unwound")
    }
   
}



And make the code for the second view controller be what I posted in my previous comment.


4) Set up your unwind segue as you normally do.


Then, see if that test project works.

The test project did work which leads me to believe it is my storyboard. It is important to note that in my test project I used two view controllers but in my app I used a navigation controller and a view controller. I think this has something to do with it.

So basically I took the navigation controller out of my app. I will now have to rewrite some things (no longer calling navigation controller) but this will fix the issue. If I could give you triple points I would. Thank you so much!! I wish we had a stronger iOS developer community where I'm at so I could have another pair of eyes look at my code and come up with a fix in 2 seconds 🙂

Unwind Segue Doesn't Work
 
 
Q