3

My Swift segue is not working at all and isn't throwing any errors. The breakpoint shows me that the app lands on this line but nothing happens:

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

The code block is a login form with Facebook:

if let accessToken: FBSDKAccessToken = FBSDKAccessToken.currentAccessToken() {
    PFFacebookUtils.logInInBackgroundWithAccessToken(result.token, block: {
        (user: PFUser ? , error : NSError ? ) - > Void in
        if user!.isNew {
            self.performSegueWithIdentifier("SignupSegue", sender: self)
        } else {
            self.navigateToInGame(true)
        }
    })
}

Here's the segue function it should call, but doesn't even get to it:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
    {
        if (segue.identifier == "SignupSegue") {
            let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let vc = storyboard.instantiateViewControllerWithIdentifier("SignUpViewController") 
            self.showViewController(vc, sender: self)
        }
    }

Any ideas?

Marcus Leon
  • 55,199
  • 118
  • 297
  • 429
winston
  • 3,000
  • 11
  • 44
  • 75
  • I just wanna clear that why do you need to show the viewController after setting segue in storyboard? You might not set the segue with the identifier "SignupSegue". So please double check on that. Correct me if am wrong. – Boopathy Mar 12 '16 at 03:19
  • OK, I understand. I commented out the "showViewController" line (I got this from a Google search). I'm just looking to establish a segue from one storyboard to another. Still getting the error after commenting that out. Thanks! – winston Mar 12 '16 at 03:23

6 Answers6

3

Generally, any UI updating has to be in main thread. I think the block for PFFacebookUtils.logInInBackgroundWithAccessToken is still in the background state in above situation. Maybe trigger the showViewController in dispatch_async(dispatch_get_main_queue(), {}) and see if there is any difference.

dispatch_async(dispatch_get_main_queue(), {
    let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewControllerWithIdentifier("SignUpViewController")
    self.showViewController(vc, sender: self)
})
Allen
  • 1,714
  • 17
  • 19
  • The dispatch definitely helped! I now get an error `LoginViewController has no segue with identifier 'SignupSegue`. I generally see this error when I don't have a segue drawn out in the storyboard. However, since they are separate storyboards, I was hoping the `if identifier` function would do this programmatically. Did I miss something? – winston Mar 12 '16 at 02:51
  • @james If I do understand the meaning of you have a separate storyboard, it sounds like you have a storyboard with a different name but not "Main", right? ``UIStoryboard(name: "SomethingElse", bundle: nil)`` – Allen Mar 12 '16 at 02:55
  • I'm sorry. I have Main.storyboard and Login.storyboard. I'd like to segue LoginViewController (Login.storyboard) -> SignUpViewController (Main.storyboard) – winston Mar 12 '16 at 03:02
  • I set the ID and it didn't work. The error says it can't find the segue so I think the Storyboard ID's are set correctly. I'm confused why it can't find the segue even though I have it set right there in the function :/ – winston Mar 12 '16 at 03:09
  • @james Hmm, it should be very straightforward. I just created a sample project and you can download it and take it for reference here. https://db.tt/LLPH8Kvh – Allen Mar 12 '16 at 03:15
  • In your example you only have one storyboard. Will the code in your ViewController also work if you were to segue to another controller in another storyboard? – winston Mar 12 '16 at 03:19
  • @james Actually, there are two. Please check it agin. One is Login.storyboard and another is Main.storyboard. So the func of ViewController in main storyboard opens another view controller in Login.storyboard. – Allen Mar 12 '16 at 04:04
  • I got it! I put the `et storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewControllerWithIdentifier("SignUpViewController") self.showViewController(vc, sender: self)` INSIDE the dispatch function. I wonder why this worked instead of just calling the performsegue inside the dispatch function? Also coldyam website is down. I'd love to get in touch with you next time I have a question! Thanks for everything – winston Mar 12 '16 at 04:19
  • Good one. I was playing with the storyboards. Look at my answer if you wanna do things using storyboard. I just learned about Storyboard Reference now ;) – Boopathy Mar 12 '16 at 04:33
  • @james You are welcome. My Amazon EC2 has a degradation today. Just redirect the DNS to another IP. The website could be accessible later. Cheers! – Allen Mar 12 '16 at 06:04
2

