How to automatically take Screenshots with Fastlane Snapshot

Grabbing screenshots for store display can become an exhausting work. Clicking button X to open screen B and take a screenshot, or input a username & password then wait for the login process or taking an exact screen which deeply nested. Multiply it by how many localisation that’s supported. Also, don’t forget the revision or new design for next month. Now if you didn’t automate your way to do all this, then you’re doing it wrong.

Introducing Fastlane Snapshot (for iOS & tvOS), tools for automating all this hard work for taking app snapshots.


To install Fastlane, you need to have Ruby installed first (which is installed by default in MacOS) and then run the command

gem install fastlane

This will install all the Fastlane tools including Snapshot and Screengrab.

iOS Project Setup

For this tutorial, we’ll be using project that we’ve created. You can git clone it here or download the zip file here. This app contains 2 screen with en-US & fr-FR localisation.
To start with Fastlane snapshot, open your terminal and type

fastlane snapshot init

This command will add 2 files, SnapshotHelper.swift is a class that contains helper methods to get snapshots. Snapfile is a configuration file for Fastlane Snapshot.

Now open the app project and add SnapshotHelper.swift inside HelloAppUITests folder. Next, open HelloAppUITests.swift and remove everything inside setUp method except super.setUp(), then add this lines to setup Snapshot

let app = XCUIApplication()

Next, we will add Localization.strings and a helper method to get Localisation strings inside UI Tests. Click on HelloApp project, head to HelloAppUITests target. Go to Build Phase, expand Copy Bundle Resources and press + button then choose Localizable.strings.

Now open HelloAppUITests.swift and add this method :

