Manbolo Blog

Manbolo Team Blog, creators of MeonArchives

Core Data and iCloud

tweet

From Drew McCormack, in Under the Sheets with iCloud and Core Data: The Basics:

I’ve spent the last 3-4 months integrating Core Data syncing over iCloud into Mental Case for Mac. (…)
To say it has been a challenge would be an understatement — it has probably been one of the hardest tasks I have ever undertaken for a Mac or iOS app.

Core Data looks scary.
iCloud can be intimidating.
Core Data syncing via iCloud looks scary and intimidating…

From jc.

iPad Keyboard Improvments

tweet

From Andy Ihnatko in Apple’s post-PC world:

Side-notes to Apple: if you were serious when you said that the iPad is a serious computer, you need to beef up the iPad’s keyboard support. The arrow keys should map to screen up/screen down in every scrolling text; I should be able to rotate between running apps by hitting Cmd-Tab, as I can in Mac OS; Pages should let me apply boldface and italics via keyboard shortcuts, and it shouldn’t ‘help’ me extend selected text the way it does when I’m selecting via touch.

I love my iPad and if you look at all the available text editor on iPad, it’s quite obvious that content generation (and obviously text content) is big on iPad. With iOS 6 coming, Apple must improve the keyboard support on iPad:

  1. By improving the hardware keyboard support: switching between apps from the hardware keyboard, improving text selection etc…
  2. By improving the build-in software keyboard: if you look at this video, you can see how the software keyboard could be used as a trackpad for quick text selection (Thanks to Daniel Hooper)

The ’Undo’ feature is also very very awkward on iPad: shake your iPad to undo some typo doesn’t work. I dislike this undo gesture on iPhone too by the way.

From jc.

Non Square App Store Icons

tweet

Have you ever noticed that App Store icons have a non square aspect ratio?

Non square App Store icon of Tiny Wings

Strange, isn’t it? On my iPhone 4S (iOS 5.1.1), App Store icons in app description are 118px by 114px. With Steve gone, is anybody at Apple looking for pixel perfection?

From jc.

No Silver Bullets

tweet

Shell apps vs NativeCourtesy of Benjamin Sandosfky - Shell Apps and Silver Bullets

From Benjamin Sandofsky, Shell Apps and Silver Bullets is a must read piece on why developing native is always better than developing “shell apps” (ie a thin native wrapper around a web app). This article is so so good that it’s hard to pick only one quote. Maybe this one is my favorite:

Shells apps promise to let you share a single implementation across several platforms. In practice, they trade several straightforward implementations for a single complex implementation.

Benjamin raises the following issues with shell app:

  1. The Framework Tax or how PhoneGap and others bridge framework will always be behind the native features.
  2. Browser Fragmentation HTML5 on Windows Phone is not the HTML5 on Android is not the HTML5 on iOS
  3. Versioning (maybe the least convincing bullet)
  4. The Uncanny Valley native apps are consistent to their platform.
  5. Performance I’m still waiting to see a web app that perfoms as good as a native app.

And the myths that explained why companies keep trying to build shell apps:

I would add a fourth: “It Will Cost Less”. At the end of the day, if you count the time you spend dealing with your open source framework, the browser fragmentation, the time you will spend to mimic the native OS etc… I’m sure that developing and maintaining one shell app that works flawless on iOS and Android is more expensive that developing and maintaining an iOS and an Android version.

Benjamin sums up so well what I’m thinking: if you want the best “app experience”, go to the native language/framework of your platform. And frankly, who doesn’t want to have the best product?

A last one:

Web technology is great for many things. Replicating a native app experience is not one of them.

So good article.

From jc.

How to Indicate What Devices Are Supported by Your iOS App

tweet

In Hypercritical n°64 “You Will Die Instantly!”, John Siracusa discusses about an iOS issue regarding app devices compatibility vs iTunes metadatas. The issue (raised in the previous show) was about a customer complaining that the game he had downloaded didn’t run on its iPhone 3G, whereas iTunes indicated that every devices (including the 3G) were supported. As the customer complained about it, the developer argued that the description of the app clearly stated that the iPhone 3G was not supported by this game, whereas the iTunes metadatas told the contrary. So, there seemed to be a difference between what iTunes said and what the app description said, and presumably the system should have avoided it.

