Detect UITabBar Selected Index/ Item Changes that is set Programmatically

Along all answers here, if you subclassed UITabBarController already, here's a simple solution for all tab bar changes (User initiated and programmatic ):

// Override selectedViewController for User initiated changes
override var selectedViewController: UIViewController? {
    didSet {
        tabChangedTo(selectedIndex: selectedIndex)
    }
}
// Override selectedIndex for Programmatic changes    
override var selectedIndex: Int {
    didSet {
        tabChangedTo(selectedIndex: selectedIndex)
    }
}

// handle new selection
 func tabChangedTo(selectedIndex: Int) {}

Previous answers are sufficient to "detect" the changes, however it does not detect which index is being pressed.

func selectItemWithIndex(value: Int) {
    self.tabBarControllertabBarController.selectedIndex = value;
    self.tabBar(self.tabBar, didSelectItem: (self.tabBar.items as! [UITabBarItem])[value]);
}

self.selectedIndex will not return the selected index right away. To check which item is being pressed, we would need to compare the item with the tabBarItems in our UITabBarController

override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
    if item == (self.tabBar.items as! [UITabBarItem])[0]{ 
       //Do something if index is 0
    }
    else if item == (self.tabBar.items as! [UITabBarItem])[1]{
       //Do something if index is 1
    }
}

In swift, you can do it by overriding selectedIndex property of UITabBarController.

First subclass UITabBarController and add all the following code.

//Overriding this to get callback whenever its value is changes
   override var selectedIndex: Int {
      didSet {
         handleTabSelection(selectedIndex: selectedIndex)
      }
   }

Also add this delegate method of UITabBarControllerDelegate

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    //Tab tapped
    guard let viewControllers = tabBarController.viewControllers else { return }
    let tappedIndex = viewControllers.index(of: viewController)! 
    //Tab tapped at tappedIndex
    handleTabSelection(selectedIndex: tappedIndex)
}

Finally, we call this method from both places so that all the cases are handled.

private func handleTabSelection(selectedIndex: Int) {
    //Do something on tab selection at selectedIndex
}