Stewart Gleadow's Blog

My ramblings about iOS and other software topics.

Enabling Accessibility for iOS Applications

I’m looking to write up a few posts about using accessibility for testing native iOS applications. Here is the first one, dealing with enabling accessibility for your apps in the simulator and on the device.

Why use accessibility

Firstly, the iPhone and iPad are setting a new standard for usability by impaired users. That’s a great thing, and I think we should be making a bigger effort to support these features. Apple provides assistants like VoiceOver that use the building UIAccessibility framework. If you’d like to find out more about accessibility for the sake of accessibility, Matt Gemmell has a great post on the topic

A great side effect of making an app accessible to assistive devices is that the app also becomes easier to test in an automated fashion. This post is not about how and why to use accessibility to test your app (although that is a valid topic that I will write a separate post on), it a quick guide to turning accessibility on. Generally, the properties of the UIAccessibilty framework are only available to third party testing tools like Frank and KIF if you have accessibility enabled for the application, either in the simulator or on the device.

Enabling accessibility in the Simulator

During development, we need to enable accessibility for both OS X and the iOS Simulator. Under Mac OS X, open up the System Preferences and open the Universal Access pane at the top right. In the pane that opens, check the box for Enable Access for Assistive Devices. The OS will persist this setting from now on.

Load the iOS Simulator and open the Settings application. Enable accessibility using the switch under General > Accessibility. If you see a little coloured box appear, you have successfully enabled accessibility in the simulator. The setting is stored in an underlying plist file under that iOS version of the simulator, so you will need to enable this setting for for both iOS 4 and 5 separately, but that will effect all of the device types (iPhone, iPhone Retina and iPad) for that OS version. The iOS Simulator will keep accessibility enabled as long as you don’t clear out its settings. If you’re anything like me, you have Reset Content and Settings mapped to a keyboard shortcut, you’ll regularly need to navigate in and re-enable accessibility.

The small coloured box that appears is called the accessibility inspector. It shows a small summary of what is available from the UIAccessibility framework for iOS. There are two main types of information shown in the inspector: notifications and properties. Notifications are fired when the UI changes. To be honest, I’ve not played around with firing accessibility notifications much at all. Notifications could be a potential solution for tests that need to ‘wait’ until a screen transition is finished before continuing rather than busy-waiting or just a plain old sleep (you know it’s not a real test suite unless there are a couple of sleep calls in there!).

The properties show aspects of the selected UI element. With the accessibility inspector expanded, tap around in some of Apple’s built in applications to see the UIAccessibility properties. With the accessibility inspector expanded like this, the first touch even brings up the accessibility – which is great if that’s the only way to use the app, but can get in the way if you’re not used to it. If you collapse the inspector using the little cross-button, touch interaction returns to normal. In the image above I’ve collapsed the accessibility inspector and dragged it to the side, since the properties are available to our tests anyway, so it’s easiest to get it as far off the screen as possible.

Enabling accessibility on the device

Usually, to get access to the accessibility framework on an actual iOS device, you need to enable VoiceOver. If you do your testing with Apple’s sanctioned UIAutomation Instrument, it seems to be able to hook in automatically without you having to specifically enable those features. Although with iOS 5, I’ve found that to not always be the case.

VoiceOver is pretty easy to enable in the Settings app under General > Accessibility > VoiceOver. Once this is enabled, the device acts in a similar way to when you have the accessibility inspector visible in the simulator. A pleasant computerised voice now describes your every gesture, and more importantly, activates the accessibility framework for all applications, including the one you want to test. Your first tap will select a UI element and read the available information about it. Double-tapping actually executes the action for a button. People using VoiceOver as a means to navigate the OS are likely to drag their finger on the screen to get a better idea of where items are relative to each other, so single-finger scrolling is also disabled. You can scroll by dragging with three fingers.

Navigate some well known apps on the phone, and see how Apple’s own applications integrate with VoiceOver. To actually design an accessible application, you’ll be wanting to spend a whole lot of time using VoiceOver yourself to get an idea of what information is useful and necessary. At some stage, you will pick up a test device with VoiceOver on, so it’s good to know at least how to get into the settings and turn accessibility off in order to operate manually.

If you are only enabling VoiceOver for testing purposes, the changed gestures and audio instructions can seem to get in the way of you just using the device. If you regularly switch accessibility on and off, using iTunes is going to be much more convenient. Before the latest version of iTunes, enabling accessibility this way required the device to be plugged in via USB. I was pleasantly surprised to discover that this VoiceOver can now be toggle on and off over wifi.

Enabling accessibility programmatically

Note: these steps apply to the iOS Simulator only. If someone knows how to enable accessibility programmatically on the device, I would love to know

When you are running tests in the simulator, it’s likely that you use the Reset Content and Settings… menu item frequently. If you forget to reenable accessibility after this, tests will fail because UI information will not be available to the tests. We need a programmatic way to turn accessibility on. I’m not sure how to do this on the device, but in the simulator, this is just a matter of setting a flag in one of the underlying plist files. It’s possible to call plist editors from the command line to do this, but then the plist file is separate for each iOS version supported by the simulator. I find it’s easier to call from the Objective C code at run time, since it it possible to obtain the root for that version of the simulator.

#import <dlfcn.h>

+ (void)load
{
    NSAutoreleasePool *autoReleasePool = [[NSAutoreleasePool alloc] init];

    NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"];
    if (simulatorRoot) {
        void *appSupportLibrary = dlopen([[simulatorRoot stringByAppendingPathComponent:@"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport"] fileSystemRepresentation], RTLD_LAZY);
        CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");

        if (copySharedResourcesPreferencesDomainForDomain) {
            CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));

            if (accessibilityDomain) {
                CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
                CFRelease(accessibilityDomain);
            }
        }
    }

    [autoReleasePool drain];
}

I first tried to use this code in init for a test framework class, and it didn’t seem to work. Moving the code to be called from load solved the problem. Since this code actually changes a plist file on the file system, perhaps it needs to be executed early before the UI loads so that the rest of the system acts as if accessibility is enabled. Not only does this approach ensure accessibility is always enabled in the simulator, it doesn’t bring up the inspector over the top of the UI.

Special thanks to Cedric Luthi, who originally wrote the code to enable accessibility programmatically for DCIntrospect in this commit, and to an awesome colleague of mine, Sadat Rahman (@sadatrahman), for bringing that code to my attention.

Moving From Wordpress to Octopress

My Wordpress blog was a complete failure in terms of content and effort on my part. I blame part of this on the barrier of having to use Wordpress, which I find bloated and slow. I want to write blogs the same way I write code: in my text editor using git to keep track of changes, and deploying with a git push. I’m hoping most of my posts will involve a lot of code snippets, so being able to edit the posts in a proper programmer’s text editor, in plain text, using Markdown is ideal.

I tried to get jekyll set up, but being a bit of a noob when it comes to stylesheets as well as Ruby, I just couldn’t find the time to get the blog set up properly. This morning I read about Matt Gemmell’s blog moving to Octopress, which seemed the best of both worlds. The simple ongoing use and management that jekyll provides, and with some additional scripts to set it all up for me.

I’ve gone and set it all up this afternoon, now to see if that means I actually have the time to write up posts!

Xcode 4 Shortcuts Lightning Talk

Last week I have a short lightning talk with Jesse Collis (@sirjec) at the Melbourne Cocoaheads meetup. The topic was fairly boring, just some useful Xcode 4 shortcuts we have found since switching from Xcode 3 a few weeks ago. It ended up being a bit of a laugh, probably the first and only time a talk about keyboard shortcuts will be funny – I think it had something to do with the pizza and beer that had just arrived.

Jesse saved me the effort of writing up anything more and linking to the slides on his blog below.

http://blog.jcmultimedia.com.au/2011/04/xcode4-shortcuts-melbourne-cocoaheads-april-2011.html

