iOS Development
Swift Programming
Segues
Mobile App Development
Programming Tutorial

How to segue programmatically in iOS using Swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Programmatically triggering a segue in iOS is useful when navigation depends on validation, network results, permissions, or any other runtime condition. Storyboards still define the transition, but your code decides when to run it. In UIKit, the most direct way is performSegue(withIdentifier:sender:).

Creating the Segue in Interface Builder

Even for a programmatic segue, you usually create the segue visually in the storyboard first. Connect the source view controller to the destination view controller, then assign a segue identifier such as ShowDetails.

That identifier is what your Swift code will use later. Without it, performSegue has no way to know which transition to execute.

Triggering the Segue from Code

Here is a common UIKit example. A button tap checks whether the form is valid and only then triggers navigation.

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 signInTapped(_ sender: UIButton) {
8        guard isFormValid() else {
9            showValidationError()
10            return
11        }
12
13        performSegue(withIdentifier: "ShowDashboard", sender: self)
14    }
15
16    private func isFormValid() -> Bool {
17        let username = usernameField.text ?? ""
18        let password = passwordField.text ?? ""
19        return !username.isEmpty && !password.isEmpty
20    }
21
22    private func showValidationError() {
23        let alert = UIAlertController(
24            title: "Missing Information",
25            message: "Enter both a username and password.",
26            preferredStyle: .alert
27        )
28        alert.addAction(UIAlertAction(title: "OK", style: .default))
29        present(alert, animated: true)
30    }
31}

This keeps navigation tied to actual business logic instead of wiring the transition directly to the button in the storyboard.

Passing Data in prepare(for:sender:)

Running the segue is only half of the pattern. Most of the time you also need to configure the destination controller before it appears.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    private let currentUser = "mark"
5
6    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
7        if segue.identifier == "ShowDashboard",
8           let dashboard = segue.destination as? DashboardViewController {
9            dashboard.username = currentUser
10        }
11    }
12}
13
14final class DashboardViewController: UIViewController {
15    var username: String?
16
17    override func viewDidLoad() {
18        super.viewDidLoad()
19        title = username
20    }
21}

prepare(for:sender:) is the right place to inject data into the destination when you are using storyboard segues. Keep it focused on configuration rather than complex logic.

Deciding Whether a Segue Should Happen

If a segue can also be triggered from the storyboard, you can intercept it with shouldPerformSegue(withIdentifier:sender:). This is useful when a button is wired to a segue visually, but navigation should be blocked under some conditions.

swift
1import UIKit
2
3final class ProfileViewController: UIViewController {
4    var hasAcceptedTerms = false
5
6    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
7        if identifier == "ShowCheckout" {
8            return hasAcceptedTerms
9        }
10        return true
11    }
12}

If you are already calling performSegue yourself, you often do not need this override because the guard logic can live in the action method.

Segue vs Direct Navigation

A common source of confusion is treating every navigation path as a segue. If you create view controllers entirely in code or use a navigation controller without storyboards, you may push or present the destination directly instead.

swift
1import UIKit
2
3final class SettingsViewController: UIViewController {
4    @IBAction private func openHelp(_ sender: UIButton) {
5        let help = HelpViewController()
6        navigationController?.pushViewController(help, animated: true)
7    }
8}

That is not a segue. It is direct navigation. Both approaches are valid, but they solve slightly different problems.

Common Pitfalls

The most frequent failure is a mismatched segue identifier. If the storyboard uses ShowDashboard and your code calls showDashboard, the app crashes at runtime because the identifier must match exactly.

Another common issue is forgetting that the destination may be wrapped in another container, such as a navigation controller. In those cases, segue.destination is the container, not the final content controller.

Developers also put too much logic into prepare(for:sender:). That method should configure the destination, not perform validation, networking, or side effects that belong earlier in the flow.

Finally, avoid mixing storyboard-triggered segues and manual performSegue calls for the same user action unless the control flow is very clear. Otherwise, you may trigger navigation twice or make debugging harder than necessary.

Summary

  • Create the segue in the storyboard and assign a stable identifier.
  • Call performSegue(withIdentifier:sender:) when runtime logic should decide when navigation happens.
  • Pass data in prepare(for:sender:) before the destination appears.
  • Use shouldPerformSegue only when storyboard wiring needs a final gate.
  • Prefer direct pushViewController or present when the destination is created entirely in code.

Course illustration
Course illustration

All Rights Reserved.