37

I try the next code snippet:

var alert = UIAlertController(title: "Alert", message: "Cannot connect to : \(error!.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)

in my AppDelegate, but it prints me the next error in console:

Warning: Attempt to present <UIAlertController: 0x7ff6cd827a30> on <Messenger.WelcomeController: 0x7ff6cb51c940> whose view is not in the window hierarchy!

How can I fix this error?

Orkhan Alizade
  • 7,379
  • 14
  • 40
  • 79
  • 5
    In the `AppDelegate`, as the error describes, the window hierarchy is not yet created, so from there you cannot present anything (at least from the `didFinishedLaunchingWithOptions`), so you should move your code to a `ViewController` – Dániel Nagy Aug 24 '15 at 10:12
  • @DánielNagy i see, but i have to show it from AppDelegate. Isn't there any solutions? – Orkhan Alizade Aug 24 '15 at 10:15
  • 2
    @OrkhanAlizade create a `ViewController`, put your code into the ViewControllers `viewDidAppear` method, and in your `AppDelegate`, set that `ViewController` as the windows `rootViewController` (and also don't forget to create the window itself). – Dániel Nagy Aug 24 '15 at 10:22
  • @DánielNagy it works! Thank you! – Orkhan Alizade Aug 24 '15 at 10:37
  • @OrkhanAlizade you are welcome! – Dániel Nagy Aug 24 '15 at 10:37

8 Answers8

40

This is what i'm using now to do that.

var alertController = UIAlertController(title: "Title", message: "Any message", preferredStyle: .ActionSheet)
var okAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) {
                    UIAlertAction in
                    NSLog("OK Pressed")
                }
var cancelAction = UIAlertAction(title: "No", style: UIAlertActionStyle.Cancel) {
                    UIAlertAction in
                    NSLog("Cancel Pressed")
                }
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
30

Swift 5:

let alert = UIAlertController(title: "Test", message:"Message", preferredStyle: UIAlertController.Style.alert)
        
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
       
// show the alert
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
lukemmtt
  • 476
  • 6
  • 15
Ahmed Safadi
  • 4,402
  • 37
  • 33
  • 2
    Whilst this answer may solve the OP's issue, it would be worthwhile elaborating on how this achieves the solutions. Simply posting code-only answers may prove unhelpful to the OP or future users. Please elaborate. – Geoff James May 21 '17 at 08:46
  • @GeoffJames done :) – Ahmed Safadi May 21 '17 at 10:57
19

As per Jorge's answer, updated for Swift 4

let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
        UIAlertAction in
        NSLog("OK Pressed")
    }
let cancelAction = UIAlertAction(title: "CANCEL", style: UIAlertActionStyle.cancel) {
        UIAlertAction in
        NSLog("Cancel Pressed")
    }
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
Chris Herbst
  • 1,241
  • 1
  • 16
  • 28
14

Swift 3.0 or above, Working in all condition , like in case of tab bar, in case of presented view etc ..

let alert = UIAlertController(title: "Test", message:"Message", preferredStyle: UIAlertControllerStyle.alert)

// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))

// show alert
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
emraz
  • 1,563
  • 20
  • 28
9

I had the similar problem.

I have fixed it by presenting UIAlertController in Main Queue.

Code Looks like following.

let alert = UIAlertController(title: "My Title", message: "My Message", preferredStyle: .alert)

let actionYes = UIAlertAction(title: "Yes", style: .default, handler: { action in
    print("action yes handler")
})

let actionCancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { action in
    print("action cancel handler")
})

alert.addAction(actionYes)
alert.addAction(actionCancel)

DispatchQueue.main.async {
    self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
stackich
  • 3,607
  • 3
  • 17
  • 41
Rashesh Bosamiya
  • 609
  • 1
  • 9
  • 13
3

I suppose you are calling that code snippet from the applicationDidFinishLunchingWithOptions.

I tried it as a matter of fact because I had to. The thing is: what you are trying to do is correct but the ViewController that the AppDelegate makes and presents is about to be put on screen and before that, the code snippet tries to create an alertView and put in on top of non existent View of the RootViewController.

What I would do is move it to another delegate call which is guaranteed to be called after the RootViewController is presented.

func applicationDidBecomeActive(application: UIApplication) {
    //This method is called when the rootViewController is set and the view.
    // And the View controller is ready to get touches or events.
    var alert = UIAlertController(title: "Alert", message: "Cannot connect to :", preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
    self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)            
}

But as always know the responsibility of the AppDelegate. It is to handle the application lifecycle and application wide delegate calls and events. If putting code here makes sense, then do it. But if you will be better off putting the code on the rootViewController or other parts then think about it too.

Anyway, hope it helps. Cheers!

stackich
  • 3,607
  • 3
  • 17
  • 41
kandelvijaya
  • 1,545
  • 12
  • 20
3

Have you tried using UIApplication.shared.keyWindow?.rootViewController?.present(...) ?

emmics
  • 994
  • 1
  • 11
  • 29
0

I would suggest NOT doing this in the AppDelegate. The App Delegate it intended to handle Delegate functions from the OS rather than implementing things like alert views.

If you are wanting to present an alert view here to be shown at the start of the app I would do this by implementing it in your first view controller.

Swinny89
  • 7,273
  • 3
  • 32
  • 52