One of John’s listener feedback was that this discrepancy (between iTunes metadatas and the app description) is not due to the developer, but is instead a lack of possibility, on the development side, to list devices on which an app is running.

Actually, it’s not exactly true, and I would like to explain the possibility you have, as a developer, to precisely target which devices your application is supporting.

1. Hardware capabilities

One of the most important resources of your app in Xcode is the Info.plist file. This file contains important key for you app, like the app bundle id, CFBundleIdentifier, which is the unique identifier of your application, the bundle display name, which is the name of your app on SpringBoard, the background mode you use etc…

Among these keys, the UIRequiredDeviceCapabilities key, while not mandatory, is crucial. This key allows you to list ’hardware-like’ capabilities that your app needs. Examples of what this key can contain are: microphone, gyroscope, video-camera, front-facing-camera etc.. I said ’hardware-like’ because some of these values are more software oriented (like gamekit aka Game Center, or even opengles-1) but the majority is hardware oriented.

UIRequiredDeviceCapabilities exampleExample of capablity requiring armv7 support

Playing with these capabilities, you can target which device your app is running on. In the iTunesConnect Developer Guide, there is even a matrix showing how you can combine the different values and the resulted supported devices.

iTunes devices matrix

By the way, do you know that there is an iPhone 3GS ’China flavor’ without wifi ?

A side note: when you will submit your app to the App Store, Apple will add an iTunesMetadata.plist file, containing various info about your app, and including the UIRequiredDeviceCapabilities, another proof of the importance of it. For instance, you could have:

<key>UIRequiredDeviceCapabilities</key>
<dict>
    <key>armv7</key>
    <true/>
    <key>opengles-2</key>
    <true/>
</dict>

2. Software capabilities

You can also adjust the minimum iOS required by your app. Once again, as the app developer, this is your responsibility, and if your set a minimum version to iOS 3.0, you must insure that your app is effectively running on this OS ( and test if APIs are here etc…, weak link frameworks etc.. more information here)

To set the minimum iOS version, simply select your target, go to ’Build Settings’, and set the iOS Deployment Target to what you want.

IOS Deployment target setting

That’s all. When you will submit your app to the App Store, Apple will add to your Info.plist file the following key:

<key>MinimumOSVersion</key>
<string>4.1</string>

to indicate what is the minimum iOS version for your app.

3. Summary and wrap up with Hypercritical

So, to make the link with the Hypercritical issue, the developer of the incriminated game should have added in the Info.plist file an armv7 capability requiring armv7 support. Simply doing this, his game would have been only available on iPhone 3GS and superior, and the iTunes metadata would have reflected it. In fact, the armv7 capability is even in the default plist file when you’re creating a new projet under Xcode 4.2 and superior (Yes, Apple is pushing you to armv7 devices!)

Theses capabilities are not exactly a list of devices but by playing with it, developers can indicate precisely on which devices their apps are running on. I like the Apple approach, and I find that Apple has done a pretty good job regarding how to specify device compability. UIRequiredDeviceCapabilities can be used to target hardware capabilities, and these capabilities will infer which devices you’re supporting. So you don’t have to know all iOS devices and their capabilities, but you need to understand what features (hardware or software) you’re requiring. Doing it intelligently, and the iTunes metadatas will match exactly the devices you’re supporting.

For instance, if you look at the Infinity Blade II iTunes page, you will see this:

Infinity Blade

Looking inside the Infinity Blade app bundle, you can check that the UIRequiredDeviceCapabilities are

<key>UIRequiredDeviceCapabilities</key>
<dict>
    <key>armv7</key>
    <true/>
    <key>opengles-2</key>
    <true/>
</dict>

and the iOS version

<key>MinimumOSVersion</key>
<string>4.0</string>

that matches exactly the iTunes description!

By the way, if you don’t know Hypercritical and the other 5by5 podcasts, stop reading me and subscribe to these gems!

From jc.

Notepad++ : Cannot Save Color Highlighting Modification

tweet

Notepad version : 5.9.3

The problem

In "Settings / Style configurator" menu, I want to modify a "colour style" :

Notepad++ Style Configurator

Some solutions

Modify default theme

Notepad++ Default theme

Hard modify specific theme configuration file

Notepad++ XML Theme

From Paul.

WWDC 2012 is Here!

tweet

Updated on 2012/04/25: Sold out in 2 hours!

GO GO GO !

Xcode 4 vs Me : Running Custom Scripts after Build

tweet

The easy way

Sometimes, you need to run some custom scripts after each build of your app with Xcode. For instance, I set the version strings of my apps (aka CFBundleVersion and CFBundleShortVersionString) to my working repository svn revision. I can then uniquely identify apps binaries and quickly see which modifications in my source code introduce weird bug.

Adding a custom build phase that will run a script is very easy in Xcode 4:

  1. In the Project Navigator, select your Project
  2. Select the Target on which you want to add a build script
  3. Select ’Build Phases’ tab
  4. Click on ’Add Build Phase’ button in the lower right corner and select ’Add Run Script’
    Add Build Phase button
  5. In the script window, type your own script or drag a script file over it Custom script

To set the svn version on the app version, I use the following script:

echo -n ${TARGET_BUILD_DIR}/${INFOPLIST_PATH} | xargs -0 /usr/libexec/PlistBuddy -c "Set :CFBundleVersion 1.4.`svnversion -n`"
echo -n ${TARGET_BUILD_DIR}/${INFOPLIST_PATH} | xargs -0 /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString 1.4.`svnversion -n`" 

This script puts the svn revision number, i.e. 1769M, as a minor build identifier. That’s way, my app version will be 1.4.1769M (if you don’t know it yet, PlistBuddy is your friend when you want to deal with plist files).

You can use this technique to run various script after your builds, like do some automated tests etc…

The hard way

Problems begin to arise when you want to invoke some nonstandard tool in your script (like convert from ImageMagick).

You can hardcode the path to this binary in your custom script but maybe you’re using Homebrew as I do and someone in your team is using MacPorts. In this case, you don’t want to hardcode the path to the binaries but you want Xcode to use the right command in the right path.

The problem is that scripts invoked by Xcode don’t read any of the usual shell configuration files (.profile, .bash_profile, .cshrc, /etc/paths …), so Xcode won’t be able to find your tools. If you echo $PATH at the beginning of the script, you will see

$PATH in Xcode

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin 

Xcode customize $PATH with the following directories:

no matter how you’ve configured $PATH in the standard way.

The solution is to use an OSX special environment file: with ~/.MacOSX/environment.plist you can add environment variables so that Cocoa application can retrieve these variables at launch. In this Apple Technical Q&A, you can see how to construct this file.

Simply follow this steps:

  1. Create a .MacOSX folder inside your home directory mkdir ~/.MacOSX
  2. Type in a Terminal defaults write $HOME/.MacOSX/environment PATH "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin". Don’t forget to change the value of the PATH variable with your configuration. As I use Homebrew, /usr/local/bin is the first directory I’m interested in. This command will create the necessary plist, set the variable PATH to your list of binary directory
  3. Logout your session, login and you can run Xcode and now you can use your non-standard tool in your custom scripts!

A small warning: if you’re editing environment.plist with an external editor, insure that the plist is in binary format, otherwise it won’t be taken into account by OSX (to convert the plist in binary, just type plutil -convert binary1 environment.plist).

The Hell way

Now, the crazy problems!

I want to use some svn tools as part as my build workflow (like svnversion). I want also to use the last version of svn, which is 1.7.4, and the version shipped with Xcode is 1.6.17. I don’t know all the modifications of svn 1.7 over 1.6 but the only fact that there is now only one .svn folder at the root of your working directory (à la git..) is so cool…and convince me to switch.

As I’m using Homebrew, to get the last version of svn, I just type this in a Terminal :

brew install svn

And boum! svn 1.7.4 is installed directly on my system, in /usr/local/bin.

But there is a big problem: in my custom build scripts, when I use some svn tools, Xcode keep using svn 1.6.17. The previous technique won’t work because when updating $PATH, Xcode will put its own path first /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin before /usr/local/bin where the new svn is. In any case, Xcode will use in priority the tools in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin.

So to make Xcode 4 work with svn 1.7, the solution is to link your /usr/local/bin/svn* files to the path used by Xcode (solution via Stack Overflow, translated from http://hxsdit.com/1484):

  1. First, create a backup directory of /Applications/Xcode.app/Contents/Developer/usr/bin/svn* files :
    • cd /Applications/Xcode.app/Contents/Developer/usr/bin
    • mkdir ~/Backupsvn/
  2. Move the svn tools used by Xcode
    • sudo mv svn* ~/Backupsvn/
  3. Link you brand new svn to the path used by Xcode
    • ln -s /usr/local/bin/svn* /Applications/Xcode.app/Contents/Developer/usr/bin/

You can now use svn 1.7.4 in your custom script. A big warning: the source control command in Xcode and the Repositories in Organizer doesn’t seem to work anymore . I’ve struggled to identify and solve the problem, and I haven’t succeeded to connect to my svn server… Anyway, as I’m using svn with Cornerstone and sometimes in command line, it’ not a big deal for me.

From jc.

iOS Automated Tests with UIAutomation

tweet

UIAutomation icon

Quick introduction

Automated tests are very useful to test your app “while you sleep”. It enables you to quickly track regressions and performance issues, and also develop new features without worrying to break your app.

Since iOS 4.0, Apple has released a framework called UIAutomation, which can be used to perform automated tests on real devices and on the iPhone Simulator. The documentation on UIAutomation is quite small and there is not a lot of resources on the web. This tutorial will show you how to integrate UIAutomation in your workflow.

The best pointers to begin are the Apple documentation on UIAutomation, a very good quick tutorial in Apple Instruments documentation and, of course, the slides/videos of WWDC 2010 - Session 306 - Automating User Interface Testing with Instruments. You’ll need a free developper account to access this ressources.

Another framework to be mention is OCUnit, which is included in Xcode, and can be used to add unit tests to your app.

  1. Your first UIAutomation script
  2. Dealing with UIAElement and Accessibility
  3. Tips to simplify your life
  4. Advanced interactions
  5. The end

1. Your first UIAutomation script

UIAutomation functional tests are written in Javascript. There is a strong relation between UIAutomation and accessibility, so you will use the accessibility labels and values to simulate and check the results of simulated UI interaction.

Let’s go, and write our first test!

Using iOS simulator

  1. Download the companion project TestAutomation.xcodeproj, and open it. The project is a simple tab bar application with 2 tabs.
  2. Insure that the following scheme is selected ’TestAutomation > iPhone 5.0 Simulator’ (Maybe you’ve already switched to 5.1 so it could be also iPhone 5.1) Scheme
  3. Launch Instruments (Product > Profile) or ⌘I.
  4. In iOS Simulator, select the Automation template, then ’Profile’ Template chooser
  5. Instruments is launching, and start recording immediately. Stop the record, (red button or ⌘R).
  6. In the Scripts window , click ’Add > Create’ to create a new script Create Script
  7. In the Script window editor, tap the following code

    var target = UIATarget.localTarget();
    var app = target.frontMostApp();
    var window = app.mainWindow();
    target.logElementTree();
    

  8. Re-launch the script ⌘R (you don’t need to save). The script runs and you can stop it after logs appear.

Voilà! You’ve written your first UIAutomation test!

Using an iOS device

You can also run this test with a real device, instead of the simulator. Automated tests are only available on devices that support multitask: iPhone 3GS, iPad, running iOS > 4.0. UIAutomation is unfortunately not available on iPhone 3G, whatever is the OS version.

To run the test on a device:

  1. Connect your iPhone to USB
  2. Select the scheme ’TestAutomation > iOS Device’
  3. Check that the Release configuration is associated with a Developper profile (and not an Ad-Hoc Distribution profile). By default, profiling is done in Release (there is no reason to profile an app in Debug!) Distribution profile
  4. Profile the app (⌘I)
  5. Follow the same steps than previously on the Simulator.

2. Dealing with UIAElement and Accessibility

UIAElement hierarchy

There is a strong relationship between Accessibility and UIAutomation: if a control is accessible with Accessibility, you will be able to set/get value on it, produce action etc… A control that is not “visible” to Accessibility won’t be accessible through automation.

You can allow accessibility/automation on a control whether using Interface Builder, or by setting programmatically the property isAccessibilityElement. You have to pay some attention when setting accessibility to container view (i.e. a view that contains other UIKit elements). Enable accessibility to an entire view can “hide” its subviews from accessibility/automation. For instance, in the project, the view outlet of the controller shouldn’t be accessible, otherwise the sub controls won’t be accessible. If you have any problem, logElementTree is your friend: it dumps all current visible elements that can be accessed.

Each UIKit control that can be accessed can be represented by a Javascript Object, UIAElement. UIAElement has several properties, name, value, elements, parent. Your main window contains a lot of controls, which define a UIKit hierachy. To this UIKit hierarchy, corresponds an UIAElement hierachy. For instance, by calling logElementTree in the previous test, we have the following tree:

+- UIATarget: name:iPhone Simulator rect:{{0,0},{320,480}}
|  +- UIAApplication: name:TestAutomation rect:{{0,20},{320,460}}
|  |  +- UIAWindow: rect:{{0,0},{320,480}}
|  |  |  +- UIAStaticText: name:First View value:First View rect:{{54,52},{212,43}}
|  |  |  +- UIATextField: name:User Text value:Tap Some Text Here ! rect:{{20,179},{280,31}}
|  |  |  +- UIAStaticText: name:The text is: value:The text is: rect:{{20,231},{112,21}}
|  |  |  +- UIAStaticText: value: rect:{{145,231},{155,21}}
|  |  |  +- UIATabBar: rect:{{0,431},{320,49}}
|  |  |  |  +- UIAImage: rect:{{0,431},{320,49}}
|  |  |  |  +- UIAButton: name:First value:1 rect:{{2,432},{156,48}}
|  |  |  |  +- UIAButton: name:Second rect:{{162,432},{156,48}}

Hierarchy

To access the text field, you can just write:

var textField = UIATarget.localTarget().frontMostApp().mainWindow().textFields()[0];

You can choose to access elements by a 0-based index or by element name. For instance, the previous text field could also be accessed like this:

var textField = UIATarget.localTarget().frontMostApp().mainWindow().textFields()["User Text"];

The later version is clearer and should be preferred. You can set the name of a UIAElement either in Interface Builder:

Interface builder

or programmaticaly:

myTextField.accessibilityEnabled = YES;
myTextField.accessibilityLabel = @"User Text";

You can see now that accessibility properties are used by UIAutomation to target the different controls. That’s very clever, because 1) there is only one framework to learn; 2) by writing your automated tests, you’re also going to insure that your app is accessible! So, each UIAElement can access its children by calling the following functions: buttons(), images(), scrollViews(), textFields(), webViews(), segmentedControls(), sliders(), staticTexts(), switches(), tabBar(), tableViews(), textViews(), toolbar(), toolbars() etc… To access the first tab in the tab bar, you can write:

