learning iOS and Swift

Type-safe method to instantiate a viewcontroller from storyboard in Swift

The Problem

I really like storyboards, i like to draw my ui flow in one place without need of creating new .xib files for each controller. But there is a thing i miss from olds xibs, with xibs i can instantiate viewcontrollers in a single line of code:

I don’t like this way, you have to cast always to the right class, identifiers are hard-coded strings, you have to add them in Interface Builder and then you have to remember and copy the strings everywhere in your code. Misspelling are behind the corner!
And of course if you decide to change the name of the identifier, you need to change it in every place it was hard-coded. A solution to hard-coding identifiers could be the use of enums:

Swift

1

2

3

4

5

6

7

8

9

10

enumStoryboardIdentifiers: String{

caseLoginViewController

caseSettingsViewController

caseAddressbookViewController

}

letstoryBoard=UIStoryboard(name:"Main",bundle:nil)

// instantiateViewControllerWithIdentifier returns an UIViewController object so we need cast to right type

The Solution

I think the final use is very elegant and easy to use, you will have to design your viewcontroller in storyboard, assign a class, and use the same class name as identifier in Interface Builder. After few search, i wrote these lines of code:

First, i create a protocol called StoryboardInstantiable. Class variables or class methods are not allowed in protocols, use static instead.

Swift

1

2

3

4

protocolStoryboardInstantiable: class{

staticvarstoryboardIdentifier: String{get}

staticfuncinstantiateFromStoryboard(storyboard:UIStoryboard)->Self

}

Now we can extend UIViewController class to conform StoryboardInstantiable protocol. The idea is to use the view controller’s class name as identifier, so we will write it only once in Interface Builder.

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

extensionUIViewController: StoryboardInstantiable{

staticvarstoryboardIdentifier: String{

// Get the name of current class

letclassString=NSStringFromClass(self)

letcomponents=classString.componentsSeparatedByString(".")

assert(components.count>0,"Failed extract class name from \(classString)")