iOS Storyboard

UIStoryboard Best Practices

Introduction

Recently I have working on an app which had a big storyboard and it was difficult to manage all view controllers in it. So I decided to split this big storyboard into small storyboards. But I have been facing another problem on replying on string literals to instantiate view controller, which is not a safe way.

For example:-


let storyboard = UIStoryboard("Main",bundle: nil);
let identifier = "ArticleViewController"
let viewController = storyboard.instantiateViewControllerWithIdentifier(identifier) as! ArticleViewController

So now, the problem with string literals there is always a chance of typing mistake while writing the identifier. To solve this I thought to define the identifiers of each view controller as Global Constants string literals. Initially this was a good idea but for an app with 25-30 view controller it was difficult to manage Global Constants and on changing the identifier value at one place might affect other places. So lets see what are the best practices to work and manage UIStoryboard.

UIStoryboard Best Practices

As we all know that swift is a "Protocol-Oriented-Language", we have made UIStoryboard safer and eaier to work with we using protocols, extension generics and enums.

Readable UIStoryboard Names

Your storyboard should have readable names and it will be better if we give them names based on the module. For example in our app we have an module for "Articles", so the storyboard name should be "Article.storyboard"

Uniform Storyboard Identifiers

When we intend to work with storyboard identifiers in our app, the best practice is to give the class name as identifier. Suppose we have a class named ArticleViewController.swift so the storyboard identifier should be "ArticleViewController". This will reduce the burden to think a unique identifier and creates uniformity through out our app.

Add Python to PATH

Enums

To make instantiation safe with storyboard, we should create an extension to the UIStoryboard class consiting of enum which defines all the different storyboard files we have in our project.


extension UIStoryboard
  enum Storyboard: String{
    case Main
    case ArticleStoryboard
    case NewsStoryboard
  }
}

After following above three points you will see that working with storyboard and instantiating a view controller will be a lot safer and ther is uniformity through out our app. The XCode will also us help out by providing auto-complete when we begin typing identifier.

let storyboard = UIStoryboard(name: UIStoryboard.Storyboard.NewsStroyboard.rawValue, bundle: nil)

The above code will run & work fine but still the code syntax it bit too long and ugly.

So, to solve this and make our syntax better we will add a convience intializer in our UIStoryboard extension


convenience init(storyboard: Storyboard, bundle: NSBundle? = nil) {
    self.init(name: storyboard.rawValue, bundle: bundle)
}

Protocol Extensions and Generics

Generally an app won't have many storyboard, for an app having 10-15 storyboard it is easily maintable using enums. But an app can have more than 50 UIViewController, so to manage them using enums is quite difficult. We should use protocols to mangage it.

StoryboardIdentifiable Protocol


protocol StoryboardIdentifiable {
    static var storyboardIdentifier: String { get }
}

We’ve made a protocol which gives any class that conforms to it, a static variable, storyboardIdentifier. This will reduce the amount of work we have to do when managing identifiers for view controllers.

StoryboardIdentifiable Protocol Extension

extension StoryboardIdentifiable where Self: UIViewController {
    static var storyboardIdentifier: String {
        return String(self)
    }
}

Here we have use where clause which makes it only apply to classes that are either UIViewController or it’s subclasses.

#### StoryboardIdentifiable global conformance

```swift

extension UIViewController : StoryboardIdentifiable { }

The above line will make every UIViewController within our project conforms to the StoryboardIdentifiable protocol.

UIStoryboard Extension with Generics

// ViewController Instantiation
    func instantiateViewController<T:UIViewController where T: StoryboardIdentifiable>() ->T{
        guard let viewController = self.instantiateViewControllerWithIdentifier(T.storyboardIdentifier) as? T else{
            fatalError("Couldn't instantiate view controller with identifier with \(T.storyboardIdentifier)")
        }

        return viewController
    }

The above function will instantiate a view controller by taking its storyboard identifier, add this function in UIStoryboard extension.

How to use

let storyboard = UIStoryboard.storyboard(.ArticleStoryboard)
let viewController: ArticleViewController = storyboard.instantiateViewController()
viewController.printHeadline()
presentViewController(viewController, animated: true, completion: nil)

or you can write it more compact way as

let viewController: ArticleViewController = UIStoryboard(storyboard: .ArticleStoryboard).instantiateViewController()
presentViewController(viewController, animated: true, completion: nil)

So we’ve managed to get rid of ugly, unsafe string literals as identifiers by replacing them with enums, protocol extensions and generics.

results matching ""

    No results matching ""