14

I'm using the Twitter login in the signup process of my app. And I'm asking for the user's email. Once I get it, I'd like to present a UIAlertController.

Here's my code:

func askForTWMail(){
    if (Twitter.sharedInstance().session() != nil) {
        let shareMailVC=TWTRShareEmailViewController(completion: {(mail:String!, error:NSError!) in
            if (mail != nil) {
                print("GOT MAIL: \(mail)")
                self.gotMail()
            }else{
                print("MAIL VC ERROR: \(error)")
            }
        })
        println("PRESENT MAIL VC")
        self.presentViewController(shareMailVC, animated: true, completion: nil)
    }else{
        println("User not logged in")
    }
}

func gotMail(){
    var alertController=UIAlertController(title: "Some title", message: "Some message", preferredStyle: UIAlertControllerStyle.Alert)
    var okAction=UIAlertAction(title:"Yes", style: UIAlertActionStyle.Default) {
    UIAlertAction in
    //some action
    }
    var cancelAction=UIAlertAction(title:"No", style: UIAlertActionStyle.Cancel){
    UIAlertAction in
    //some action
    }
    alertController.addAction(okAction)
    alertController.addAction(cancelAction)
    self.presentViewController(alertController, animated: true, completion: nil)                
}

But I get this error (I guess because the TWTRShareEmailViewController is not dismissed):

Warning: Attempt to present UIALertController on xViewController whose view is not in the window hierarchy!

Any idea of how I should write this? How can I know when the TWTRShareEmailViewController is dismissed to continue the signup process and be able to present my UIAlertController? I'm not aware of a delegate method related to TWTRShareEmailViewController.

Any help is appreciated. Thanks.

Mason G. Zhwiti
  • 6,444
  • 11
  • 61
  • 97
Marie Dm
  • 2,637
  • 3
  • 24
  • 43

4 Answers4

34

Found a solution here. I'm probably doing it wrong but if not it might be an Apple bug. The workaround is to delay the presentation of the UIAlertController:

dispatch_async(dispatch_get_main_queue(), ^{
    self.presentViewController(alertController, animated: true, completion: nil)
})

EDIT: I found another workaround (I don't use the solution I put down here anymore). I had to change this because the Twitter login was also breaking my transitions between VCs.

I now call a specific UIViewController (I called it something like TWLoginVC) where I do all the Twitter login and other stuff. The view is just black so the user don't see the process is actually done in another VC (he just has to pick up the Twitter user he wants to login with). I guess you could also put a clear background to be even more invisible.

When I call this view controller and dismiss it, the transition is not applied to it and I don't have any more problem with it.


EDIT Update for Swift:

DispatchQueue.main.async{
     self.present(alertController, animated: true, completion: nil)
}
Or Arbel
  • 2,965
  • 2
  • 30
  • 40
Marie Dm
  • 2,637
  • 3
  • 24
  • 43
  • Did you find a better solution for this issue? I am also using dispatch_async – Ignacio Chiazzo Mar 04 '16 at 15:50
  • As I explained in my edited response, I have a special VC for Twitter stuff. So when the user wants to login with Twitter, I modally present my 'TWLoginVC' where everything is done (avoiding strange behavior in some cases, with special transitions between VCs for example). But I changed my code a bit and I don't need to present an UIAlertController anymore. So no sorry I don't have a better solution :/ – Marie Dm Mar 04 '16 at 17:43
  • 1
    With the new TwitterKit (I just updated it to v 2.0.2), the way to request the users' email changed. It's explained in the Twitter doc: https://docs.fabric.io/ios/twitter/request-user-email-address.html. – Marie Dm Apr 22 '16 at 12:56
5

Here's an updated answer for Swift 3 tested on Xcode 8 based on Marie Dm's answer.

DispatchQueue.main.sync {
     self.present(alertController, animated: true, completion: nil)
}
Asdrubal
  • 2,421
  • 4
  • 29
  • 37
-1

If DispatchQueue.main.sync caused crush, try DispatchQueue.main.async. "async" worked for my problem while returning from contactPicker().

stevieb
  • 9,065
  • 3
  • 26
  • 36
HIEPING
  • 281
  • 3
  • 3
-1

delay presenting viewController solved my problem and saved my day :D

Swift 4.0 :

DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 
    //present your view controller her
    self.presentViewController(alertController, animated: true, completion: nil)
}
Amer Alzibak
  • 1,489
  • 15
  • 16