For the last few years, I’ve always created a dummy UITester target in any app I’m working on. This target is something akin unit tests for interface elements. After talking to Matt earlier today we realized that perhaps not everyone knows this trick, and I think it’s something every iOS dev should be doing.
Here’s what this target does: Each new interface element (buttons, sliders, image views, etc.) will have its own testing view controller. This view controller can do whatever needs to be done. It can animate elements, handle touches, run events on timers, fire notifications, or anything else. Iterative development is greatly simplified when you don’t need to run your full app to test whether an element is working as it should. Additionally, any other engineers on your team will be able to use your testing view controller to see how a new element should be used.
I’ve created a dummy app that has two custom interface elements, an
The first step is creating a UITester target. Create a new Master-Detail Application in Xcode. For those of you new to Xcode 5 (i.e. everyone) the command to create a new target is accessible by selecting your Project in the Project Navigator and selecting “Add Target…” from the “Editor” menu. Feel free to erase any testing targets one is created, you won’t be using it.
You should open the newly created
Main.storyboard and delete the
DetailViewController, we won’t be using it. In the project, delete the
DetailViewController.[h|m] files as well.
Each interface element should have an associated view controller. This view controller is responsible for displaying the element and handling any user interactions you’d like to test. Go head and create two new view controllers,
GreenSliderViewController. These should be imported in
Here’s how I like to setup my
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
Adding a new testing view controller is as simple as:
- Add another value to the
nameForElementTestName:to return the string that will be visible in the
viewControllerForTestName:to return the View Controller that will be pushed onto the nav stack.
And that’s about it. Use the
OrangeButtonViewController classes to setup your interface elements appropriately for testing, and you’re good to go.
If you end up making heavy use of this UITester target, you are going to want to enable state restoration. Iterative development is much easier if the element you’re working on is always open when you restart the app.
First step, you’ll need to give Restoration IDs to the nav controller and tableview controllers in your
Main.storyboard. Use caution and make sure you are giving the restoration ID to the view controller, not one of it’s views.
Next, enable state restoration in the
UITester target. In the app delegate, add the following lines.
1 2 3 4 5 6 7 8 9 10 11
Each view controller in your app should have its restoration ID set. I found that, for an simple app like this, having a superclass handle the restoration is the way to go. All my testing view controllers are subclasses of a
RestorableViewController class. Here are the contents of its
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
One sticking point that gets me every time I create a new UITester target: you must explicitly declare that you implement the
UIViewControllerRestoration protocol. Just implementing the proper methods is insufficient and if you do not declare this than state restoration will silently and confusingly fail.
I hope this was helpful. Setting up a new target like this has certainly made developing new interface elements more straightforward for me.