snapshot

Alias for the capture_ios_screenshots action

Automate taking localized screenshots of your iOS, tvOS, and watchOS apps on every device

Check out the new fastlane documentation on how to generate screenshots


snapshot generates localized iOS, tvOS, and watchOS screenshots for different device types and languages for the App Store and can be uploaded using (deliver).

You have to manually create 20 (languages) x 6 (devices) x 5 (screenshots) = 600 screenshots.

It's hard to get everything right!

  • New screenshots with every (design) update
  • No loading indicators
  • Same content / screens
  • Clean Status Bar
  • Uploading screenshots (deliver is your friend)

More information about creating perfect screenshots.

snapshot runs completely in the background - you can do something else, while your computer takes the screenshots for you.


FeaturesUI TestsQuick StartUsageTipsHow?


Features

  • Create hundreds of screenshots in multiple languages on all simulators
  • Take screenshots in multiple device simulators concurrently to cut down execution time (Xcode 9 only)
  • Configure it once, store the configuration in git
  • Do something else, while the computer takes the screenshots for you
  • Integrates with fastlane and deliver
  • Generates a beautiful web page, which shows all screenshots on all devices. This is perfect to send to QA or the marketing team
  • snapshot automatically waits for network requests to be finished before taking a screenshot (we don't want loading images in the App Store screenshots)

After snapshot successfully created new screenshots, it will generate a beautiful HTML file to get a quick overview of all screens:

img/actions/htmlPagePreviewFade.jpg

Why?

This tool automatically switches the language and device type and runs UI Tests for every combination.

Why should I automate this process?

  • It takes hours to take screenshots
  • You get a great overview of all your screens, running on all available simulators without the need to manually start it hundreds of times
  • Easy verification for translators (without an iDevice) that translations do make sense in real App context
  • Easy verification that localizations fit into labels on all screen dimensions
  • It is an integration test: You can test for UI elements and other things inside your scripts
  • Be so nice, and provide new screenshots with every App Store update. Your customers deserve it
  • You realize, there is a spelling mistake in one of the screens? Well, just correct it and re-run the script

UI Tests

Getting started

This project uses Apple's newly announced UI Tests. We will not go into detail on how to write scripts.

Here a few links to get started:

Quick Start

  • Create a new UI Test target in your Xcode project (top part of this article)
  • Run fastlane snapshot init in your project folder
  • Add the ./SnapshotHelper.swift to your UI Test target (You can move the file anywhere you want)
    • (Xcode 8 only) add the ./SnapshotHelperXcode8.swift to your UI Test target
  • (Objective C only) add the bridging header to your test class:
    • #import "MYUITests-Swift.h"
      (The bridging header is named after your test target with -Swift.h appended.)
  • In your UI Test class, click the Record button on the bottom left and record your interaction
  • To take a snapshot, call the following between interactions
    • Swift: snapshot("01LoginScreen")
    • Objective C: [Snapshot snapshot:@"01LoginScreen" timeWaitingForIdle:10];
  • Add the following code to your setUp() method:

Swift:

let app = XCUIApplication()
setupSnapshot(app)
app.launch()

Objective C:

XCUIApplication *app = [[XCUIApplication alloc] init];
[Snapshot setupSnapshot:app waitForAnimations:NO];
[app launch];

Make sure you only have one launch call in your test class, as Xcode adds one automatically on new test files.

img/actions/snapshot.gif

You can try the snapshot example project by cloning this repo.

To quick start your UI tests, you can use the UI Test recorder. You only have to interact with the simulator, and Xcode will generate the UI Test code for you. You can find the red record button on the bottom of the screen (more information in this blog post)

Usage

fastlane snapshot

Your screenshots will be stored in the ./screenshots/ folder by default (or ./fastlane/screenshots if you're using fastlane)

New with Xcode 9, snapshot can run multiple simulators concurrently. This is the default behavior in order to take your screenshots as quickly as possible. This can be disabled to run each device, one at a time, by setting the :concurrent_simulators option to false.

Note: While running snapshot with Xcode 9, the simulators will not be visibly spawned. So, while you won't see the simulators running your tests, they will, in fact, be taking your screenshots.

If any error occurs while running the snapshot script on a device, that device will not have any screenshots, and snapshot will continue with the next device or language. To stop the flow after the first error, run

fastlane snapshot --stop_after_first_error

Also by default, snapshot will open the HTML after all is done. This can be skipped with the following command

fastlane snapshot --stop_after_first_error --skip_open_summary

There are a lot of options available that define how to build your app, for example

fastlane snapshot --scheme "UITests" --configuration "Release"  --sdk "iphonesimulator"

Reinstall the app before running snapshot

fastlane snapshot --reinstall_app --app_identifier "tools.fastlane.app"

By default snapshot automatically retries running UI Tests if they fail. This is due to randomly failing UI Tests (e.g. #2517). You can adapt this number using

fastlane snapshot --number_of_retries 3

Add photos and/or videos to the simulator before running snapshot

fastlane snapshot --add_photos MyTestApp/Assets/demo.jpg --add_videos MyTestApp/Assets/demo.mp4

For a list for all available options run

fastlane action snapshot

After running snapshot you will get a nice summary:

Snapfile

All of the available options can also be stored in a configuration file called the Snapfile. Since most values will not change often for your project, it is recommended to store them there:

First make sure to have a Snapfile (you get it for free when running fastlane snapshot init)

The Snapfile can contain all the options that are also available on fastlane action snapshot

scheme("UITests")

devices([
  "iPad (7th generation)",
  "iPad Air (3rd generation)",
  "iPad Pro (11-inch)",
  "iPad Pro (12.9-inch) (3rd generation)",
  "iPad Pro (9.7-inch)",
  "iPhone 11",
  "iPhone 11 Pro",
  "iPhone 11 Pro Max",
  "iPhone 8",
  "iPhone 8 Plus"
])

languages([
  "en-US",
  "de-DE",
  "es-ES",
  ["pt", "pt_BR"] # Portuguese with Brazilian locale
])

launch_arguments(["-username Felix"])

# The directory in which the screenshots should be stored
output_directory('./screenshots')

clear_previous_screenshots(true)

override_status_bar(true)

add_photos(["MyTestApp/Assets/demo.jpg"])

Completely reset all simulators

You can run this command in the terminal to delete and re-create all iOS and tvOS simulators:

fastlane snapshot reset_simulators

Warning: This will delete all your simulators and replace by new ones! This is useful, if you run into weird problems when running snapshot.

You can use the environment variable SNAPSHOT_FORCE_DELETE to stop asking for confirmation before deleting.

SNAPSHOT_FORCE_DELETE=1 fastlane snapshot reset_simulators

Update snapshot helpers

Some updates require the helper files to be updated. snapshot will automatically warn you and tell you how to update.

Basically you can run

fastlane snapshot update

to update your SnapshotHelper.swift files. In case you modified your SnapshotHelper.swift and want to manually update the file, check out SnapshotHelper.swift.

Launch Arguments

You can provide additional arguments to your app on launch. These strings will be available in your app (e.g. not in the testing target) through ProcessInfo.processInfo.arguments. Alternatively, use user-default syntax (-key value) and they will be available as key-value pairs in UserDefaults.standard.

launch_arguments([
  "-firstName Felix -lastName Krause"
])
name.text = UserDefaults.standard.string(forKey: "firstName")
// name.text = "Felix"

snapshot includes -FASTLANE_SNAPSHOT YES, which will set a temporary user default for the key FASTLANE_SNAPSHOT, you may use this to detect when the app is run by snapshot.

if UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT") {
    // runtime check that we are in snapshot mode
}

Specify multiple argument strings and snapshot will generate screenshots for each combination of arguments, devices, and languages. This is useful for comparing the same screenshots with different feature flags, dynamic text sizes, and different data sets.

# Snapfile for A/B Test Comparison
launch_arguments([
  "-secretFeatureEnabled YES",
  "-secretFeatureEnabled NO"
])

Xcode Environment Variables

snapshot includes FASTLANE_SNAPSHOT=YES and FASTLANE_LANGUAGE=<language> as arguments when executing xcodebuild. This means you may use these environment variables in a custom build phase run script to do any additional configuration.

How does it work?

The easiest solution would be to just render the UIWindow into a file. That's not possible because UI Tests don't run on a main thread. So snapshot uses a different approach:

When you run unit tests in Xcode, the reporter generates a plist file, documenting all events that occurred during the tests (More Information). Additionally, Xcode generates screenshots before, during and after each of these events. There is no way to manually trigger a screenshot event. The screenshots and the plist files are stored in the DerivedData directory, which snapshot stores in a temporary folder.

When the user calls snapshot(...) in the UI Tests (Swift or Objective C) the script actually does a rotation to .Unknown which doesn't have any effect on the actual app, but is enough to trigger a screenshot. It has no effect to the application and is not something you would do in your tests. The goal was to find some event that a user would never trigger, so that we know it's from snapshot. On tvOS, there is no orientation so we ask for a count of app views with type "Browser" (which should never exist on tvOS).

snapshot then iterates through all test events and check where we either did this weird rotation (on iOS) or searched for browsers (on tvOS). Once snapshot has all events triggered by snapshot it collects a ordered list of all the file names of the actual screenshots of the application.

In the test output, the Swift snapshot function will print out something like this

snapshot: [some random text here]

snapshot finds all these entries using a regex. The number of snapshot outputs in the terminal and the number of snapshot events in the plist file should be the same. Knowing that, snapshot automatically matches these 2 lists to identify the name of each of these screenshots. They are then copied over to the output directory and separated by language and device.

2 thing have to be passed on from snapshot to the xcodebuild command line tool:

  • The device type is passed via the destination parameter of the xcodebuild parameter
  • The language is passed via a temporary file which is written by snapshot before running the tests and read by the UI Tests when launching the application

If you find a better way to do any of this, please submit an issue on GitHub or even a pull request :+1:

Radar 23062925 has been resolved with Xcode 8.3, so it's now possible to actually take screenshots from the simulator. We'll keep using the old approach for now, since many of you still want to use older versions of Xcode.

Tips


Check out the new fastlane documentation on how to generate screenshots


Frame the screenshots

If you want to add frames around the screenshots and even put a title on top, check out frameit.

Available language codes

ALL_LANGUAGES = ["da", "de-DE", "el", "en-AU", "en-CA", "en-GB", "en-US", "es-ES", "es-MX", "fi", "fr-CA", "fr-FR", "id", "it", "ja", "ko", "ms", "nl-NL", "no", "pt-BR", "pt-PT", "ru", "sv", "th", "tr", "vi", "zh-Hans", "zh-Hant"]

To get more information about language and locale codes please read Internationalization and Localization Guide.

Use a clean status bar

You can set override_status_bar to true to set the status bar to Tuesday January 9th at 9:41AM with full battery and reception. If you need more granular customization, to set a Carrier name for example, also set override_status_bar_arguments to the specific arguments to be passed to the xcrun simctl status_bar override command. Run xcrun simctl status_bar --help to see the options available.

Editing the Snapfile

Change syntax highlighting to Ruby.

Simulator doesn't launch the application

When the app dies directly after the application is launched there might be 2 problems

  • The simulator is somehow in a broken state and you need to re-create it. You can use snapshot reset_simulators to reset all simulators (this will remove all installed apps)
  • A restart helps very often

Determine language

To detect the currently used localization in your tests, access the deviceLanguage variable from SnapshotHelper.swift.

Speed up snapshots

A lot of time in UI tests is spent waiting for animations.

You can disable UIView animations in your app to make the tests faster:

if ProcessInfo().arguments.contains("SKIP_ANIMATIONS") {
    UIView.setAnimationsEnabled(false)
}

This requires you to pass the launch argument like so:

snapshot(launch_arguments: ["SKIP_ANIMATIONS"])

By default, snapshot will wait for a short time for the animations to finish. If you're skipping the animations, this wait time is unnecessary and can be skipped:

setupSnapshot(app, waitForAnimations: false)

snapshot
Supported platforms ios, mac
Author @KrauseFx

3 Examples

capture_ios_screenshots
snapshot # alias for "capture_ios_screenshots"
capture_ios_screenshots(
  skip_open_summary: true,
  clean: true
)

Parameters

Key Description Default
workspace Path to the workspace file
project Path to the project file
xcargs Pass additional arguments to xcodebuild for the test phase. Be sure to quote the setting names and values e.g. OTHER_LDFLAGS="-ObjC -lstdc++"
xcconfig Use an extra XCCONFIG file to build your app
devices A list of devices you want to take the screenshots from
languages A list of languages which should be used ["en-US"]
launch_arguments A list of launch arguments which should be used [""]
output_directory The directory where to store the screenshots *
output_simulator_logs If the logs generated by the app (e.g. using NSLog, perror, etc.) in the Simulator should be written to the output_directory false
ios_version By default, the latest version should be used automatically. If you want to change it, do it here
skip_open_summary Don't open the HTML summary after running snapshot false
skip_helper_version_check Do not check for most recent SnapshotHelper code false
clear_previous_screenshots Enabling this option will automatically clear previously generated screenshots before running snapshot false
reinstall_app Enabling this option will automatically uninstall the application before running it false
erase_simulator Enabling this option will automatically erase the simulator before running the application false
headless Enabling this option will prevent displaying the simulator window true
override_status_bar Enabling this option will automatically override the status bar to show 9:41 AM, full battery, and full reception (Adjust 'SNAPSHOT_SIMULATOR_WAIT_FOR_BOOT_TIMEOUT' environment variable if override status bar is not working. Might be because simulator is not fully booted. Defaults to 10 seconds) false
override_status_bar_arguments Fully customize the status bar by setting each option here. Requires override_status_bar to be set to true. See xcrun simctl status_bar --help
localize_simulator Enabling this option will configure the Simulator's system language false
dark_mode Enabling this option will configure the Simulator to be in dark mode (false for light, true for dark)
app_identifier The bundle identifier of the app to uninstall (only needed when enabling reinstall_app) *
add_photos A list of photos that should be added to the simulator before running the application
add_videos A list of videos that should be added to the simulator before running the application
html_template A path to screenshots.html template
buildlog_path The directory where to store the build log *
clean Should the project be cleaned before building it? false
test_without_building Test without building, requires a derived data path
configuration The configuration to use when building the app. Defaults to 'Release' *
sdk The SDK that should be used for building the application
scheme The scheme you want to use, this must be the scheme for the UI Tests
number_of_retries The number of times a test can fail before snapshot should stop retrying 1
stop_after_first_error Should snapshot stop immediately after the tests completely failed on one device? false
derived_data_path The directory where build products and other derived data will go
result_bundle Should an Xcode result bundle be generated in the output directory false
test_target_name The name of the target you want to test (if you desire to override the Target Application from Xcode)
namespace_log_files Separate the log files per device and per language
concurrent_simulators Take snapshots on multiple simulators concurrently. Note: This option is only applicable when running against Xcode 9 true
disable_slide_to_type Disable the simulator from showing the 'Slide to type' prompt false
cloned_source_packages_path Sets a custom path for Swift Package Manager dependencies
skip_package_dependencies_resolution Skips resolution of Swift Package Manager dependencies false
disable_package_automatic_updates Prevents packages from automatically being resolved to versions other than those recorded in the Package.resolved file false
testplan The testplan associated with the scheme that should be used for testing
only_testing Array of strings matching Test Bundle/Test Suite/Test Cases to run
skip_testing Array of strings matching Test Bundle/Test Suite/Test Cases to skip
xcodebuild_formatter xcodebuild formatter to use (ex: 'xcbeautify', 'xcbeautify --quieter', 'xcpretty', 'xcpretty -test'). Use empty string (ex: '') to disable any formatter (More information: https://docs.fastlane.tools/best-practices/xcodebuild-formatters/) *
xcpretty_args DEPRECATED! Use xcodebuild_formatter: '' instead - Additional xcpretty arguments
disable_xcpretty Disable xcpretty formatting of build
suppress_xcode_output Suppress the output of xcodebuild to stdout. Output is still saved in buildlog_path
use_system_scm Lets xcodebuild use system's scm configuration false

* = default value is dependent on the user's system


Lane Variables

Actions can communicate with each other using a shared hash lane_context, that can be accessed in other actions, plugins or your lanes: lane_context[SharedValues:XYZ]. The snapshot action generates the following Lane Variables:

SharedValue Description
SharedValues::SNAPSHOT_SCREENSHOTS_PATH The path to the screenshots

To get more information check the Lanes documentation.


Documentation

To show the documentation in your terminal, run

fastlane action snapshot

CLI

It is recommended to add the above action into your Fastfile, however sometimes you might want to run one-offs. To do so, you can run the following command from your terminal

fastlane run snapshot

To pass parameters, make use of the : symbol, for example

fastlane run snapshot parameter1:"value1" parameter2:"value2"

It's important to note that the CLI supports primitive types like integers, floats, booleans, and strings. Arrays can be passed as a comma delimited string (e.g. param:"1,2,3"). Hashes are not currently supported.

It is recommended to add all fastlane actions you use to your Fastfile.


Source code

This action, just like the rest of fastlane, is fully open source, view the source code on GitHub


Back to actions