var tabBar = UIATarget.localTarget().frontMostApp().tabBar();
var tabButton = tabBar.buttons()["First"];  

The UIAElement hierarchy is really important and you’re going to deal with it constantly. And remember, you can dump the hierarchy each time in your script by calling logElementTree on UIAApplication:

UIATarget.localTarget().frontMostApp().logElementTree();

In the simulator, you can also activate the Accessibility Inspector. Launch the simulator, go to ’Settings > General > Accessibility > Accessibility Inspector’ and set it to ’On’.

Accessiblity Inspector

This little rainbow box is the Accessibility Inspector. When collapsed, Accessibility is off, and when expanded Accessibility is on. To activate/desactivate Accessibility, you just have to click on the arrow button. Now, go to our test app, launch it, and activate the Inspector.

Accessiblity Inspector On

Then, tap on the text field and check the name and value properties of the associated UIAElement (and also the NSObject accessibilityLabel and accessibilityValue equivalent properties). This Inspector will help you to debug and write your scripts.

Simulate user interactions

Let’s go further and simulate user interaction. To tap a button, you simply call tap() on this element:

var tabBar = UIATarget.localTarget().frontMostApp().tabBar();
var tabButton = tabBar.buttons()["First"];  

// Tap the tab bar !
tabButton.tap();

