Category Archives: storyboards

Testing Storyboards in Swift?

Awhile back I blogged about how to test storyboards by loading the ViewController from a Storyboard. Recently I’ve been trying to figure out how to do the same thing in Swift.

Update: latest code appears to be working ok for testing view controllers as long as I don’t try to run them on an actual iPhone device. When I do that, the test hangs.

The code and instructions posted by Mike Cole appear to almost work in Swift with the latest Xcode (I’m using Xcode 6.2, but 6.1 works also). I had to also reference the ViewController’s view. Evidently the ViewController will lazy load the view, meaning that the outlets don’t become connected until after ViewController.view is referenced.

Also, be careful what you put into your view controller’s viewDidLoad method. Code that requires that the view is actually being displayed should be moved to viewWillDisplay or may cause the unit test to crash (since there isn’t really a display during unit testing).

Example XCTest setup code to load a viewcontroller named “MainVC” from main.storyboard.

override func setUp() {
  super.setUp()
        
  let storyboard = UIStoryboard(name: "Main", bundle: NSBundle(forClass: self.dynamicType))
  vc = storyboard.instantiateViewControllerWithIdentifier("MainVC") as ViewController
        
  let dummy = vc.view // force loading subviews and setting outlets
        
  vc.viewDidLoad()
}

I’ve left the previous post below, but it’s basically outdated now. After running into various problems, I resorted to using Objective-C unit tests, and describe how to do so below. I wouldn’t recommend doing so anymore, since Swift tests appear to work ok now.
After fumbling around for awhile with the Swift test case file, and not making much progress, I decided to try something different, and tried adding an Objective-C test case file to my Swift project. At least in this case, I know how to load the ViewController from the Storyboard. And this actually worked.

This turned out to be fairly simple. Let me warn you though, that I consider this approach to be a temporary hack, albeit an interesting one. I believe the correct solution will be to figure out how to do this in a Swift unit test case file. At this point though, I’m not sure if I’m being prevented from doing so due to bugs in beta level Xcode 6, or just my lack of Swift knowledge.

But caveat emptor aside, here is how to create an Objective-C unit test case file in an otherwise entirely Swift project, and use it to load and test a ViewController from a storyboard:

  • Set the product module name so that you will know the swift header file name to import (eg. “#import ProductModuleName-swift.h”). Note that this should not be necessary, as the Product Module Name is supposed to default to the Product Name. This wasn’t working as of Beta 2.
  • Add an Objective-C unit test case file. Make sure to set the language to Objective-C. It will default to Swift.
  • I did not have to create an Objective-C Bridging  Header for the Swift ViewController, because I’m only going the other way, from Swift to Objective-C, but I did anyways when prompted. I don’t think having it will hurt anything, and you might want it later.
  • Import the swift header file into the Objective-C unit test case file:
    #import “ProductModuleName-swift.h”

That was it. This then gave me access to my ViewController definitions. Then use the previous described method of loading a storyboard and viewcontroller from it.

I will update this article once I figure out how to do it correctly in a Swift unit test case file.

Using Storyboards

We generally do not use Storyboards in our projects where I work. That’s mainly because, at least as of iOS 6, they cannot be merged. This means that only a single developer can make changes to a storyboard at any given time. However, it appears that Apple is working on correcting this situation. So I’ve been taking another look at Storyboards, and investigating how these can be integrated into our best practices (TDD, clean code and architecture).

The first question that I had was “how to unit testing view controllers loaded from a storyboard?”. This appears to be fairly easy, and I found the answer on Rafael Adson’s blog. Simply load the view controller from the storyboard in the test setUp method:

- (void)setUp
{
    [super setUp];
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    self.vc = [storyboard instantiateViewControllerWithIdentifier:@"main"];
    [self.vc performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
}

You’ll need to set the identifier for the view controller in the storyboard, and then modify the code above to match the name of your storyboard and the id you chose for the view controller.

The second question is how to integrate storyboards into the VIPER architecture. First off, it seems that the storyboard should replace the wireframe component. But there’s a problem. Normally the wireframe would be responsible for creating the view controller, interactor, and presenter. But the storyboard only creates a view controller.

Now, we can easily have the view controller’s viewDidLoad method create the interactor and presenter, but that results in making the view controller aware of those components. From a clean architecture perspective, we’d prefer that these components remain as detached from each other as possible.

That said, I don’t know how the storyboard can be used to create all three components, so I guess we’re stuck with the viewDidLoad hack at this point. I’m continuing to investigate, and I’d love to hear your suggestions.