I’ve managed to find the video footage of the xcode shortcuts talk put together by Oliver Jones (@orj) on vimeo. After flicking through the footage a couple of questions were raised that I can now answer:

  • Q: Does Cmd+Option+[ or ] work if multiple lines are selected?
    A: Yes.
  • Q: Does Ctrl+6 for the class outline in the jump bar work in non-primary editors?
    A: Yes, it is based on the editor that has focus.
  • Follow up: Proper clean (Cmd+Option+Shift+K) did not completely clean the build for us.
    rm -rf is still your friend

The Melbourne Cocoaheads faithful also contributed some keyboard shortcuts of their own, here are the ones I jotted down:

  • Cmd+Ctrl+Opt+? – open item under the cursor in Xcode help (“real help, not the stupid little help” @tupps)
  • Cmd+Double-Click – drill through to the implementation
  • Cmd+Opt+, – open current file back in the main editor
  • Cmd+/ – Comment Line
  • Cmd+Shift+[ or ] – navigate back and forth between tabs
  • Cmd+T – Open new tab (same as Safari)
  • Cmd+Ctrl+Up and Down – switch between counterparts

and a few more shortcuts I’ve found since then:

  • Cmd+Ctrl+Opt+Up – open up the counterpart in the secondary editor
  • – Organise Import… just kidding. Maybe one day.

I’m sure there are plenty more shortcuts I haven’t found yet. Let me know the ones you find most useful.

RVM: Escaping Gem Dependency Hell

This is my second post on my move to rvm. The first being about cleaning out my existing gems for a fresh start.

Bundler seemed like it was the ideal solution to having specific gems for specific projects, but it seems to install in your system gems by default causing version and dependency conflicts with other projects I’m working on. I know I can use bundle --deployment but then I have to bundle exec everything and I don’t have enough Ruby-fu to use the bundler-managed gems within Rakefile etc.

I’m looking to rvm to solve the problem. The steps I followed are below:

Installing RVM

There are some pretty good installation instructions on the rvm site. The actual installation is a just single command.

$ bash < <(curl -B http://rvm.beginrescueend.com/install/rvm)

You’ll see a bunch of output, and towards the bottom, a confirmation message saying “Installation of RVM to /Users/sgleadow/.rvm/ is complete.”, but that’s not all. You need to set up your shell to know about rvm. Put the following line into your .bash_profile or wherever your chosen shell wants it.

[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

After that, source your profile and check rvm is present and accessible.

$ source ~/.bash_profile
$ which rvm/Users/sgleadow/.rvm/bin/rvm
/Users/sgleadow/.rvm/bin/rvm
$ rvm --version
rvm 1.5.2 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]
$ type rvm | head -1
rvm is a function

Installing some Rubies

Now that rvm is installed, let’s install some rubies to use with it. First, check which ruby versions are available.

$ rvm list known
...
[ruby-]1.8.7[-p334]
...

I’ll install the main two versions that I’m using at the moment. Each install will take a few minutes, so this is a good time to make a cup of tea. I would usually say coffee but I haven’t bought any fresh coffee beans in a while and the stuff I has is so old that the coffee tastes like sawdust.

$ rvm install 1.8.7
$ rvm install 1.9.2

I’m still using 1.8.7 for some things at the moment, so I’ll set that up as the default, and double check that the active ruby binary is now the one I just installed using rvm.

$ rvm --default use 1.8.7
$ ruby -v
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10.7.0]
$ which ruby
/Users/sgleadow/.rvm/rubies/ruby-1.8.7-p334/bin/ruby
$ which gem
/Users/sgleadow/.rvm/rubies/ruby-1.8.7-p334/bin/gem
$ rvm list default
Default Ruby (for new shells)
ruby-1.8.7-p334 [ x86_64 ]

Done. We have rvm up and running

Setting up your gemsets

While rvm solves the issue of having multiple versions of ruby installed on the one machine, and switching between them, it also allows you to set up multiple gemsets, which is the feature that started me on this journey in the first place. I’d like to set up a separate gemset for each separate project to try and keep them separate and escape from dependency hell.

I should have a pretty clean slate to start on for my gems. I’m not 100% on whether rvm will use my old system gems or not, but I’ve cleaned them all out anyway, so it shouldn’t be a problem.