You can also call doubleTap(), twoFingerTap() on UIAButtons. If you don’t want to target an element, but only interact on the screen at a specified coordinate screen, you can use:

When you specify a duration, only a certain range is accepted i.e.: for drag duration, value must be greater than or equal to 0.5s or less than 60s.

Now, let’s put this in practice:

  1. Stop (⌘R) Instruments
  2. In the Scripts window, remove the current script
  3. Click on ’Add > Import’ and select TestAutomation/TestUI/Test-1.js
  4. Click on Record (⌘R) and watch what’s happens…

The script is:

var testName = "Test 1";
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();

UIALogger.logStart( testName );
app.logElementTree();

//-- select the elements
UIALogger.logMessage( "Select the first tab" );
var tabBar = app.tabBar();
var selectedTabName = tabBar.selectedButton().name();
if (selectedTabName != "First") {
    tabBar.buttons()["First"].tap();
}

//-- tap on the text fiels
UIALogger.logMessage( "Tap on the text field now" );
var recipeName = "Unusually Long Name for a Recipe";
window.textFields()[0].setValue(recipeName);

target.delay( 2 );

//-- tap on the text fiels
UIALogger.logMessage( "Dismiss the keyboard" );
app.logElementTree();
app.keyboard().buttons()["return"].tap();

var textValue = window.staticTexts()["RecipeName"].value();
if (textValue === recipeName){
    UIALogger.logPass( testName ); 
}
else{
    UIALogger.logFail( testName ); 
}

This script launches the app, selects the first tab if it is not selected, sets the value of the text field to ’Unusually Long Name for a Recipe’ and dismisses the keyboard. Some new functions to notice: delay(Number timeInterval) on UIATarget allows you to introduce some delay between interactions, logMessage( String message) on UIALogger can be used to log message on the test output and logPass(String message) on UIALogger indicates that your script has completed successfully.
You can also see how to a access the different buttons on the keyboard and tap on it app.keyboard().buttons()["return"].tap();


3. Tips to simplify your life

Introducing Tune-up

Now, you’ve a basic idea of how you could write some tests. You will notice soon that there is a lot of redundancy and glue code in your tests, and you’ll often rewrite code like that:

var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();

That’s why we’re going to use a small Javascript library that eases writing UIAutomation tests. Go to https://github.com/alexvollmer/tuneup_js, get the library and copy the tuneup folder aside your tests folder. Now, we can rewrite Test1.js using Tune-Up

#import "tuneup/tuneup.js"

    test("Test 1", function(target, app) {

    var window = app.mainWindow();
    app.logElementTree();

    //-- select the elements
    UIALogger.logMessage( "Select the first tab" );
    var tabBar = app.tabBar();
    var selectedTabName = tabBar.selectedButton().name();
    if (selectedTabName != "First") {
        tabBar.buttons()["First"].tap();
    }

    //-- tap on the text fiels
    UIALogger.logMessage( "Tap on the text field now" );

    var recipeName = "Unusually Long Name for a Recipe";
    window.textFields()[0].setValue(recipeName);

    target.delay( 2 );

    //-- tap on the text fiels
    UIALogger.logMessage( "Dismiss the keyboard" );
    app.logElementTree();
    app.keyboard().buttons()["return"].tap();

    var textValue = window.staticTexts()["RecipeName"].value();

    assertEquals(recipeName, textValue);
});

Tune-Up avoids you to write the same boilerplate code, plus gives you some extra like various assertions: assertTrue(expression, message), assertMatch(regExp, expression, message), assertEquals(expected, received, message), assertFalse(expression, message), assertNull(thingie, message), assertNotNull(thingie, message)… You can extend the library very easily: for instance, you can add a logDevice method on UIATarget object by adding this function in uiautomation-ext.js:

