TabBar Items with Swift Enums

Written by raymund.cat | Published 2017/11/16
Tech Story Tags: swift | ios | design-patterns

TLDRvia the TL;DR App

A quick tip on managing your child ViewControllers

Tabbed Navigations is pretty convenient and fun in iOS. Thanks to the native UITabBarController, a subclass of UIViewController, it doesn’t really need not much setting up other than its child ViewControllers.

But after that, admittedly, we do still find ourselves in between some dilemmas managing the child ViewControllers especially when we need access to them from the TabBarController. There are several different ways of accomplishing it depending on several more factors like weight or intention of the tasks, some of them being:

  1. Child View Controller positions should be easily changed/switched without much issues but subjective design.
  2. Parent TabBarControllers should not have to know much about the child ViewControllers other than the cases they need to communicate.
  3. Parent TabBarControllers should be able to easily access and accurately distinguish child ViewControllers from each other.

One simple way of tracking the child ViewControllers is through their String tabBarItem.title property. If name comparison is all you need, maybe that will be do?

But be careful still. Strings are easy to implement and modify, but also very loose if you don’t pay strict attention to them. Using Strings as a point of comparison can easily result in a lot of false flags without you knowing, so I would stay away from it for now.

Another way can be done through data types. Similar to the title comparisons, you will probably end up with a lot of if else ‘s and because of this, it is only mostly applicable given no two child ViewControllers share the same type.

Unlike String comparisons, this may save you from accidentally misinterpreting rogue values. But if you go this route, testing the parent UITabBarController will be extra tricky since since relying on strict data types for identifying objects will limit your mocking capabilities.

Describing our Tab Items through Protocols and Enums

Another way of managing child View Controllers that I’d like to share with you, is through a mix of Swift Enum and Protocol Implementation. By creating a finite set of TabBar identity values for your child ViewControllers to hold, you also improve their testability by giving them a good base for mock or test classes.

First, We’ll implement our finite set of identities namedTabBarType through a set of Enum values representing our expected TabBar Items.

enum TabBarType: String {

case featured = "Featured"

case accounts = "Accounts"

case cart = "Cart"

case search = "Search"

case none = "None"

}

This way we can lock down our possible values to make sure we can catch any rogues as early as compile time.

Second, we’ll create a short Protocol named TabBarChild to describe an object that holds a TabBarType value representing them.

protocol TabBarChild {

var tabBarType: TabBarType? { get set }

}

Third, we’ll make our designated child ViewController classes conform to the new protocol so their parents know how to communicate to them as TabBar children. This can be accomplished in many different ways depending on your team’s preferred design, but in this article I can show you a couple.

Solution 1: Extending UIViewController

The simpler one is through extending the UIViewController class to conform to the TabBarChild protocol. You basically just assume every UIViewController to be TabBarChild -capable with the drawback of having to design ViewControllers to store/compute the value of TabBarType a little less conventionally.

extension UIViewController: TabBarChild {

var tabBarType: TabBarType {

    get { return TabBarType(rawValue: tabBarItem.title ?? "" ) ?? .none }

    set { tabBarItem.title = newValue.rawValue }

}

}

In this example, I chose to store its value to the ViewController’s tabBarItem.title. With this, you can easily modify how the ViewController is displayed by simply changing the TabBarType ‘s value. It is a convenient approach as it also lets you assign/remove TabBarType properties. Here’s a sample of what it would look like in implementation:

//somewhere in you codelet viewController = SearchViewController()viewController.tabBarType = .searchtabBarController.viewControllers.append(viewController)

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

if viewController.tabBarType == .search {  
    //do something with the search tab here?

}

return true

}

Solution 2: Custom UIViewController Extensions

Not a fan of the solution above? Another way is going further and removing TabBarChild ‘s set access, leaving the conforming object with an immutable TabBarType property so their implementation with sort of look like this:

protocol TabBarChild {

var tabBarType: TabBarType { get }

}

extension AccountsViewController: TabBarChild {

var tabBarType: TabBarType {

    return .accounts

}

}

This lets you assign TabBarType properties to your child ViewControllers more securely. On your TabBar management layer, accessing them may look like this:

//sample UITabBarDelegate callsextension HomeFlowController: UITabBarControllerDelegate {

func tabBarController(\_ tabBarController: UITabBarController,     shouldSelect viewController: UIViewController) -> Bool {

    if let tabBarChild = viewController as? TabBarChild,

        tabBarChild.tabBarType == .accounts {

        //do something with accounts tab here?

    }

    return true

}

}

Since it is agnostic about your child ViewController type, you can easily keep the changes in your ViewController implementation from directly affecting your Tab Management logic.

What’s Next?

The solutions I presented here can definitely be further expanded so try and work around other possibilities. See which one may work best for you and your team. Would adding more properties/functions to TabBarChild protocol help you better? Or would a more simplified version do the job just right?

And if you got your own solutions, please feel free to share them here and I’m very interested to hear about them!

You made it all the way here!

I hope that you got what you’re looking for when you opened this article. If so, I would very much appreciate it if you would recommend this to your friends ^^

if you have any more questions/feedbacks, please feel free to drop a message!


Published by HackerNoon on 2017/11/16