Using Fastlane Snapshot to generate screenshots with UIPickerViews

This week, I released an update for my Tip Solver calculator to add Chinese localization. I had to generate 5 screenshots for 5 devices (iPhone and iPad) across 3 languages. In the time that I spent automating the process with Fastlane Snapshot, I could have easily done it manually in way less time. But the good news is that I’ve set myself up to painlessly generate screenshots for new languages. Snapshot takes some time to run, but it’s still a huge improvement over generating screenshots manually.

It took me a lot longer than I would have liked to setup my Snapshot process due to my usage of UIPickerViews. Tip Solver makes heavy usage of UIPickerView and I ran into many issues with UITest.

Your mileage may vary, but I found I had to do the following to be able to use UITest and UIPickerViews:

  • disable Ads (which run over the network)
  • drastically reduce the number of UIPickerView rows (in numberOfRowsInComponent)
  • use titleForRow instead of viewForRow for UITest running

The last one (using titleForRow) was a complete non starter since I rely on heavy UIPickerView visual customization. Generating screenshots with incorrect picker views defeats the whole point of the exercise.

I tried using the Xcode’s UITest recorder, but I ran into many issues. One glaring issue is that while recording, I was able to swipe the UIPicker up, but when I played it back, it ended up swiping up the Control Center (instead of adjusting the UIPicker). There is a method (adjustToPickerWheelValue), but I found that it only works with titleForRow (which I don’t use). What I would like is an expansion of the XCUIElement API to add a simple increment/move up or down once.

My final solution (aka work around) was to use a combination of Fastlane launch arguments & brute forcing the UIView (via UIViewController viewDidAppear) to generate my screenshots. My work around isn’t ideal, but it gets the job done.

In my Fastlane Snapfile, I was able to define arguments:

launch_arguments([
 "-screenshot 1",
 "-screenshot 2",
 "-screenshot 3",
 "-screenshot 4",
 "-screenshot 5"
])

In my ViewController (running Swift 3), I was able to handle them accordingly:

let screenshot = UserDefaults.standard.string(forKey: "screenshot")
if screenshot == "1" {
    // do something
} else if screenshot == "2" {
    // do something
} else if screenshot == "3" {
    // do something
} else if screenshot == "4" {
    // do something
} else if screenshot == "5" {
    // do something
}

Once everything is setup, generating screenshots was simply running snapshot on the command line.

I’m sure there’s room for improvement in the code (using an enum, etc.), but I left it at that since it’s only for screenshot generation.

If you’ve made it all the way down here, thanks for reading. I just wanted to share my experience with UITest and UIPickers. UITest probably needs more love from Apple as it was not pleasant to work with.