extend(UIATarget.prototype, {
   logDevice: function(){
   UIALogger.logMessage("Dump Device:");
   UIALogger.logMessage("  model: " + UIATarget.localTarget().model());
   UIALogger.logMessage("  rect: " + JSON.stringify(UIATarget.localTarget().rect()));
   UIALogger.logMessage("  name: "+ UIATarget.localTarget().name());
   UIALogger.logMessage("  systemName: "+ UIATarget.localTarget().systemName());
   UIALogger.logMessage("  systemVersion: "+ UIATarget.localTarget().systemVersion());

   }
});

Then, calling target.logDevice() you should see:

Dump Device:
  model: iPhone Simulator
  rect: {"origin":{"x":0,"y":0},"size":{"width":320,"height":480}}
  name: iPhone Simulator

Import external scripts

You can also see how to reference one script from another, with #import directive. So, creating multiples tests and chaining them can be done by importing them in one single file and call:

#import "Test1.js"
#import "Test2.js"
#import "Test3.js"
#import "Test4.js"
#import "Test5.js"

By the power of the command line

If you want to automate your scripts, you can launch them from the command line. In fact, I recommend to use this option, instead of using the Instruments graphical user interface. Instruments’s UI is slow, and tests keep running even when you’ve reached the end of them. Launching UIAutomation tests on command line is fast, and your scripts will stop at the end of the test.

To launch a script, you will need your UDID and type on a terminal:

instruments -w your_ios_udid -t /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate name_of_your_app -e UIASCRIPT absolute_path_to_the_test_file 

For instance, in my case, the line looks like:

instruments -w a2de620d4fc33e91f1f2f8a8cb0841d2xxxxxxxx -t /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate TestAutomation -e UIASCRIPT /Users/jc/Documents/Dev/TestAutomation/TestAutomation/TestUI/Test-2.js 

UIAutomation on command line

If you are using a version of Xcode inferior to 4.3, you will need to type:

instruments -w your_ios_device_udid -t /Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate TestAutomation -e UIASCRIPT /Users/jc/Documents/Dev/TestAutomation/TestAutomation/TestUI/Test-2.js 

A small catch, don’t forget to disable the pass code on your device, otherwise you will see this trace: remote exception encountered : ’device locked : Failed to launch process with bundle identifier ’com.manbolo.testautomation’. Yes, UIAutomation doesn’t know yet your password!

The command line works also with the Simulator. You will need to know the absolute path of your app in the simulator file system. The simulator ’simulates’ the device file system in the following folder ~/Library/Application Support/iPhone Simulator/5.1/. Under this directory, you will find the Applications directory that contains a sandbox for each of the apps installed in the simulator. Just identify the repository of the TestAutomation app and type in the simulator:

instruments -t /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Instruments/PlugIns/AutomationInstrument.bundle/Contents/Resources/Automation.tracetemplate "/Users/jc/Library/Application Support/iPhone Simulator/5.1/Applications/C28DDC1B-810E-43BD-A0E7-C16A680D8E15/TestAutomation.app" -e UIASCRIPT /Users/jc/Documents/Dev/TestAutomation/TestAutomation/TestUI/Test-2.js

A final word on the command line. If you don’t precise an output file, the log result will be put in the folder in which you’ve typed the command. You can use -e UIARESULTSPATH results_path to redirect the output of the scripts.

I’ve not succeeded to launch multiple scripts in parallel with the command line. Use the whole nights to chain and launch your scripts so you will really test your app “while you sleep”.

Interactively record interaction

Instead of typing your script, you can record the interaction directly on the device or in the simulator, to replay them later. Do to this:

  1. Launch Instruments (⌘I)
  2. Create a new script
  3. Select the Script editor Record interactions
  4. In the bottom of the script editor, see that red button ? Record interactions Press-it!
  5. Now, you can play with your app; you will see the captured interactions appearing in the script window (even rotation event). Press the square button to stop recording. Record interactions

“When things don’t work, add UIATarget.delay(1);”

While writing your script, you will play with timing, animations and so on. UIAutomation has various functions to get elements and wait for them even if they’re not displayed but the best advice is from this extra presentation:

When things don’t work, add UIATarget.delay(1);!


4. Advanced interactions

Handling unexpected and expected alerts

