How to Dump the Storyboard but Keep the Visualization

by Lou Franco

Filed under: iOS and Storyboards

Storyboards get a bad rap. Sure, they are impossible to test and code review, and merges are a real pain. But it takes a second to look at a Storyboard and visualize what the UI is going to look like (or even if you are in the right file). If only there was a way to preserve the visualization but dump the XML.

“Oh great, it’s a giant XML file,” said no one ever

When I am just working on a tiny app or something I want beginners to understand, I use Storyboards all day long. You get instant feedback, so it’s super-fast to make a UI. I even use them on bigger projects because my teammates and I have strategies to avoid Storyboard merges.

But I get it. You can’t test them, you can’t share code easily, there’s no real abstraction possibilities. It’s probably the only place in your code base where you are reduced to using implicitly unwrapped optionals.

I know it’s wrong, but I just can’t quit them. I envy the web dev’s edit-save-view cycle, whereas I have an edit-build-wait-wait-view cycle. Storyboards are my only hope of faster feedback.

Or so I thought.

I think Kickstarter is on to something

Last year Kickstarter open-sourced their app, and started giving talks about it. In this video Brandon Williams says, “We were able to completely replace Storyboards with Playgrounds”. The truth is more complicated because there are actually a lot of Storyboards in the app.

But, forget about that for a second because the demo is pretty compelling. He showed how they were able to load a view controller in a Storyboard and inject locale and device size to see a view in a variety of environments instantly.

I have been playing around with Playgrounds a lot, but I was thinking mostly about stand-alone Playground books, not integrating them into the app development environment.

It turns out it’s not that hard.

The Kickstarter project achieves this with a pure functional approach, but the main thing you need is dependency injection (what Brandon calls “no co-effects”, but what we’ll call “passing in parameters”). All of the rest of what the Kickstarter app does is very cool and valuable, but for just getting playgrounds to show a view, you don’t need much.

  1. Put all code you want to access in the Playground into a Framework (you need this to be able to import into the Playground)
  2. Instantiate the View Controller
  3. Inject its data and environment
  4. Set it to the Playground page’s liveView property

#3 is a doozy if your app is normal and just made up of a bunch of Massive View Controllers. But, if you have some discipline, you might be able to pull it off.

Here is my playground code:

import PlaygroundSupport
import UIKit
import ViewsInPlaygroundSampleFramework

let vc = ViewController()

vc.set(message: "Hello, Playground!")

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = vc

For this VC:

public class ViewController: UIViewController {

    let hello = UILabel()

    public override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.white

        self.view.addSubview(hello)

        hello.translatesAutoresizingMaskIntoConstraints = false
        hello.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
        hello.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
    }

    public func set(message: String) {
        hello.text = message
    }

}

It looks like this:

View Controller in a Playground

You can download the full app and playground on GitHub.

How to get there

For most apps, this isn’t feasible because our ViewControllers are probably not as pure as this one or Kickstarter’s. But, you might be able to adopt this for new code with a few steps

  1. Don’t access your singletons directly. Like Kickstarter, wrap them in a struct and pass them in (dependency injection)
  2. Don’t access CoreData or wherever you are storing your data (you know the drill … pass them in)
  3. Obviously, don’t write out anything. In this case, your best bet is some kind of delegate. If you use MVVM or something similar, great. Just make sure your ViewModel is a protocol that you can mock in the Playground.

If you do this, you will get other benefits like testability, the possibility of automatic screenshot generation, and lots of code reuse and abstraction opportunities. All without losing the benefit of fast UI feedback cycles and visualization.

Never miss a post

Get more tips like this in your inbox