Refactoring From a Huge Storyboard to Multiple Smaller and More Manageable Objects

Dealing with storyboard references

Davide Fin
OverApp

--

The Day Has Come

I recently worked on an iOS app that has been on the market for some time now. It is an app published before Apple released its new wonder framework called SwiftUI and was created using a single storyboard to implement the entire UI.

As often happens, an app is conceived, designed, and finally created in one way and, over time, corrections, improvements, and changes in the flows and evolutions are made.

In short, new scenes have been added to the original storyboard and over time, these have become numerous.

The final result is, as many of us have experienced, that Xcode now opens that one big storyboard with effort and slowness.

Not only moving between the scenes, for example, to examine how they are connected to each other, or selecting a view controller to edit it or correct a graphic defect, it is now no longer very easy.

So, the day has come for me too. My job was to eliminate this huge object containing all of the UI by separating the scenes into multiple storyboards.

The goal was having smaller and more manageable storyboards, speed up opening times with Xcode and, in order to one day have more developers working on the app, limit the problems with the merge and the actual conflicts that you usually have when you work under version control.

Multiple Storyboards

I had previously worked on other projects that used multiple storyboards. However, these had been conceived and designed from the beginning and then created. Therefore, it was all very simple.

I knew which view controllers were connected to others distributed in the various scenes. The transition between scenes implemented in different storyboards was generally carried out in the following way:

let storyboard = UIStoryboard(name: “SecondStoryboard”, bundle: nil)let secondVC = storyboard.instantiateViewController(identifier:”SecondViewController”)show(secondVC, sender: self)

Transitions between scenes in the same storyboard were managed with segues set up with interface builder.

I had to face new problems which were relevant to me:

  • Grouping those existing scenes in some way.
  • Separating the groups on different storyboards.
  • Making sure that the navigation between the scenes (distributed in different storyboards) continues to work.

Grouping view controllers was obviously the simplest activity. In part, it was already done. The view controllers that took care of, for example, login, password recovery, etc. were basically placed close together on the storyboard and everything was pretty simple.

For the separation of the scenes and their navigation distributed on several storyboards, I had to look for some contribution on the web to find a comfortable approach.

This allowed me to delve into things I had only heard of and to tackle two different approaches, each interesting in its own way.

A Completely Interface Builder Approach

Ever heard of storyboard references? Apple introduced them in iOS 9 and macOS 10.11. They do exactly what I needed.

They allow you to break a storyboard up into multiple, smaller storyboards. A storyboard reference ties multiple storyboards together, creating one, large, composite storyboard.

I was extremely amazed at how simple their use is. We just have to:

  • Open our huge storyboard.
  • Select the scenes we want to extract and place in a separate file.
  • Use the Refactor to storyboard … option in the Xcode Editor menu.

Our favorite IDE just asks us the name of the new storyboard, we can eventually decide to place the new file inside a new folder or an existing one. Done.

Navigation between scenes with the existing segues works without the need to change anything. The “prepare” functions used to setup view controllers, for example to transmit data to the target scene, work as they are.

A Manual Programmatic Approach

This approach forces manual generation of smaller storyboards.

In my specific case, once I identified the general groups destined to collect the scenes, I duplicated the unique big storyboard to get some absolutely identical files. Therefore, I attributed specific names to the individual duplicates such as “Login”, “Contents”, “Settings”.

I imported these new storyboards into the solution and one after the other, I cleaned them up to leave only the useful scenes for each group.

At the end of the work, the Login storyboard contained only the login scenes. The others each contained their own scenes according to their specialization.

This approach also forces you to write some code to properly call the view controllers implemented on different storyboards. It is the approach already mentioned at the beginning of this article, please find the code example below.

let storyboard = UIStoryboard(name: "SecondStoryboard", bundle: nil)let secondVC = storyboard.instantiateViewController(identifier: "SecondViewController")show(secondVC, sender: self)

It’s easy to write and to read and can be quickly adapted to replace the existing code as follows:

self.performSegue(withIdentifier: "goThere", sender: self)

Eventually, if the target scene requires values from the calling view controller, the code in the prepare function is not useful anymore:

And may be simply replaced with:

Having said that, it would be more convenient if we weren’t forced to explicitly instantiate the storyboard and the view controller every time we need to “jump” from a scene to another.

Routable

Routable allows you to display a scene from a storyboard in a very simple way as it is easy to see in the three methods in this example of the ViewController class:

As you can see, it is not necessary to explicitly instantiate the target storyboard and view controller every time we need to bring the user to a specific screen.

Sending information from source and target controllers is simple and straightforward. See at the end of this article for the complete code of the Routable component.

Recent Updates Introduced by Xcode 11 and iOS 13

To my surprise, during my research, I found out that Xcode 11 and iOS 13 have introduced some new features to storyboards. I am referring to SegueActions and custom initializers.

SegueAction

We have seen that if we use segues for navigation between scenes, we must implement the prepare function to transfer data from the starting view controller to the other.

We have to write a switch statement to check the segue identifier name then prepare values we want to send to the destination controller. Before this, we have to check if the identifier is nil, and eventually exit from the function.

We can avoid the implementation of this function and use instead a SegueAction, a method in our view controller that UIKit calls during the segue.

The sample below shows a simple SegueAction:

We can opt for a more concise form if we do not need all the input parameters:

The destination view controller should be implemented with a custom initializer as shown in the following example:

The procedure to create a SegueAction is very easy. We create the segue as always in IB, in my example, I am connecting my blue button to a destination view controller:

We choose the kind of segue:

Then we create the SegueAction as if we were to create an IBAction by control-dragging the connection from the segue and the code of the viewController:

We give a name to the new function:

And finally, the SegueAction is created.

In my example, I added at the end the myData parameter to transmit it to the target ViewController:

The prepare function is not needed anymore.

Custom initializers

The custom initializer created in the sample discussed above may be used also in a different way. I already mentioned that transitioning between ViewControllers can be done without using segues.

For example, we can create a common IBAction like the following:

iOS 13 provides a version of instantiateViewController that accepts a creation block, which allows us to call the custom initializer:

Conclusion

We will still have to deal with storyboards for a long time. We all certainly have applications on the market that will require maintenance and evolution.

Not only that, but many developers will still be reluctant to adopt SwiftUI for the creation of new projects as they consider it a technology not yet mature (is it true?).

Others will have projects that are currently under development, which started before SwiftUI was released and which may not see the light for a long time.

Therefore, we will still have to deal with a very useful tool that puts us in front of the problems that we all know well and which I mentioned in the article.

When we are forced to separate a large storyboard into smaller and more manageable objects, storyboard references or a coding approach like the one proposed are therefore two comfortable solutions.

Also, it’s nice that Apple still releases new features in the storyboards, proof that it realizes that there are many apps on the market with a UI created using such a tool and that will definitely need maintenance and evolution for a few years yet.

Complete Code for Routable Component

Routable and its storyboard extension were both written by Osvaldo Pirrello, CEO and founder at OverApp, whom I thank.

Routable.swift

Extension+UIStoryboard.swift

--

--