Hardcoding Resources in Automated Screenshots

Posted by Lieven on Thursday, October 29th, 2020

I had been creating screenshots for SkiMaps manually for the past 10 years, so when I added iPad support, it was long overdue to automate the process.

Automation

I used fastlane for generating and delivering screenshots based on UI tests.

The documentation is pretty clear, but SkiMaps is using live data, so to avoid the risk of showing a completely dark or misty webcam, or an empty list of activities, I had to hardcode some data in the app.

Debug-only resources

To include the image in the app bundle when generating the screenshots, but not in release builds, you can add a per-configuration setting for "Excluded Source File Names":

Excluded Source File Names: Debug: leave blank, Release: *ForDebug*

Any sources or resources that have ForDebug in their file name will now only be included in Debug builds.

Detecting UI tests

In the UI test you can set an environment variable to communicate to the app that it's started by a UI test:

let app = XCUIApplication()
app.launchEnvironment = ["UI_TESTING": "1"]
setupSnapshot(app)
app.launch()

which can then be checked in the app:

extension UIDevice {
    static var isUITesting: Bool {
        return (ProcessInfo.processInfo.environment["UI_TESTING"] == "1")
    }
}

Putting it all together

So when you want to load e.g. an image that's hardcoded for creating screenshots but dynamic in production:

var webcamURL: URL? {
    #if DEBUG
    if UIDevice.isUITesting {
        return Bundle.main.url(forResource: "WebcamForDebug", withExtension: "jpg")
    }
    #endif
    
    return webcam.url
}

With this and other similar checks, it's always been sunny in Val Thorens 5 minutes ago:

Hardcoded webcam image in Skimaps on iPad Hardcoded webcam image in Skimaps on iPhone

Coda: UI Test Woes

Fastlane snapshot is great, but I did have to update the list of devices in the Snapfile to match the screen sizes currently supported in App Store Connect:

devices([
	"iPhone 11 Pro Max", # 6.5"
	"iPhone 11 Pro", # 5.8"
	"iPhone 8 Plus", # 5.5"
	"iPhone 8", # 4.7"
	"iPhone SE (1st generation)", # 4"
	"iPad Pro (12.9-inch) (3rd generation)", # 12.9"
	"iPad Pro (11-inch) (2nd generation)", # 11"
	"iPad Pro (12.9-inch) (2nd generation)", # 12.9" (rectangular)
	"iPad Pro (10.5-inch)", # 10.5"
	"iPad Pro (9.7-inch)", # 9.7""
])

Be sure you run the tests in Xcode using multiple simulators before you run fastlane snapshot, because the reason for failure can sometimes be hidden a bit from the error output.

But even when running the tests from Xcode, I ran into problems where the connection between the UI tests process and the app itself broke. Quitting the simulator and Xcode, resetting simulators,... nothing worked. The only thing that did help was rebooting my Mac. Functional high ground!