Handling alert in automated tests has always been difficult: you’ve carefully written your scripts, launch your test suite just before going to bed, and, in the morning, you discover that all your tests has been ruined because your iPhone has received an unexpected text message that has blocked the tests. Well, UIAutomation helps you to deal with that.

By adding this code in your script,

UIATarget.onAlert = function onAlert(alert){
    var title = alert.name();
    UIALogger.logWarning("Alert with title ’" + title + "’ encountered!");
    return false; // use default handler
}

and returning false, you ask UIAutomation to automatically dismiss any UIAlertView, so alerts won’t interfere with your tests. Your scripts will run as if there has never been any alert. But alerts can be part of your app and tested workflow so, in some case, you don’t wan’t to automatically dismiss it. To do so, you can test against the title of the alert, tap some buttons and return true. By returning true, you indicate UIAutomation that this alert must be considered as a part of your test and treated accordantly.

For instance, if you want to test the ’Add Something’ alert view by taping on an ’Add’ button, you could write:

UIATarget.onAlert = function onAlert(alert) {
    var title = alert.name();
    UIALogger.logWarning("Alert with title ’" + title + "’ encountered!");
    if (title == "Add Something") {
        alert.buttons()["Add"].tap();
        return true; // bypass default handler
    }
    return false; // use default handler
 }

Easy Baby!

Multitasking

Testing multitasking in your app is also very simple: let’s say you want to test that crazy background process you launch each time the app resumes from background and enter in - (void)applicationWillEnterForeground:(UIApplication *)application selector, you can send the app in background, wait for for 10 seconds, and resume it by calling:

UIATarget.localTarget().deactivateAppForDuration(10);

deactivateAppForDuration(duration) will pause the script, simulate the user taps the home button, (and send the app in background), wait, resume the app and resume the test script for you, in one line of code!.

Orientation

Finally, you can simulate the rotation of your iPhone. Again, pretty straightforward and easy:

var target = UIATarget.localTarget();
var app = target.frontMostApp();

// set landscape left
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);
UIALogger.logMessage("Current orientation is " + app.interfaceOrientation());

// portrait
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_PORTRAIT);
UIALogger.logMessage("Current orientation is " + app.interfaceOrientation()); 

5. The end

Useful links

This was a pretty long post but I hope that you see the power of UIAutomation and the potential burst in quality that your app can gained. There is not a lot of documentation on UIAutomation, but I’ve listed a bunch of links that may help you.

And, of course

You’ll need a free developper account to access this ressources.

A video

To conclude this trip to UIAutomation, I don’t resist to show you how we use UIAutomation with Meon in a little video. We use various test, and in this video, we test that the player can play from level 0 to level 120. Heeeelp me, my iPhone is alive!

From jc.

Meon in Top 6 New Paid Apps on Google Play

tweet

Updated on 2012/04/09: Nº4!!!

Updated on 2012/04/08: Now, n°5!

Oooohhhh boy! 12 days after our introduction on Google Play, Meon is the sixth app in the ’Top New Paid Apps’ category… on the French market…
We have just passed Another World, in the back of FIFA 12, and now our target is Angry Birds Space Premium

Hum…

We have a lot of feedback, a lot of features to add, and, hum, some bugs to correct! Keep in touch, updates are going to be rolled very soon!

From jc.

Clean Up Xcode with Alfred

tweet

Xcode Clean Up with Alfred

From Mugunth Kumar, a small script for cleaning up Xcode when it becomes too slow…

rm -r ~/Library/Application\ Support/iPhone\ Simulator/5.1/Applications
mkdir ~/Library/Application\ Support/iPhone\ Simulator/5.1/Applications
rm -r ~/Library/Developer/Xcode/DerivedData 
mkdir ~/Library/Developer/Xcode/DerivedData

In the associated gist, there is also a proposal to clean the older SDKs versions.

I’ve added a Growl notification to my script, and I will use it with Alfred. If you don’t know yet Alfred, think of Spotlight on steroids. Buy the free version on the Mac App Store, and the £15 Powerpack on the Alfred web site, you won’t regret it.

Speaking of Mugunth Kumar, don’t miss the second part of his RESTful series - Doing it the right way.

From jc.

All Posts