Ok. I just tried it out. Hope you did all the things regarding StoryBoard Reference. Me too had the same issue with performSegueWithIdentifier.

Example: Let take two storyboard main and signup.

1) In main.storyboard create a storyboard reference. Set the storyboardId in the Storyboard Reference as signup and the referencedId as the storyboardId of the scene(viewController) which is in signup.storyboard. Look at this link for a clear picture Storyboard to Storyboard

2) Set the segue identifier between viewController and Storyboard Reference in main.storyboard

3) Since I faced the same problem with performSegueWithIdentifier, I replaced it with shouldPerformSegueWithIdentifier.

override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
    if(identifier == "segue_identifier"){
       // segue_identifier is the viewController and storyBoard Reference segue identifier.
        print("hello")
    }
    return true;
}

Let me know if you find any issues. It did work for me.

Boopathy
  • 415
  • 3
  • 12
1

Performing a segue leads to present a new view controller.You don't need to and can't create and show view controller in prepareForSegue.It will look like:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
    {
        if (segue.identifier == "SignupSegue") {

            let vc = segue.destinationViewController

        }
    }
Lumialxk
  • 6,239
  • 6
  • 24
  • 47
  • Thanks for the reply! Will this work if I want to segue to another controller in a different storyboard? – winston Mar 12 '16 at 03:20
  • @james You can use storyboard reference(available in iOS 8 and later). Otherwise you can't use segue between storyboards. – Lumialxk Mar 12 '16 at 03:22
  • Oh OK...i wonder if I should just combine everything on my Main storyboard – winston Mar 12 '16 at 03:24
  • @james Don't do this, I tried but Xcode will become very very slow when you open storyboard file(took me 3 minutes). – Lumialxk Mar 12 '16 at 03:26
  • Just to try it out, I moved the LoginViewController to the Main storyboard. Even with both controllers in the same segue, `self.performSegueWithIdentifier("SignupSegue", sender: self)` still produces error `LoginViewController has no segue with identifier 'SignupSegue''` So confused – winston Mar 12 '16 at 03:37
  • @james If you've set segue identifier in storyboard,just perform it and don't show or create view controller in codes.It works well. – Lumialxk Mar 12 '16 at 03:48
  • I think the `dispatch_async(dispatch_get_main_queue(), { self.performSegueWithIdentifier("SignupSegue", sender: self) })` might be screwing it up. But if I don't use it then the app doesn't even run the line of code at all – winston Mar 12 '16 at 03:57
0

Swift 3 solved:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if (segue.identifier == "SignupSegue") {
                DispatchQueue.main.async {
                    let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                    let vc = storyboard.instantiateViewController(withIdentifier: "SignUpViewController")
                    self.show(vc, sender: self) 
                }
          }
    }
Darius Miliauskas
  • 3,391
  • 4
  • 35
  • 53
0

You can try this ...

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
    {
    if (segue.identifier == "SignupSegue") {
        if let destination = segue.destination as? SignUpViewController {
         ...
        }

    }
}
Ali Ihsan URAL
  • 1,894
  • 1
  • 20
  • 43
  • Please edit your answer to explain why this piece of code answers the question. – André Kool Feb 26 '18 at 13:51
  • Because these are not need i think. --> let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewControllerWithIdentifier("SignUpViewController") – Ali Ihsan URAL Feb 26 '18 at 14:03
0

The closest I can get to overcome same problem for myself:

Made trigger var segueLogin : Bool = false with initialised value in the Class.

When PFFacebookUtils gets needed values for segue, change trigger to true:

PFFacebookUtils.logInInBackground(withReadPermissions: permissions) {
            (user: PFUser?, error: Error?) -> Void in
            if let user = user {
                if user.isNew {
                    print("User signed up and logged in through Facebook!")
                    self.segueLogin = true

                } else {
                    print("User logged in through Facebook!")
                    self.segueLogin = true
                }
            } else {
                print("Uh oh. The user cancelled the Facebook login.")
                self.loginCancelledLabel.alpha = 1
            }
        }

Then added code to viewDidAppear class. Realised it starts everytime PFFacebookUtils complete. So it checks if returned value is true and performs segue after successful PFFacebookUtils session:

override func viewDidAppear(_ animated: Bool) {
        if segueLogin == true {
            self.performSegue(withIdentifier: "segueSingup", sender: self)
        }
    }