iOS Development
Swift
Segue
prepareForSegue
Xcode

Prevent segue in prepareForSegue method?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

No. prepare(for:sender:) is too late if your goal is to stop a segue from happening. That method is for configuring the destination view controller after UIKit has already decided the segue should run.

What prepare(for:sender:) Is Actually For

prepare(for:sender:) is the place where you pass data to the destination or tweak its configuration before the transition completes.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    var username = "mark"
5
6    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
7        if segue.identifier == "ShowProfile",
8           let profile = segue.destination as? ProfileViewController {
9            profile.username = username
10        }
11    }
12}
13
14final class ProfileViewController: UIViewController {
15    var username: String?
16}

That is normal usage. But at this stage, the segue has already been approved. You can configure it, but you should not think of this method as a gatekeeper.

The Correct Way: shouldPerformSegue

If the segue is triggered from a storyboard control such as a button, the usual way to prevent it is shouldPerformSegue(withIdentifier:sender:).

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    @IBOutlet private weak var usernameField: UITextField!
5    @IBOutlet private weak var passwordField: UITextField!
6
7    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
8        if identifier == "ShowProfile" {
9            let username = usernameField.text ?? ""
10            let password = passwordField.text ?? ""
11            return !username.isEmpty && !password.isEmpty
12        }
13
14        return true
15    }
16}

If this returns false, UIKit does not perform the segue.

This is the right tool when your validation decides whether navigation should happen at all.

Show Feedback When Blocking the Segue

If the segue is blocked, you usually want to explain why.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    @IBOutlet private weak var usernameField: UITextField!
5    @IBOutlet private weak var passwordField: UITextField!
6
7    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
8        guard identifier == "ShowProfile" else { return true }
9
10        let username = usernameField.text ?? ""
11        let password = passwordField.text ?? ""
12
13        if username.isEmpty || password.isEmpty {
14            showValidationError()
15            return false
16        }
17
18        return true
19    }
20
21    private func showValidationError() {
22        let alert = UIAlertController(
23            title: "Missing Information",
24            message: "Enter both username and password first.",
25            preferredStyle: .alert
26        )
27        alert.addAction(UIAlertAction(title: "OK", style: .default))
28        present(alert, animated: true)
29    }
30}

Blocking navigation without feedback often feels like a broken interface to the user.

Alternative: Remove the Storyboard Segue and Navigate Manually

Sometimes the cleanest design is not to let Interface Builder trigger the segue automatically at all. Instead, connect the button to an action, validate there, and call performSegue only when appropriate.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    @IBOutlet private weak var usernameField: UITextField!
5    @IBOutlet private weak var passwordField: UITextField!
6
7    @IBAction private func continueTapped(_ sender: UIButton) {
8        let username = usernameField.text ?? ""
9        let password = passwordField.text ?? ""
10
11        guard !username.isEmpty, !password.isEmpty else {
12            showValidationError()
13            return
14        }
15
16        performSegue(withIdentifier: "ShowProfile", sender: self)
17    }
18
19    private func showValidationError() {
20        let alert = UIAlertController(
21            title: "Missing Information",
22            message: "Enter both username and password first.",
23            preferredStyle: .alert
24        )
25        alert.addAction(UIAlertAction(title: "OK", style: .default))
26        present(alert, animated: true)
27    }
28}

This approach is often easier to reason about because the code that decides whether navigation happens lives in one place.

Why Cancelling Inside prepare Is a Bad Idea

Developers sometimes try to stop the segue by dismissing the destination, popping the navigation stack, or doing some other reversal inside prepare(for:sender:). That usually creates awkward animations and brittle logic because the transition has already been set in motion.

Even if you can force a workaround, it is the wrong lifecycle point. UIKit already gave you an earlier hook that is meant for the decision itself.

Common Pitfalls

One common mistake is putting validation in prepare(for:sender:) and expecting it to cancel the transition. That method is for setup, not prevention.

Another issue is using shouldPerformSegue but forgetting that it only applies to storyboard-triggered segues. If you call performSegue manually, your action method should do the validation before calling it.

Developers also sometimes block a segue but provide no feedback, which makes the tap feel ignored rather than intentionally rejected.

Finally, if the destination is embedded in a container such as a navigation controller, remember that prepare(for:sender:) still configures the destination chain after the segue is allowed. Do not mix the configuration stage with the decision stage.

Summary

  • You cannot reliably prevent a segue inside prepare(for:sender:) because that method is too late in the lifecycle.
  • Use shouldPerformSegue(withIdentifier:sender:) to block storyboard-triggered segues.
  • If you trigger navigation manually, validate first and call performSegue only when the conditions pass.
  • Keep prepare(for:sender:) focused on passing data and configuring the destination.
  • Always give the user clear feedback when navigation is blocked.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.