Providing a Soft Landing with WebKit

So your user has just downloaded your shiny new application. Like all your applications you’ve strived to make it as simple and intuitive as possible, yet sitting now in front of your user for the first time it would be nice if it could just give them a gentle nudge in the right direction. A video tutorial perhaps? Help creating that first project?

What you need is a welcome window, like the one in GarageBand, which pops up the first time a user runs your application. This concept was recently described as a Soft Landing by Ruben Bakker (@Mailplane) during Matt Gemmell's workshop at NSConference. The expression really appeals to me, it sums up exactly what we want to achieve, giving the user a gentle introduction to our software rather than crashing in head first.

We recently had to implement a soft landing for one of our products. In this post I'll describe the mechanics behind it and, as we've open sourced this component, I'll also provide instructions for integrating this component quickly into your own application.

Soft landings tend to be less graphically constrained than the applications they support. A typical Mac application may, for example, have a table or outline view on the left hand side with a more detailed view of the current selection on the right. A welcome screen by contrast may have any arrangement of images and text. It makes sense therefore to use a technology such as HTML to render this content. The question is: how does this HTML interact with the underlying Cocoa application?

The answer is: very easily. WebKit provides a bridge which allows JavaScript to communicate directly with Cocoa objects in your application, sending messages and accessing properties. The first step in setting up this communication is to tell the JavaScript code about your object.
id scriptObject = [webView windowScriptObject];
[scriptObject setValue:self forKey:@"cocoaObject"];
The JavaScript code can now access your object as follows:
var cocoaObject = window.cocoaObject;
Because we do not want script running within a web browser to be able to access any old method or property we now need to provide a couple of class methods to control this access, one for methods and one for properties:
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (BOOL)isKeyExcludedFromWebScript:(const char*)name;
These methods should always return YES unless the passed in selector (or key name) is one for which we want to provide access.

In our example we have also provided one more class method:
+ (NSString*)webScriptNameForSelector:(SEL)sel
This method lets us provide cleaner method names for JavaScript to use. By default the bridge replaces colons with underscores so the Objective-C call:
[a setValue:b forKey:c];
Would in JavaScript become:
a.setValue_forKey_( b, c );
The webScriptNameForSelector: method lets us overwrite the default behaviour to provide our own method names.

Integrating our component.

You can download the code, along with an example project from github:


There are 4 files which you'll need to include in your project:

OPSSoftLandingWindowController.h
OPSSoftLandingWindowController.m
SoftLanding.XIB
SoftLanding.html

Copy these files to your project and include them in the target. You also need to ensure that your project links with the Webkit framework.

You will need to modify the XIB file to change the text to match your application. There are two places where you need to do this; the title on the Window and the text on the "Show this window" checkbox.

To display the soft landing at startup add the following line at an appropriate location in your code (the applicationDidFinishLaunching: method on your app delegate is probably as good a place as any):
[OPSSoftLandingWindowController showWithDelegate:self onlyIfRequired:YES];
This method takes two parameters. The first parameter is a delegate object which will handle any notification messages from the JavaScript. The controller expects the delegate to implement the following method:
- (void)softLandingWindowController:(OPSSoftLandingWindowController*)softLanding didRecieveNotification:(NSString*)notification;
You may pass nil as the delegate if you only want the soft landing to display some HTML and do not have any messages which may be passed back.

If the user selects not to show the soft landing at application startup there is currently no way for them to see it again. This isn't very nice, so you will probably want to add a menu under Help to allow the user to display the welcome screen again.
- (IBAction)showWelcome:(id)sender
{
[OPSSoftLandingWindowController showWithDelegate:self onlyIfRequired:NO];
}
That's it. All that remains is your customisation of the displayed HTML. If you're unfamiliar with JavaScript you might want to have a look at the sample SoftLanding.html as a starting point. The important point is that the headers (denoted by the h1 tags) are told to call the onClickHandler JavaScript code when the user clicks on them.
<h1 cocoaTagName="Welcome Clicked" onclick="onClickHandler(event)">Welcome to ShinyApp</h1>
This code then pulls the cocoaTagName attribute from clicked element and passes it on to your application.