func localizedString(key: String) -> String {
    let localizationBundle = Bundle(for: HelloAppUITests.self)        

    // handle "en-US" localisation
    if let path = localizationBundle.path(forResource: deviceLanguage, ofType: "lproj") {
        let deviceBundle = Bundle(path: path)
        let result = NSLocalizedString(key, bundle: deviceBundle!, comment: "")
        return result

    // handle "Base.lproj" localisation
    if let path = localizationBundle.path(forResource: "Base", ofType: "lproj") {
        let deviceBundle = Bundle(path: path)
        let result = NSLocalizedString(key, bundle: deviceBundle!, comment: "")
        return result

    // handle "en" localisation
    if let path = localizationBundle.path(forResource: NSLocale.current.languageCode, ofType: "lproj") {
        let deviceBundle = Bundle(path: path)
        let result = NSLocalizedString(key, bundle: deviceBundle!, comment: "")
        return result
    return "?"

This method helps us to locate the Localizable strings inside UI Test and return the localised string from the defined key. Now move your cursor inside testExample(), this will activate the Record button on the bottom left.

Next, Click that button (this will build and run your app on emulator). Then click on “Continue” and in Product Detail page, click on “ADD TO CART“, and press Stop Record button. Above actions will add UI test scenarios inside testExample().

For our Snapshot localisation to work, we need to change the hardcoded strings into its localised version. On the “Continue” change it into

localizedString(key: "Continue")

And on the “Add To Cart” change it into

localizedString(key: "Add To Cart").uppercased()

Next, we will add codes to take & save our snapshots. Modify the code inside testExample() to be like this :

let app = XCUIApplication()
app.buttons[localizedString(key: "Continue")].tap()
app.buttons[localizedString(key: "Add to cart").uppercased()].tap()
snapshot("03Alert", timeWaitingForIdle: 2)

snapshot(“FileName”) is to take a snapshot and then save it with “FileName.png”. For our alert after tapping “Add to card”, because there’s a 2 seconds delay before alert is shown, we use snapshot(“FileName”, timeWaitingForIdle: n_seconds)

Next, open Snapfile with your text editor. As you can see, there are configurations for device types to be used, languages and others. For optimal results, we want iPhone 8 Plus and iPhone X. For other iPhone sizes, AppStore Connect will resize the screenshots from iPhone 8 Plus and even though iPhone X screenshots is optional, it’s nice to have those, especially with snapshot automation. Next for the language, we will add “en-US” and “fr-FR”. Then we set clear_previous_screenshots to true because we don’t need older screenshots. The final setting for Snapfile is like this :

    "iPhone 8 Plus",
    "iPhone X"



Now open the Terminal and type :

fastlane snapshot

This command will build your app, run it inside the simulators matching the devices setting, run your UI test, take and save snapshots. After done, you should see these results.

Fastlane also generates a nice html page containing all your screenshots for selected devices & localisations.

Flutter 101: How to create beautiful Android and iOS apps with Android Studio.

Wouldn’t it be awesome if you could create an amazingly beautiful app that looks exactly like it should both on iOS and Android? I mean, not exactly the same but really close. What if I tell you that you can do that with the same code base?

Yes, it is possible 🙂

Say hello to Flutter. One code base. Beautiful apps.

Flutter provides out of the box support of beautiful widgets, powerful programming language, smooth performance, official IDE support (you can even use Android Studio) and many others.


So let’s start playing with the framework. Install Flutter by following steps here based on your operating system. Tip: I recommend that you first install Android Studio if you don’t have it already, it will save you a lot of work of setting up Flutter, simulator, Android SDK and IDE support. * From this line forward, I assume that we will use Android Studio, but this also works with other IDE. Next, open your Android Studio, open Preferences and select Plugins. Click on Browse repositories… and search for Dart, install it but don’t restart Android Studio yet, then search again for Flutter, install it and restart Android Studio.

Now, with Flutter installed , when you open Android Studio, it should show Create New Flutter Project in File menu.

If you’re on a Mac, plug your iPhone or iPad and surprisingly, Android Studio can detect and show your iDevice and also iOS simulators, this is very nice.

Let’s create new Flutter application, and Android Studio will generate an example of a Flutter app with Material theme for you. Now select your device / simulator and click Run button. After Flutter build finished, this beautiful app will appear on your screen.

Main Dart file for Flutter app is usually located at lib/main.dart. When you explore it, you will realise how easy it is to create a Material page with Toolbar, Main Content and Floating Action Button. No XML needed, no CSS styling needed and no confusing configuration.

One of the main benefit of using Flutter is Hot Reload, it’s similar with Android Instant Run but more powerful so you can change your source code and after pressing save, the page will be reloaded. Let’s try by changing the Theme primary colour from Blue to Lime. Go to line 20 in main.dart and change this line Colors.Blue into Colors.Lime save by pressing CMD+S / CTRL+S then open your simulator / device. Voila, instant change. Tips: One of the benefit of using Android Studio is you can instantly see the color of Colors value on the left side.

Color indicator on the left side.
Instant changes with saved state (The button was pushed 5 times).

If you’re an Android developer you must be familiar with gradle as a package manager for Android project or Podfile / Cartfile if you’re an iOS developer. Flutter is the same, it has `pubspec.yaml` as a dependency manager. I wrote dependency manager not a package manager, because in pubspec can also contain assets for your file, whether it’s fonts, sounds and images. Flutter configuration also written inside pubspec.

Login Page

Next, why don’t we start with creating a common pattern of app. Login screen and Homepage with navigation drawer & content. Let’s start by creating login screen. We need username, password field and submit button.

Open main.dart and remove everything. Add import material lib then add the entry point for Flutter application.

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

Next, we create our MyApp class. Write this lines :

class MyApp extends StatelessWidget {
  // This widget is the root of your application.

Go inside class expression and press CTRL + I or via menu Code -> Implement Methods and choose build method. Android Studio will add the boilerplate code for you. So why extending StatelessWidget? It’s because there are no state changes inside MyApp widget. There will be more explanations and differences between StatelessWidget and StatefulWidget below.


Implement build method

Inside build method, add this line of codes.

return new MaterialApp(
   title: 'Flutter Demo',
   theme: new ThemeData(
       primarySwatch: Colors.lime,
   routes: {
    // "/": (context) => LoginRoute(),
    // "/home": (context) => HomeRoute(),
   initialRoute: "/",

If you notice from first boilerplate code when we create new Flutter project. There are differences in our code above. In the boilerplate, we add initial page (or route in Flutter) by using home . Here we add routes and the initial page is declared using initialRoute. Using routes will have an advantage on navigation.

Now we create the LoginRoute. Right click on lib, click on New -> Dart File and name it login_route.dart. Note: Dart convention is to name every file with lower_snake_case.dart. First we import Flutter material lib. Next, add these lines:

class LoginRoute extends StatefulWidget {
    State createState() => _LoginRouteState();

Note: There are two kind of Widget in Flutter. Stateless and Stateful. If your widget will be capturing user input like text field, dropdown, button you want your widget to be Stateful. When your widget will be doing network call, you also want your widget to be Stateful. The key point is when there are State that will be changed based on some event (user input, async network call, system event) extend your class from StatefulWidget. Parent class can be Stateless (if there are no state changes) even when the child widget is Stateful.

Note: In Dart, for a multi line expression body, you can use

(parameter) {

For one line expression body, you can shorten it into

(paremeter) => callMethod01(parameter);

Next, add these lines :

class _LoginRouteState extends State<LoginRoute> {
    Widget build(BuildContext context) {
        final username = Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
                decoration: InputDecoration(
                    labelText: "Username",
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(4.0),

        final password = Padding(
            padding: const EdgeInsets.all(16.0),
            child: TextField(
                obscureText: true,
                decoration: InputDecoration(
                    labelText: "Password",
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(4.0),

        final submitButton = Padding(
            padding: const EdgeInsets.all(16.0),
            child: Material(
                elevation: 5.0,
                shadowColor: Colors.lime.shade100,
                child: MaterialButton(
                    minWidth: 200.0,
                    height: 48.0,
                    child: Text(
                        "LOG IN",
                        style: TextStyle(color: Colors.white, fontSize: 16.0),
                    color: Colors.lime,
                    onPressed: () {
                        // Navigator.push(context, MaterialPageRoute(builder: (context) => HomeRoute()));

        return Scaffold(
            backgroundColor: Colors.white,
            body: Center(
                child: ListView(
                    children: [username, password, submitButton],

If we’re using StatefulWidget, we must define State class, which has build method inside. Inside this build method define how we will render the components and this will be re-rendered when a state change by calling .setState(). You don’t need to worry about how often the rendering happens and will it has any impact on performance, the Flutter rendered is smart enough to render only the changes.

First, we create the username TextField and add a decoration to style the look of the TextField. Next we create the password TextField with obscureText property set to true. In Flutter , to add space (padding) and to position an element we wrap that element to be a child of Padding or Center (to Center an element), Column (like LinearLayout with Vertical orientation / UIStackView with vertical orientation) or Row (like LinearLayout with Horizontal orientation / UIStackView with horizontal orientation).

Tip: when you’re using Android Studio. After you add the element and want to wrap it inside Padding / Center / Column / Row, write your element first, then press Alt + Enter to show action. Then you can choose what dimension / container for wrapper.

Quick actions


Column quick action result.

Next, we add Submit button. We use MaterialButton to add a button with material look and then we set the width, height, text (by setting child property and fill it with Text), color, and so on. To add more Material feels, we wrap that button inside Material widget to set it’s elevation and shadow color.

Lastly, we stitch all the widgets above by using Scaffold. Scaffold is like a skeleton for app with Material theme. It can consist of AppBar, Body, Floating Action Button, Drawer, Right Drawer and Bottom navigation bar. For our login, we only need the body part, add List View (or Scroll View, but I prefer List View because we can position all items directly without a need to wrap it inside a Column) then fill the children of ListView with our components above.

Back to main.dart. Uncomment these line :

"/": (context) => LoginRoute(),

Then, below of our material, we import our login_route file.

import 'package:flutter_app_as/login_route.dart';

Because Flutter plugin for Android Studio haven’t yet implemented auto import, we need to write the import manually. But don’t worry, the autocomplete is still available. Now Save the project or Run it if you haven’t already run it in simulator / device.  You should see a login screen with beautiful UI and nice animation when you clicked on username / password field.


Login page

Home Page

Next, we will create a home page and then connect the login page to home page when we click on LOG IN button. First, create new home_route.dart and import material.dart library. Then add this line :

class HomeRoute extends StatelessWidget {

We extend this class from StatelessWidget because this class doesn’t have any state change and without user input. Note: you may need to extend it from StatefulWidget later when you want the drawer to be fully functional (e.g clicking on menu will change the content of the body). Next, implement the method from StatelessWidget by pressing CTRL+I.

class HomeRoute extends StatelessWidget {
    Widget build(BuildContext context) {

Add these lines inside build method :

return Scaffold(
    appBar: AppBar(
        title: Text("Welcome"),
    drawer: Drawer(
        child: Column(
            children: [
                    accountName: Text("John Doe"),
                    accountEmail: Text(""),
    body: Center(
        child: Text("Hello"),

We return a Scaffold with Appbar, Drawer and Body. For Appbar we set the title to be a Text with label “Welcome” Inside the Drawer we add a column with UserAccountsDrawerHeader on top and menus  on the bottom of it. This menus will be navigation menus that user can tap on it. Now let’s create this menus. Before return Scaffold line, add these lines :

final menus = Column(
    children: <Widget>[
            title: Text("Logout"),
            onTap: () {
                Navigator.popUntil(context, ModalRoute.withName("/"));

First, we add Column because we want the elements to be placed in vertical order (top-down). Column or Row need an array of widgets for its children. Next we add ListTile as the children. You may be wondering, Why ListTile, why not just Text ?. This is because by using ListTile, we can easily add onTap event on its child, which in this case is Text.

Inside onTap() we add Navigator.popUntil that will pop the navigator stack until it finds a route with name “/”. If you remember, this route name have already defined inside main.dart. Back to main.dart and uncomment this line :

"/home": (context) => HomeRoute(),

Then we import home_route.dart

import 'package:flutter_app_as/home_route.dart';

Next, open login_route.dart and uncomment this line :

Navigator.push(context, MaterialPageRoute(builder: (context) => HomeRoute()));

Because we also refer to HomeRoute() then we need to also import home_route.dart inside login_route. Now save the the file to hot reload the app. Now when you log in app will navigate to Home route and when you tap on Logout menu app will go back to login screen.

Floating Action Button

Now let’s see if hot reload works inside a nested route. Navigate your app to Home page (by clicking login button). Open home_route.dart and after body (inside Scaffold), add a Floating action button.

floatingActionButton: FloatingActionButton(
   child: new Icon(Icons.ac_unit),
   onPressed: (){},

Press save, and our home route will be hot reloaded and we should see the fab.

Home page with fab.

Image Asset

Another nice thing about Flutter that you saw on code above is it’s shipped with material Icons and Colors, this will really help on creating a fast prototype. Next, we will add an image on top of Username that will be our company logo. First, download this image : then move this image to /assets folder.

Then we add this image inside pubspec, so this image will be shipped inside our Flutter app.

Define our image asset.

Next, open login_route.dart and add this image asset with this line :

final icon = Image.asset(

Inside the ListView children, add icon before username, so it will be like this :

    children: [icon, username, password, submitButton],

Save and you should now see our image asset on top of username field.


From this tutorial, we can see how easy it is to create a beautiful app like this, with only 3 files. Even though Flutter hasn’t yet release 1.0 yet (when this blog was written the latest version is Release Preview 1) but so far it’s stable and some apps from big developer team has already used it in production like Hamilton : The Musical App (iOS & Android) and Hookle (iOS & Android).

You can clone full source code here :

Do you have any questions or comments?

Happy to get your feedback at the comments below.

IBM case study

“For us, the combination of “shake to report”, the integration with JIRA and the video recordings really was a WOW factor”– Marcelo Juliano Ramos, IBM Watson automation specialist.



Marcelo is an a Watson automation specialist based in the IBM office in Brazil.

IBM Watson is an artificial intelligence technology, based on cognitive computing that understands natural language and is capable of answering questions posed in multiple languages.

As part of his role in IBM, Marcelo works with Volkswagen on an in-house innovative new project involving Watson technology for the new Volkswagen Virtus.
This new car model will offer a cognitive manual, which utilizes IBM Watson to respond to drivers’ questions about the vehicle, including information from the car manual. The app understands natural, informal speaking and “learns” more based on the interaction with the driver. The driver can either speak, write or take photos to interact with the app. Any “how to” questions about the car can be answered by this smart assistant, simply and quickly, in multiple languages. This solution allows a new way of interacting with the vehicle and offers a new technological experience to driving.


TestFairy is a mobile testing platform that enables teams to find, report and solve complex problems with their mobile apps, all from one dashboard. The platform includes an enterprise  app-distribution solution for iOS and Android apps, and provides an SDK that collects valuable information such as videos showing user behavior, app logs and crash reports. This information is collected and presented in an organized way, in a single, easy-to-use dashboard helping teams to quickly reproduce and understand problems, allowing for accurate and timely bug fixes.


“Users simply did not report on all problems because the manual process they had for reporting feedback was tedious and unfriendly”

As the Volkswagen Virtus project has many features and complex capabilities, and testing is done both in the lab and in the field for cars, the IBM and Volkswagen teams needed an organized way to manage testing and ensure the app is as quick and easy to use as the vehicle it accompanies. The app was to undergo testing by personnel from both companies, and so the teams needed to make sure they can both access and share the information collected throughout the process.

Reflecting on past experience working on an HR-focused app within IBM, Marcelo said their biggest problem was receiving enough useful feedback and managing this amount of data in a manner that allows the team to track problems, reproduce and fix them on time.
Users simply did not report on all problems because the manual process they had for reporting feedback was tedious and unfriendly, requiring a user testing the app to decide to take screenshots, attach them to an email and explain the situation.
As colleagues admitted to the team, it often put them off of reporting some issues, as the process required to much effort on their part. In addition, the received reports were not always useful, as sometimes the recordings may be partial or the information provided inaccurate or insufficient. This lead the team, on many occasions, to rework and waste time on recreating issues that didn’t have enough information provided on them.

Finally, a lot of data collected from the testing was eventually lost because there was no organized method of storing and using it in retrospect.As two teams are testing the app in its development process, they needed a way to efficiently manage testing throughout the development cycle and in transition from the IT development at IBM to a more user experience oriented approach at Volkswagen.


“I no longer have to wait for users to tell me what they’ve experienced. Now I can see it for myself”

After the trying experience of unorganized testing, Marcelo’s team started using TestFairy, and that made all the difference. The fact that all sessions are automatically recorded, and contain all the relevant data the development team may need to solved their biggest problem.

What actually made the testers hop on board was how incredibly easy the reporting process has become. All they had to do is shake their phone, and a bug report with a full recording and attached screenshots, logs and metrics was automatically created and sent.
For Marcelo’s team, the fact that those reports automatically open up issues on their JIRA ensured that no more bugs are missed, and all the relevant data for fixing them is available right there on that ticket. For issues users do choose to report, having all the session recordings at hand enables the team to check if other users experienced them as well.

Now, with more reports flowing in with comprehensive and accurate information, the team is able to quickly reproduce and fix problems, as well as fully understand the actual user experience, all before releasing the app.


Testing and getting feedback during the development process of an app is no easy task in any company or setting. The task is even harder when it involves a complex app and two developing companies. By using TestFairy, the IBM team is able to streamline this process and effectively run testing on their app, get more reports thanks to the super easy feedback it enables, and have urgent issues promptly sent to their JIRA. No more bugs missed, no more waiting for users – the teams can now watch the entire experience’s recordings from one dashboard.

Thank you London, this week was awesome!

We are back from a great week in London, full of excitement, excellent new leads, and probably too much beer 🙂 We started with 2 days in Apps world, probably the biggest apps event in Europe. The event attaracted more than 15,000 people, and for some moment it felt like they all came to the event especially to hang out in our booth!

Apps World’s highlight was definitely Steve Wozniak keynote, and guess who used his exhibitor’s pass to get in 10 minutes before the opening and catch a 3rd row seat just to get a picture of his childhood hero!

And right after Apps World, when we thought that it can’t get any better than this, came DroidCon, an Android developers only event where the atmosphere was excellent, the people were extremely friendly and the beer was free. As you can see in these pictures, our puzzle giveaway was a big hit.

Our presentation at DroidCon got really nice feedback, as you can see in these tweets:

Not sure if I mentioned it already, but that was a fun week!
Here are some pictures, here is the full album

GeekTime Conference

Had a wonderful time presenting TetFairy at the GeekTime conference. We are in the 10 lucky startups that made it to the finals, and although we didn’t take the trophy, that was one of the most exciting days we have ever had 🙂

Picture by Alan Weinkrantz