$ gem list
*** LOCAL GEMS ***
rake (0.8.7)
$ rvm list gemsets
rvm gemsets
=> ruby-1.8.7-head [ x86_64 ]
   ruby-1.8.7-head@global [ x86_64 ]
   ruby-1.9.2-head [ x86_64 ]
   ruby-1.9.2-head@global [ x86_64 ]

rvm will set up a default gemset for you, but I’d like to have each project automatically set its own ruby version and gemset. We can use the .rvmrc file to achieve this. I’m setting a project up that uses the Frank iOS testing tool, below is a sample of the .rvmrc file I have inside that my project root directory. When I enter that directory in the shell, rvm picks up the rc file and will switch to that version of ruby (the part before the @) and also switch to a specific gemset (the part after the @). The --create means that if the gemset does not already exist, create one. The first time you enter the directory, you will need to specifically allow rvm to do this. After that, it’s automatic.

$ cat my_project/.rvmrc
rvm --create use ruby-1.8.7-p334@frank

Once the .rvmrc file is created, leave and return to the directory to kick it off, authorise it and you should see something like Using /Users/sgleadow/.rvm/gems/ruby-1.8.7-p334 with gemset frank printed to the console. To double check we are, in fact, using the newly created gemset, use rvm list gemsets or rvm gemset list and check the little hashrocket is pointing to the new gemset. Do another gem list to double check you have a clean slate.

Most of my projects use bundler, so first I’ll need to install that, then I can use bundler to manage all the gems I need installed. Before using bundler, I wanted to remove the existing gems that had been used by bundler.

$ rm .bundle/config
$ rm -r vendor/bundle
$ gem install bundler
$ bundle

Bundler should install all the gems listed in the Gemfile into the gemset you have just defined for this project. Once bundle is complete, check the gems were installed in the gemset and not just in vendor with gem list. If you’re project is all set up with a Rakefile to run your tests, try that to double check everything has gone smoothly.

Cleaning Out Ruby Gems Under OS X

As a bit of background, I have been getting into Ruby development for the past year, but I have been getting into gem dependency hell recently. Different projects need different gemsets. Bundler helps but I want to use that in combination with rvm to keep things organised.

To get a clean start with rvm, so I wanted to remove all my existing system gems. My first attempt used the little shell script below to loop over all installed gems and remove them. That worked for a lot of gems, but left some behind, possibly all the default gems that come pre-installed with OS X.

$ for x in `gem list --no-versions` ; do gem uninstall -aIx $x; done

After some googling, the next step I tried was to physically remove the gem directories that are returned by gem env paths. After that, gem list said there were no gems installed. There were still some binaries left over that depended on these gems, like spec, rails, rake in /usr/bin. These binaries no longer worked as the actual gem was gone, so I removed them. I couldn’t find a definitive list of the OS X default gems so I stopped here.

I’m not a shell guru by any means, but below is my attempt at a quick one line shell script to remove all the existing gem directories.

rm -rf `gem env paths | tr ":" " "`

Hello World

I’ve been meaning to start a blog for a while know. Until now, I’ve kept notes to myself in all sorts of places about problems I’ve encountered while developing software and solutions I’ve found. My favourite way of keeping notes to date is in a nice Moleskine notebook with a fountain pen – a bit backwards I know, you wouldn’t believe I write software for a living.

Often my notes just disappear into the depths of my filesystem or bookshelf never to be seen again. If I’m really proactive, I might write up an answer on Stack Overflow or reply to a mailing list. The next time I want to share that experience, I have to filter through to find that information again

Hopefully a blog will be an easier way to keep track of this information and later refer to it. Whether it’s useful information or not remains to be seen, but it will at least be useful to me. For shorter, random and less useful musings, I’m on twitter at @stewgleadow

The current topics I’m interested in are:

  • Objective C and native iOS apps
  • testing iOS apps, mainly with GHUnit and Frank
  • ReST APIs in Ruby/Rails
  • hand-wavy discussion about agile software development and testing in general

User groups I’m involved in or attend occasionally: