iconwrite Automate taking screenshots thanks to UIAutomation

August 10, 2011, 21:21

Taking screenshots is painful

Either by choice or simply because I failed to succeed there I decided to focus on countries other than the biggest market: the U.S.A. As a result, while never reaching the top #50 in U.S.A. some of my apps have ranked #1 in other countries including amongst others the relativiely big markets of France, UK, Germany, Spain, Japan and China. This has been a lucky move considering some ad networks (notably Admob and MobFox) tend to have higher eCPM for my apps in Europe.

To achieve this localization was an absolute requirement.

Once you use a streamlined system (such as iCanLocalize ) managing various languages isn't too hard, as long as your apps – like mine – do not hold too much text. If I need a new string, I'll just add it to my en.lproj strings file, upload it to iCanLocalize and pay only for the missing words, then in a few hours (or days) I'll download the resulting strings files for all languages.

Now the thing I hate the most with an app that has 10 languages is that if you change something visible you need to update all the screenshots. Shudder.

This includes two very painful steps:

  • A: change the language in your phone 10 times and pick every screenshot again with the Organizer and then give it a proper name by hand. You could also change the language 10 times in your simulator (slightly faster) but then you'd have to crop your shots and have to worry about gamma issues or any possible difference between simulator and device.

  • B: Upload each screenshot manually while insulting loudly the guys who designed iTunesConnect. A zip file with standardized names would be SO much faster... one can dream.

I am afraid I can't do much about B, so let's keep cursing about this. However, there's a solution to improve the speed for taking screenshots on device, making more cursing optional but still enjoyable.

Enter UIAutomation. Writing a simple script

As the name implies, UIAutomation let's you automate your apps. This is primarily aimed a running Unit Tests but who needs them, we have user reviews for that, right?

To use UIAutomation all you need to do is write a javascript file and run it together with your app in the Profiler.

Here's a simple example script, showcasing some of the stuff you can do. Here we'll take two screenshots on two different screens.

// Define the language for the current run (here, Japanese)
var forcelanguage  ='ja';

// Setup some variables for readability
var target  = UIATarget.localTarget();
var app     = target.frontMostApp();
var win      = app.mainWindow();

// Overwrite the language in NSUserDefaults
// Note that this will only take effect on next run.
// So you have to run this twice.
// Once to set the language, another to actually pick the screenshot.
app.setPreferencesValueForKey([forcelanguage], 'AppleLanguages');

// You can define your own settings of course, for example:
app.setPreferencesValueForKey("0.85", 'fakePercentageForShots');

// Store current language name for use in filenames
lang = target.frontMostApp().preferencesValueForKey("AppleLanguages")[0];

// Save screenshot for home screen
target.captureScreenWithName( lang+"_home");

// Pause for two seconds (screen capture takes a second !)

// Open settings screen by pressing a button
// (note that the button must have the xcode label 'settings' for this to work)
// you could also reference the button by its number (here: 0)

// Pause for two seconds (new screen needs some time to load and appear)

// Save screenshot for settings screen
target.captureScreenWithName( lang+"_settings");

// Pause for two seconds (screen capture takes a second !)

// Hit the back button to go back to home screen
// otherwise upon next run the app will still be on settings screen

Here's the downloadable autoshot.js with the code above. It has a bit more logging than the code above too.

Launching the script

Open your project In XCode, use the menu : Products -> Profile. The following screen will show up and you'll very wisely pick Automation, unless you have been speed-reading all along and you think this is about zombies.

Automation in Profiler

You can abort the first run immediately since you have to change the settings:

  • Select your .js script file and enable Run on Record.
  • Enable Continous logging at the bottom and point it to an empty folder. I named mine 'shots' and put it on my desktop.

Automation settings

Now press the Run button again and watch as your screenshots are saved:

execution log

Stuff to Remember:

  • taking screenshots just doesn't work from the simulator, so you have to run this on the real device.

  • the screenshots are saved in the format that you have defined as the default for XCode (I usually pick PNG)

  • this runs in a full blown javascript interpreter. You can create functions or whatever. I made this example all in one linear script so it could be easy to read and understand.

  • Overwriting the language in NSUserDefaults only affects the next run so for each session you have to define the language variable, run the script once and abort it (so it memorizes the language), then run it again to get the actual screenshots.

  • Don't forget that after doing this your app will NOT use the device's language anymore. I'd recommend deleting the app afterwards otherwise you might be puzzled next time you run it a week later and you totally forgot about this :-)

  • You can find how to reach each element in your app by calling this method which will dump the entire UI structure (at this particular moment)


Looping over all languages ? Not yet.

As you might have noticed, the sad part here is you still have to run the script once in every language. I am still looking for a solution to update the language without having stop and restart the application but so far I haven't found a way. This would maket the full process 100% automated. Any suggestions ?

Update: Taking screenshots with the simulator

XCode 4.5+ fixed the issue where scripts wouldn't be able to take shots on the simulator. However, an issue remains: when taking shots from the simulator the language forced from the script isn't taken into account.

If you need to take shots in the simulator, as happened to me with the iPhone 5 because I could own one, you can just add this in your main.m:

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

#ifdef DEBUG
    NSString* forceLanguage = @"fr";
    [[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:forceLanguage, nil] forKey:@"AppleLanguages"];

int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;

Obviously the #ifdef DEBUG is a good security measure to make sure this never ends up in the final release if you forget to comment it.

0 responses to this post

E-mail (will not be published)
rss Blog RSS Feed

rss Comments RSS Feed