Screenshot Automation

Screenshot Automation

Taking screenshots is a tedious process. Everytime your app has an update, you have to take new screenshots, for different resolutions in different languages. I think we can all agree that developers have better things to do with their time than going through this again and again. In this blogpost I'm going to describe one solution of how you can automate this process.

The tool I used is called snapshot It's a very developer-friendly tool and the setup doesn't take long. The github page of snapshot provides a very good explanation so I'm just going to give a short summary of the setup.



Go to terminal and type:

sudo gem install snapshot

now go to your project folder and type

snapshot init

this will generate some files for you in your project folder, one of them is the snapshot.js file, this is where you write all the automation code. We’re not really going to write a lot, instruments will do this for us in the next step.


Generate/Write code

Open your app in Xcode, profile it (cmd + i), select automation, and click the Record button on the bottom of the window.

Your app will run in the simulator and code will be generated whenever you tap on something in the simulator. The generated code will look like this:

target.frontMostApp().mainWindow().buttons()["red"].tap(); target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["purple"].tap(); target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["cyan"].tap(); target.frontMostApp().navigationBar().leftButton().tap();

Lets see what we can make of this:

  1. tapped the red button, then back.
  2. tapped the purple button, then back.
  3. tapped the cyan button, then back.

What we see here is that the titleLabel text of the button is used to identify the buttons. Is this really what we want?
Probably not, in most cases this text will be translated.
A better way to identify buttons (or UIViews in general) is by using the accessibilityIdentifier. This goes for UITesting as well.
Make sure not to use the accessibilityValue or accessibilityLabel because this will be read out loud when people turn on the accessibility voiceover.


Improve the generated statements

target.frontMostApp().mainWindow().buttons()["first_button"].tap(); target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["second_button"].tap(); target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["third_button"].tap(); target.frontMostApp().navigationBar().leftButton().tap();

Now we're referencing the accessibilityIdentifiers instead of the text on the button.


Let's take some screenshots

Use this statement to take a screenshot: captureLocalizedScreenshot("x"). Replace x by the name you want to give your screenshot.

For example:

target.frontMostApp().mainWindow().buttons()["first_button"].tap(); captureLocalizedScreenshot("0-Red") target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["second_button"].tap(); captureLocalizedScreenshot("1-Purple") target.frontMostApp().navigationBar().leftButton().tap(); target.frontMostApp().mainWindow().buttons()["third_button"].tap(); captureLocalizedScreenshot("2-Cyan") target.frontMostApp().navigationBar().leftButton().tap();

Go to your project folder in terminal and type: snapshot

Your screenshots will be stored into ./screenshots/ by default.

Tip: As a convention I always start the name of the screenshots with a number. Numbers are chosen according to module. A module would be a group of functionality, for example if you have a viewcontroller with a feed, and when you press on an item you go to the details of that item, these two screens would fall under the same module. Their names would then be:

  • 0-Feed
  • 0-FeedDetail

Another module could be Friends:

  • 1-FriendsList
  • 1-SearchFriends

And so on… If you follow this convention it will be easier to find the right screenshot (especially when you have a big app).


Continuously integrate screenshotting with Jenkins

If you want to use the screenshots to easily check translations or design changes on different resolutions, it might come in handy to let this job be run by a buildserver. To run this job on jenkins:

  1. Create a new job
  2. Checkout your project from the repository (preferably from your master or another stable branch)
  3. Make it build periodically for example at 6am on weekdays, this way the job wont interfere with other jobs, because it might take a while to do all the screenshotting 312u61z.jpg
  4. Add build step: execute shell:
#!/bin/bash snapshot



Nice to know

Modify the snapfile to customize:

Scheme used to build the app
Workspace path
iOS version

Snapshot automatically waits for network requests to be finished before taking a screenshot, to avoid having loading indicators on the screens. This might not always be enough, I still had some loading indicators on screen. To prevent this you can set a delay: target.delay(3) Keep in mind that setting delays will also cause the job to run longer…

When generating the javascript code with instruments, it might be a good idea to do this on an iPhone 4 (the device with the smallest screen) by doing this you prevent clicking out of bounds.

If you want to speed up your process even more, you can use deliver to upload your screenshots automatically to iTunes connect


Source code sample project

You can find the source code of the example describe above here: