Swift
iOS Development
Tab Bar Controller
App Development
Programming Tips

Programmatically switching between tabs within Swift

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

Programmatically switching tabs in iOS is usually just a matter of changing the selected tab on UITabBarController. The real complexity comes from where that code runs, how much navigation state each tab should preserve, and whether the tab switch is triggered by user actions, deep links, or cross-screen coordination.

The Basic Mechanism

UITabBarController exposes a selectedIndex property and a selectedViewController property. If you already have access to the tab bar controller, changing the current tab is straightforward.

swift
if let tabBarController = self.tabBarController {
    tabBarController.selectedIndex = 1
}

That code works from a view controller embedded inside the tab bar hierarchy. Index 0 is the first tab, index 1 is the second, and so on.

You can also switch by controller reference instead of numeric index:

swift
1if let tabBarController = self.tabBarController,
2   let controllers = tabBarController.viewControllers,
3   controllers.count > 2 {
4    tabBarController.selectedViewController = controllers[2]
5}

Using selectedViewController can be clearer when you already know which child controller you want.

Switching Tabs from a Child View Controller

A common use case is tapping a button on one screen and jumping to another tab.

swift
1import UIKit
2
3final class HomeViewController: UIViewController {
4    @IBAction func goToSettingsTab(_ sender: UIButton) {
5        tabBarController?.selectedIndex = 2
6    }
7}

This is the simplest pattern and is often all you need.

Reaching the Tab Bar Controller from Elsewhere

Sometimes the code that needs to switch tabs is not directly inside a tab child controller. In that case, you need a reference to the root tab bar controller.

swift
1if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
2   let window = scene.windows.first,
3   let tabBarController = window.rootViewController as? UITabBarController {
4    tabBarController.selectedIndex = 1
5}

This works, but it couples unrelated code to your app's root structure. For larger apps, a coordinator or routing layer is cleaner than reaching through the window hierarchy.

Preserving Navigation State

Each tab often contains its own navigation controller. Switching tabs does not automatically reset the navigation stack inside those controllers. That is usually good, because users expect each tab to remember where they were.

If you do want to reset the target tab before switching, pop its navigation stack explicitly.

swift
1if let tabBarController = self.tabBarController,
2   let nav = tabBarController.viewControllers?[1] as? UINavigationController {
3    nav.popToRootViewController(animated: false)
4    tabBarController.selectedIndex = 1
5}

That is useful for flows like "Go back to the main feed tab and show its root screen."

Programmatic tab switching is common in notification handling and deep linking. For example, a link to the profile area may need to select the profile tab first and then push a details screen inside it.

A clean structure is:

  1. Resolve the route.
  2. Select the target tab.
  3. Then hand off any deeper navigation to that tab's navigation controller.

Trying to push a controller before selecting the correct tab often leads to confusing navigation behavior.

Avoid Magic Numbers When Tabs Matter Semantically

Hardcoding selectedIndex = 3 is fine in small demos but fragile in production apps. If tab order changes, the wrong screen opens. An enum helps keep intent readable.

swift
1enum AppTab: Int {
2    case home = 0
3    case search = 1
4    case settings = 2
5}
6
7tabBarController?.selectedIndex = AppTab.settings.rawValue

This is still index-based under the hood, but the call site becomes easier to maintain.

Common Pitfalls

A common mistake is trying to switch tabs from a view controller that is not actually inside the tab bar hierarchy. In that case, self.tabBarController is nil.

Another issue is assuming that switching tabs resets the destination controller automatically. It does not. If you need a clean stack, reset it explicitly.

Developers also sometimes perform the tab switch off the main thread because it was triggered by async work. UI updates, including tab selection, must happen on the main thread.

Summary

  • Switch tabs by setting selectedIndex or selectedViewController on UITabBarController.
  • 'self.tabBarController is the easiest access path from a child view controller.'
  • Reset the target tab's navigation stack manually if needed.
  • Use a routing layer for deep links and app-wide navigation triggers.
  • Prefer named enums over magic tab indices in larger apps.

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.