The code is distributed under the FreeBSD license (do what you want with it, but don't sue us when it all goes wrong). Enjoy!

Unleashing your hidden supercomputer

The following is the content of a talk on OpenCL I gave last night at Aberdeen’s inaugural techmeetup. You should be able to watch it online shortly from the Techmeetup website.

http://techmeetup.co.uk/blog/

What's on your computer?

Back in 1965 Gordon Moore, one of the co-founders at Intel hypothesised that the speed of processors relative to their cost, would double approximately every two years. And up until recently this trend was holding.

Well, as you’ll know, things more or less continued along these lines until a couple of years ago when we started reaching processor speeds approaching 4GHz. But, I was being a little disingenuous when I referred to Moore’s Law as a doubling of speed because what Moore was actually talking about was the number of transistors which could be placed inexpensively on a chip. From this stand point Moore’s law is still holding, these days more computing power comes in the form of multiple processing units.

For example, my current computer has a 2.4GHz Core 2 Duo processor capable of performing around 20 GFLOPS (or 2x1010 floating point operations per second). This is about the same as Intel’s single core Pentium 4 running at 3.2GHz. If this is what 2 cores can do, imagine what 32 cores could do?

Well, it just so happens that I have just such a machine and it’s not that uncommon, many of you will as well.

Of course I’m referring to the graphics card. In my case I have an nVIDIA GeForce 9600M GT. This particular card has 32 cores, each running at 500MHz and providing a staggering 120 GFLOPS; six times that of the CPU alone. In fact, counting also the second GPU built into the logic board I have a machine which is theoretically capable of almost 200 GFLOPS.

Taking another example, the nVIDIA GeForce GTX 285 has 240 cores each running at around 650MHz. We’re now talking about a GPU capable of around 1TFLOP - around 50 times more that my processor. What’s more, a card like this will set you back about £300, so we’re not talking about large fortunes here.

So the question is: How can we utilise this additional power? Looking at the traditional frameworks for concurrent programming such as POSIX threads or OpenMP there is a fairly obvious drawback: They only utilise the multiprocessor capabilities of the CPU.

This may change over time as the boundaries between GPUs and CPUs narrow. According to nVIDIA’s CEO, Jen-Hsun Huang, the future of computing may well see the CPU replaced by beefed up GPUs. Whether that happens remains to be seen. However, right now we need another approach. (I have to confess that I read about this an article a couple of months back. However, looking at some more recent reports it appears that there was either some misreporting or some backtracking).

So for the moment at least we need to move into the realm of GPGPUs (General Purpose GPU programming). Let’s examine some of the options we have here.

Firstly there is CUDA. CUDA was developed by nVIDIA and stands for Compute Unified Direct Architecture. CUDA programmers use a C based language to code for the GPU. I believe it is quite widely used and there are third party wrappers for languages such as Python or Java. However, being developed by nVIDIA means that only nVIDIA hardware will be supported.

A similar offering is FireStream, which is AMD’s equivalent to nVIDIA’s CUDA. I’m not sure whether FireStream has as much of a following as CUDA, but again the disadvantage is that it is hardware specific.

A third option is DirectCompute. This is a extension to Microsoft’s DirectX collection of API and possibly worth considering if you’re only targeting Windows. But as a Mac developer it’s not what I’m looking for.

OpenCL

Another option, and the focus of this talk, is OpenCL. So what is OpenCL?
OpenCL stands for the Open Computer Language and is touted as “The Open Standard for Heterogeneous Parallel Programming”. It is intended to be to general purpose programming what OpenGL is to graphics programming or what OpenAL is to audio programming.

So why OpenCL? Well it is hardware and platform agnostic.

Some background: OpenCL was originally devised by Apple. However, in June 2008 Apple handed responsibility for the specification over to the not-for-profit consortium the Khronos Group. I think it would be fair to say that any major software or hardware company who have an interest in promoting open standards have some sort of input into the Khronos group. This list includes nVIDIA, AMD, Apple, Google, ARM, IBM, Intel and many more.

Image courtesy of Khronos Group


The fact that Intel is in this list is interesting. Remember I said that OpenCL was hardware agnostic, well that’s true. OpenCL is not just limited to the GPU but can be run on CPUs, FPGAs and any other processing unit for which an OpenCL implementation has been provided. In fact, if you’re lucky enough to be writing software for Snow Leopard then you are guaranteed to have an OpenCL compatible device; if the GPU isn’t compliant then at least the CPU will be.

This is great if your an Apple developer, but it is important to note that OpenCL is not just for Macs. This would be equivalent to saying that just because Apple provide great OpenGL support OpenGL is just for Macs. SDKs are becoming available for other platforms, although as far as I can tell at this stage they are mostly in beta. This is fairly understandable as the whole specification has been pushed through in record time.

So, you will need to be a little more careful about supported devices on other platforms. Although you should be able to check hardware support on your system through the APIs it is something you should be aware of.

So how does it work?

So how do you go about writing an application with OpenCL? Well you actually have to write two distinct sets of code: kernels written in a C based language and a controller which, through the OpenCL API, manages the running of these kernels on different compute devices.

An OpenCL application consists of a host which communicates with a set of Compute Devices. You can think of the host as your application and the compute devices as the individual CPUs, GPUs etc.

An OpenCL kernel may be compiled from code at runtime. By doing this the kernel may be optimised specifically for the hardware on which it is to be run. We use the API provided by the OpenCL SDK to perform this compilation and any developer who is familiar with OpenGL should feel quite comfortable with this API. I believe it is also possible to pre-compile kernels, but I have not done so and I believe this would be the exception rather than the rule.

Each compute device is further subdivided into Compute Units which are in turn divided into Processing Elements. So, for example, the nVIDIA GeForce GTX 285 with 30 streaming multiprocessors, each comprising 8 streaming processors would have 30 compute units and 240 processing elements.

The kernels you create will be executed on individual processing elements. A kernel executing on a single processing element is known in OpenCL as a work-item. You can think of this as a single thread of execution. Work-items are in turn grouped into work groups with a single work group running on a single compute unit. Hold that thought while I talk about memory quickly...

There are basically two types of memory which a kernel can access: global and local. Global memory is memory on the compute device, accessible by all work-items on that device. Local memory on the other hand is accessible only by work-items within the same work group. Going back to the graphics card you can see that the local memory corresponds to the local memory cache on each of the streaming multiprocessor units.

There are actually two other memory types: constant memory, which is really just constant global memory and private memory which is just memory defined within the scope of the kernel’s function.

Each executing kernel (work-item) can uniquely identify itself by a global unique id, unique across the device on which it is running. A work-item can also identify itself by a unique local id and an id unique to the work-group to which the work-item belongs. These IDs will be used to access memory which the work-item needs to read from or write to. Memory in OpenCL may be allocated either as a single dimensional buffer or as a 2 or 3D image. Correspondingly the work-items have either 1, 2 or 3 global (or local) ids. For example the method get_global_id(0) may be used to get the x co-ordinate of a 2D image while get_global_id(1) will return the y co-ordinate.

Drawbacks

So are there any drawbacks to OpenCL? Well, yes there are a couple. Firstly, at the moment the processors on most graphic cards will only perform 32-bit floating point operations, although you can set #pragma directives to tell the device to do double precision calculations if it is capable. Of course there are ways around this if you need to carry out higher precision calculations, but these will generally involve more operations to be carried out.

Another point to bear in mind is that transferring memory onto a graphics card is relatively slow. Any performance gain achieve by running the process on multiple cores may be negated by performing this memory transfer.

You will also need to take into account the fact that your code will only have access to those methods defined by the OpenCL specifications. printf statements, for example, will not work (and, somewhat related to that, debugging your kernel objects is going to be tough).

Finally, (and here I really have to confess to being fuzzy on the detail) because we are running on streaming processors, branching in your code is not going to behave the same as in normal code executing on the CPU. For instance, if you have an if-else statement in a kernel running in 8 work-items all occurrences of the if condition need to complete before the else conditions execute.

Demonstration

For the demonstration I’ve written a little application which generates Mandelbrot set images. I’ve always been fascinated by the Mandelbrot set; I’m amazed by the beautiful an complex images produced by such a simple equation.

To briefly explain: A complex number C can be said to be in the Mandelbrot set if the absolute value of Zn in equation Zn+1=Zn² + C is less than a given value after n iterations. If a given point is not within the set we can assign it a colour based on the number of iterations required before Zn exceeds the given tolerance.

If we define the maximum number of iterations required to determine whether a value is within the set as 1500 (which is the value I have chosen to use in the demonstration) we could potentially have to perform this calculation 1500 times for each pixel. Each of these calculations could themselves comprise10 floating point operations. Applying this over a 1000 by 1000 pixel image it should be apparent that we could be carrying out around 15 billion operations.

It should also be clear that the calculations carried out on a single pixel should in no way influence the calculations carried out on adjacent pixels. So as well as being an interesting example, the Mandelbrot set should also provide a good candidate for optimisation using OpenCL.

I wrote the example to perform the calculation in 3 ways. The first example simply performs the calculation on the main thread of the application using standard C. The second two perform the same calculation using OpenCL, firstly running solely on the CPU and then running on the GPU.

The results (I’m happy to report) are fairly conclusive. Running on a single thread the calculation takes a little over 10 seconds on my machine. Using OpenCL on the CPU the time is reduced to just over 4 seconds. This makes sense, we are doubling the number of cores and approximately halving the time taken required for the calculation.

Running on the GPU reduces the time further to around 0.67 seconds. Again this is about a sixth of the time required by the CPU and back at the start I said that the GPU was capable of processing around six times more operations per second than the CPU.

The code for the example can be downloaded from here. Although the GUI is written in Objective-C, I have written the OpenCL part in C++, so if you’re not on a Mac you should still be able to give it a go.

I would dearly love to see this example running on the GTX 285 I talked about - if anyone does this please let me know how you get on!

References

I hope this talk has peaked your interest enough that you’ll want to go and start finding out about this stuff for yourself. A good starting point is obviously the Kronos group website:

http://www.khronos.org/opencl/

Or you can download the specifications from:

http://www.khronos.org/registry/cl/specs/opencl-1.0.48.pdf

I would also strongly recommend that you watch the video podcasts produced by Dave Gohara at MacResearch. Even if you’re not looking at OpenCL for the Mac these are definitely well worth watching.

http://feeds.feedburner.com/opencl

The First Aberdeen TechMeetup

It's the morning after the night before - The night of the first ever TechMeetup in Aberdeen.

TechMeetup is a no bullsh*t get-together of people interested in Technology. It's about sharing knowledge. And it's about networking. But it doesn't feel like your typical business networking event. There's no name tags, suits, sales pitches or posh nibbles. But there is free beer and pizza! You can read more about the ideas behind TechMeetup on the TechMeetup Website.

TechMeetup has been running in Edinburgh and Glasgow for some time and a couple of months ago we got together with John Eddie, Bruce Scharlau, Alan Gardner and Jim Emerson to see if we could get an Aberdeen TechMeetup off the ground. And last night we held our first meeting. And it wasn't a total train wreck!

About 30 people braved a rather harsh October night to get themselves to Aberdeen University. Sam Collins and Marius Ciocanel, organisers from the Edinburgh TechMeetup, were also present to help us kick things off. The format of the meeting was pretty relaxed - there was hour or so of mingling and chatting during which time the beer and pizza arrived (with many thanks to our benevolent sponsor - Aberdeen University). Then John led a round of introductions where we all introduced ourselves before heading through to the lecture theatre for the main part of the evening - the presentations.

Sam Collins, who founded TechMeetup in Edinburgh, was the first to speak. He gave a great background into why he started TechMeetup and why it is the way it is. The first technical speaker was our very own Gavin MacLean, who spoke about OpenCL. The content from his talk will appear here on the Open Planet Software blog very soon. The final speaker of the evening was James Littlejohn who talked about http://mepath.com. Essentially, the idea behind mepath is to provide its users with relevant links to blogs that will interest them without having to search for them. He spoke about using the 'wisdom of the crowd' and explained the architecture of the mepath engine. Slides from his presentation are online here.

The presentations were recorded to video and will be online in a few days. I will post a link once they are live.

Aberdeen TechMeetup is going to be a monthly event - running on the third Wednesday of each month. If you're in the Aberdeen area and are interested in technology, I would encourage you to come along.

The next meeting will be on the 18th November in the Meston Building at Aberdeen University where one of the presentations will be on developing for the Microsoft Surface. More details will be on the TechMeetup site nearer the time.

Creating an Open Directory Replica

Today we had the need to set up an OS X 10.5 server outside our LAN that needed to authenticate users - the same users that have accounts inside our LAN. We decided the best way to achieve this was to replicate the master Open Directory onto this new server.

The tools to do this are built in to Apple’s Server Admin GUI tool so it should be easy. Right? Wrong!

To be fair it is actually really easy if you know about a couple of gotchas.

The first time we tried to create the replica it would get so far through and then Server Admin would stick at the following step:

Establishing Replica
Enabling password server replication

We found the solution to this the hard way, so hopefully this blog post will help others avoid these pitfalls in the future.

The first thing to think about is DNS. Although we had no problems, there are numerous posts on the internet that say that DNS must be working 100% for the Open Directory to function correctly. This means both forward and reverse DNS lookups need to resolve correctly. You can check using this command on both the master and replica servers:

changeip -checkhostname

Replication happens via SSH. You can think of creating the replica as pulling down a copy of the directory from the master. Once the replica has been created it works the other way round - changes are pushed from the master to the replica.

During the creation of the replica SSH must be allowed in both directions. This means opening port 22 in both directions on any firewalls between the two servers. Once the replica has been created the incoming SSH port to the master can be closed to help protect the master if required.

The crucial point to note is that the replica server needs root access via SSH to the master. This is NOT enabled by default on OS X 10.5 server so you’ll have to temporally enable it. Add the following three lines to the bottom of the /etc/ssh_config file:

PermitRootLogin yes
PasswordAuthentication yes
PubkeyAuthentication no

These lines can be removed once the replica has been created.

(Thanks to this post on Apple Discussions for pointing this out)

From here it really is just a case of following these instructions from Apple.

Trying to get noticed

We are about to launch an iPhone puzzle game. It’s called Balcassa. It’s an addictive cross between a slider puzzle and a Rubik’s Cube and has been designed exclusively to make use of the iPhone’s touch interface. In short, we think it’s got what it takes to be a reasonable success.

The problem is... how do we get it noticed? The App Store’s a pretty crowded place these days! The solution... we’ll have to do some marketing.

The ideas for a lot of the marketing we’re about to do came from the excellent podcasts produced by Scotty from the Mac Developer Network. We also did an App Store experiment a few months back from which we learned a few lessons.

Maximise the App Store
There are a few little tricks that can help make the most of your presence on the App Store.

Browsing the App Store is highly visual and having a top notch icon will help your app stand out and be recognised. We hired top designer Emanuel Sá to design the icon for Balcassa. Working with him has been an absolute pleasure. We’ve learned a lot about design and can’t wait to work with him again.

Make sure your blurb and screenshots are as good as possible. We try keep the blurb to a minimum and let the screenshots speak for themselves. I’m sure no-one really reads all that long text, especially if they’re viewing on the iPhone.

Then we need to make sure we get out the door on the right day! What I mean by that is getting onto the front page of ‘what’s new’ in the puzzle game category. And this appears to be down to managing the release date when the app is submitted to the App Store. Believe it or not, we’ve failed to achieve this twice already! The first time we submitted an app, we set it up for immediate release. Then we waited the two weeks for Apple’s approval and it ended up being given a release date the same as the date of submission - the result was it was on page 13 on the actual release day, not page 1. On our second experiment we set the release date for three weeks in the future. Again we waited two weeks for approval. Then another week until our chosen date arrived. And what release date did Apple give it?? The date they approved it - so once again our new release was buried several pages down. So this time we’ve set the release date way into the future and we’ll reset it on the day Apple approve the app.

New releases are also automatically picked up by various sites such as 148apps and included in their RSS feeds. So you should get on some people’s radar that way.

If you are very, very lucky some nice person at Apple will like your app so much it’ll get added to one of the featured sections in the App Store. But there are just a handful of spots available and 10s of thousands of apps. If it happens that’s great, but don’t expect it. As far as marketing goes, you’re going to have to take action.

Press Release
PRMac.com offer a press release distribution service for Mac and iPhone releases that is so inexpensive it’s almost crazy not to use it. Your press release gets sent to the big Mac Magazines and various places online. You can choose to write your own press release and just use their distribution services, or you can get them to write the release for you as well. For the first one I’d certainly recommend getting PRMac to write it because the press expect them to be written in a certain style, not just your typical iTunes blurb. You provide the details and PRMac craft it into a first draft of a press release and then you enter a cycle of review with them until you are happy. A great service at a great price!

Generate a little pre-release hype
We did a nice little video of Balcassa and uploaded it to YouTube. We kept it under one minute and added some cool music. We also purchased the balcassa.com domain and put up a little pre-release site with the video and a link to @balcassa on Twitter so people could follow the news.

We also posted some pre-release info and the video onto Forums at sites such as toucharcade.com.

Fusion
The final thing we decided to do is to spend some not-insignificant money on an advert that will run throughout September. On the recommendation of our designer, Emanuel Sá, we’ve chosen to use Fusion Ads. One of the main reasons we chose Fusion is that their ads appear inside Tweetie, a very popular Twitter client for the Mac, as well as several popular sites such as Cocoia and Ember.


Balcassa is now somewhere in the mysterious App Store approval process. We’re expecting approval later this week and then we start putting this plan into action. Of course if it’s rejected then we’ll have to rapidly rethink our plans! Stay tuned. We’ll be back to keep you up-to-date with progress...

Fun with NSOperation

NSOperation was introduced with Leopard as an attempt by Apple to simplify the task of writing multi-threaded applications in Objective-C. This will become increasingly important as we move away from faster processors to more cores and you can be sure that NSOperation is only the start of what Apple has in store for us in this area. In this article I discuss a fun little use of NSOperation and, hey, there is a little Core Animation thrown in to boot.

When I was at school I read James Gleick's book Chaos and decided I would write an application on my Dad's PC to draw a Mandelbrot set. I have no idea now what it was written in, it might even have been QuickBasic, but what I do remember is how slow it was. I would type in the co-ordinates and leave it for an hour or so before a small mono-chrome Mandelbrot image the size of a postcard appeared. I was quite pleased with it.

Fast forward by more years than I care to mention and I am the proud new owner of a G5 iMac, looking for a little project with which to get to grips with Xcode and Objective-C. So I decided to write a Mandelbrot screensaver. The results were a lot faster and a lot more colourful than my previous attempt, and I must have spent hours sitting watching the strange and beautiful patterns it produced. But there was a definite pause of between 2 to 5 seconds while the calculations were carried out.

So finally I've re-written it once more for this blog post using NSOperation. In fact we thought it was actually good enough to be released properly as a free screensaver. A cut-down version of the code for this project can be found here (cut-down simply because the actual project contains Sparkle updates and the like) or if you just want the screensaver itself you can get that from the downloads section of our web-site. I won't go into detail about writing the screensaver itself, but if you are interested Brian Christensen's excellent two part article is as good now as it was when I first read it all that time ago.

As anyone who has ever written any multi-threaded application will know, things start to get complicated when different threads need to interact. Traditionally this problem was handled using synchronisation mechanisms such as signalling waiting threads. This is fine until two threads manage to get into a situation where each is waiting on a signal from the other - which usually won't happen until the application is deployed on a client's machine.

Apple have helped us mortal developers get around this problem by introducing the concept of an operation queue. Independent packages of work, operations, are added to the queue and synchronisation is achieved by adding dependancies between these operations; if operation B is dependant on operation A then we are guaranteed that operation A has run to completion by the time operation B starts. Without dependancies, other operations on the queue will execute whenever they get a chance.

This model translates into three Cocoa classes: NSOperationQueue, NSOperation and NSInvocationOperation. NSOperationQueue is responsible for handling the queuing of operations (hence the name - I really couldn't think of a better way to say that). Instances of either NSOperation or NSInvocationOperation or both are placed into the queue using the addOperation: message.

NSInvocationOperation provides the simplest method of adding an operation as no sub-classing is required. Instead, instances of the class are initialised using the initWithTarget:selector:object: method. For example the code:

NSInvocationOperation *pNotifyOperation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(notifyImageComplete) object:nil]
;


will set up an operation that will call the notifyImageComplete method on self to perform its task.

NSOperation on the other hand is designed to be subclassed with an overwritten main method provided to carry out the operation's task.

So let's see how all this fits into the example of the Mandelbrot screensaver.

To display the Mandelbrot image we have three CALayers in the ScreenSaverView derived class: currentLayer, nextLayer and renderingLayer. Calculations are performed in the background and the results rendered to renderingLayer. When the application receives notification that it should refresh its view (via the animateOneFrame method) it checks to see whether this rendering has finished and replaces currentLayer with nextLayer, nextLayer with renderingLayer and renderingLayer with currentLayer. This lets us animate cross-fades between currentLayer and nextLayer without the risk of the rendering changing one of these layers half way through.

Rendering to the CALayers takes place in the MandelbrotImageGenerator class, which is also responsible for handling interactions with the NSOperationQueue and its operations.

In our project we have a MandelbrotDrawingOperation class, derived from NSOperation. The purpose of this class, as illustrated in the figure below, is to render a given portion of the Mandelbrot set into an image buffer. We derive a class (rather than using NSInvocationOperation) as we need a lot of additional information to carry out our task; the buffer to draw into, the buffer size, the visible portion to be drawn, etc.

Picture 1.png


We need to be notified once all the drawing operations have completed and we achieve this using an NSInvocationOperation which has a dependancy on each of the drawing operations. Here is the relevant code (cut down to show only those bits of importance to the discussion).

// Create the final operation to send the notification once everything is complete
NSInvocationOperation *pNotifyOperation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(notifyImageComplete) object:nil]
;
...

for( bottom = 0; bottom < height; bottom += dy )
{
...

// Create the operations to do the drawng
MandelbrotDrawingOperation* pOperation = [[MandelbrotDrawingOperation alloc]
initWithBuffer:(UInt8*)_pImageBuffer + (bottom * width * 3)
imageWidth:width imageHeight:dy
extents:curExtents
colourMap:_colourMaps[colourMap % 16]
]
;

// Make the notification operation dependant on the drawing operation
[pNotifyOperation addDependency:pOperation];
// Add the drawing operation into the queue
[_operationQueue addOperation:pOperation];
[pOperation release];
}

// Add the notification operation
[_operationQueue addOperation:pNotifyOperation];


An important point to note is that the final notification operation is not added to the queue until all of the drawing operations have been added. If the notification operation was added to the queue first it could have executed in a separate thread before we've had a chance to add any of the drawing operations to the queue.

The notifyImageComplete method simply passes on the notification to the main thread. Drawing the image into its CALayer is safer on the main thread as we do not want to risk this memory being released half way through.

-(void) notifyImageComplete
{
[self performSelectorOnMainThread:@selector(notifyImageCompleteOnMainThead) withObject:nil waitUntilDone:NO];
}



During rendering of the image we can periodically check that the operation has not been cancelled by checking the isCancelled property of self, which may be the case if the application is terminating and we are in the process of clearing up. Operations may be cancelled directly. However, in our case we simply want to cancel all currently running operations and wait until they have finished. This takes place in the dealloc method of the MandelbrotImageGenerator class:

[_operationQueue cancelAllOperations];
[_operationQueue waitUntilAllOperationsAreFinished];


So that's it. Although we still need to think carefully about how concurrent operations access the same data, I hope you can see that much of the complexity involved in synchronising this has been removed.

The Serious Business of the Idiot Detector (Part 3)

About 80 days ago Dr. Ivan Egghead's Amazing Idiot Detector went on sale on the App Store for $0.99. In part 1 of this series I explained how this was an experiment to experience selling on the App Store and various marketing ideas. Part 2 of this series showed what happened when we took out an online advert. Now in part 3 we're going to explore what happened when we released a free 'Lite' version to promote the full paid-for version.

The Lite version has just one theme - the one that looks and sounds like a military sonar device. To keep things simple, we only included the accelerometer control mode. The other thing we did was to stick a big full screen advert at start-up to directly promote the full paid-for version. So what happened?

Well, we were very happy with our download stats for the free version. In fact we were pleasantly surprised! We reached No. 1 in the Entertainment section and around No. 4 overall in several European countries. In contrast we also received incredibly bad ratings and pretty vicious comments on iTunes! It seems from the comments that users just didn't 'get' the subtle accelerometer control we had implemented.

There is plenty of evidence from various sources that releasing a free version is a good way to drive sales of a full paid-for product. And it makes sense - what better way to target your marketing than directly to those iPhone users who have shown enough interest in your product to have taken the time to download it! So, what was our experience?

No increase in sales at all. There are probably a couple of reasons for this. Firstly, we pulled the online advert around the same time as we published the Lite version; sales coming from this advert may simply have been replaced by sales from the Lite version. Secondly, if most people hadn't figured out the accelerometer control and thought the application just didn't work, they would not be happy to pay for the full version. Finally there is the fact that it's an Idiot Detector! It's just a bit of fun you might run occasionally for a laugh and if you can get it for free, why would you pay for it?

So what can we conclude?
  • Write software people actually place value in (i.e. not a Idiot Detector!).
  • Make it really easy to use and listen to your users' comments. If they're struggling to understand - it's your fault, not theirs!
  • People like free stuff - but if you're using a free version for promotion, make sure there is still value left in what you're selling.
  • People will be very vocal in their disapproval if you get it wrong and, because their reviews are pretty much anonymous, they can be quite abrasive. Although it may be hard to take, this is the most honest opinion you will receive. If you get it wrong you WILL be told, so get it right first time. A wider beta test would help.
  • The App Store is a huge place. Even with a silly little app like the Idiot Detector we are still attracting several thousand new users a week.
So, the experiment is over. What's next? We've withdrawn the paid-for version from the App Store but we're going to keep the Lite version available for a while. We've listened to the user feedback and are implementing a touch mode to make it easier to control. As a bonus we're also bundling a second theme into the Lite version. If you want to check it out, it is available here on the App Store.