Saturday 20 June 2009

Unethical practises on the App Store

Every iPhone app developer goes through great pains to get their apps noticed and hopefully to break into the charts, which will result in increased sales. Rather than building great apps to entice buyers, the approach some devs take is to release crapware and then to dishonestly fill the App Store with five star reviews that rave about the brilliance of their apps, enticing many gullible users into buying them in the process. Whilst I understand the difficulty that all devs have in promoting their applications, Apple should draw the line at this sort of unethical behaviour.

"Hold on!" I hear you cry. "How is this even possible? You need an iTunes account to place a review and creation of an iTunes account requires the entering of personal details and credit card information!"

Yes, you do need an iTunes account to place a review; however an iTunes account can be created quite easily using a promo code which EVERY developer on the App Store that has published applications can generate for their own applications. Personal details are not verified, so anything can really be entered. New email addresses are easy to generate. It wouldn't take more than a few minutes to generate each new iTunes account. This technique can currently only be applied to the US App Store.

Obviously, it's hard to prove which developers are doing this, however when it's done blatantly it's obvious. Take this developer for example, who has several applications on the App Store that all have suspiciously good reviews and ratings on the US App Store. Clicking through on each of the 'reviewers' reveals that each one has reviewed all of the developer's applications, giving five stars in each case and glowing reviews. The offending applications include My Othello and Reversi Extended (which look to be identical games).

Or course, I'm not saying these reviews were submitted using this trick; however the evidence is quite strong. Furthermore, these implementations of reversi are easily (IMHO of course) the worst on the App Store, yet have the best ratings (4 stars and 5 stars respectively on the US App Store, with 29 suspicious reviews each). The reviews are not true reflection of these implementations.

Hopefully Apple addresses this loophole, or the reviews in the App Store will become worthless. Thankfully, at this point it's only the US App Store that is affected; however, for most developers the US App Store is the most lucrative, so it might become difficult to compete with apps whose rating has been boosted using this technique. Apple, please close this loophole.

Friday 15 May 2009

The trouble with timers

On the iPhone it seems to have become commonplace to use a NSTimer to trigger redraws on OpenGL ES applications when displaying a changing or animated scene. To run smoothly, a frame rate of 30fps or higher is generally aimed for (60fps being the maximum, limited by the iPhone hardware refresh).

Relying on a timer to fire the code that renders the frame has its pros and its cons. The pros are simplicity and having everything in the same thread of execution (or runloop) means not having to worry about concurrency issues. The cons are what I'm more interested in..

The NSTimer class reference states: A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds. If a timer’s firing time occurs while the run loop is in a mode that is not monitoring the timer or during a long callout, the timer does not fire until the next time the run loop checks the timer." Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time.

The low resolution and the fact that a timer can misfire (skip frames) are the most worrying for me.

A frame rate of 60fps means a timer period of 1/60 = 0.016667 seconds (or 16.667ms). What this means is that the frame needs to be rendered well within 16.667ms or the timer will misfire. Flipping buffers when rendering an OpenGL frame can block, depending on the hardware refresh sync, making the likelihood of an overrun (and subsequent misfire) even higher.

What typically happens is the timer fires every 16.667 ms or so (depending on what else the run loop is up to), but for frames whose drawing code has not completed by the next time the timer is meant to fire, the subsequent frame is effectively skipped.


A misfire results in the time between two ticks suddenly being twice the expected period (or possibly a higher multiple of the period if the overrun in the rendering is even higher). Depending on the way in which the animation is implemented in the application, this will usually cause a visible blip or stutter. Regular misfires would make the animation appear jittery and unpleasant.

As most developers simply add the timer to the main runloop, it means that the events need to fire in between whatever other code the main runloop is executing (in particular, user events). This can add inconsistency to the timer period.

Things that can be done to improve this situation (many of which can be done together) are:

  1. Optimise the drawing code so that it comfortably completes within the timer period (16.667ms for 60fps). If it's far under this threshold, then the other things happening in the runloop shouldn't regularly push it over the threshold.
  2. Change the animation code so that it does not expect a regular period and animates the scene based on how much time has passed since the last animation. This is not always feasible - see this time-based animation tutorial.
  3. Set the timer on a seperate thread (not the main thread).
  4. Use a thread with its own custom loop rather than a timer. This in itself is not a solution if the rendering regularly exceeds the desired frame rate period, however can improve the smoothness of the animation when combined with some of the other things in this list; it also gives you greater control over when to draw the next frame when the previous frame has overrun, instead of being forced into multiples of the timer.
  5. Increase the timer period and only render the scene when an appropriate amount of time has passed (oversampling).
  6. Decrease the frame rate; however it can only be decreased so far before it starts to look bad.
  7. Decouple the drawing from code that is not directly related to rendering (such as physics simulation or animation logic) - see this blog on fixing the animation timestep for a good overview of this approach.
  8. Synch to the hardware refresh. This is typically what console games do and would be the best approach in many situations. Unfortunately this not currently possible on the iPhone as no hook or callback is provided by the underlying APIs.

My preferred approach is a combination of 1, 2 and 4. If option 8 was available I would go for a combination of 1, 2 and 8.

Tuesday 12 May 2009

Rise of the crapplication

There has been chatter of late in the iPhone developer forums that I frequent regarding the prolifation of crap applications (crapplications, or simply crapps) in the App Store and that the App Store model encourages this. Could this really be true?

Like many others, I have a full-time job and just develop iPhone applications in my spare time. But that does not mean it costs nothing to develop my applications. On the contrary - my spare time is very valuable to me - but how do I quantify it? If you assume that I did some other work, say contract work, over that time then the opportunity cost of this lost income can be determined.

With this in mind, I decided to examine one of my applications, Magnetic Block Puzzle to see what the cost of developing it was and what sort of sales I would need to achieve to break even in a reasonable period of time.

Magnetic Block Puzzle deconstructed

I estimate that it took around 200 - 300 hours to develop, from start to release-ready code. In order to calculate an opportunity cost for this time, I need to determine an hourly rate. Conservatively I am using a rate of £30 per hour. That equates to an opportunity cost of £6,000 to £9,000. For the rest of this exercise, I will use the average of £7,500 (about $11,500). Expenses incurred in developing the application have been ignored.

So, how many sales are required to break even?

TierPrice ($)Price (£)Pre-tax profit (£)Req sales
Tier 10.990.590.3620,833
Tier 21.991.190.7210,417
Tier 32.991.791.096,881
Tier 43.992.391.455,172
Tier 54.992.991.824,121
Tier 65.993.492.123,538
Tier 76.993.992.433,086
Tier 87.994.993.042,467
Tier 98.995.493.342,246
Tier 109.995.993.652,055

I need to sell 10,417 copies at its current price of £1.19 ($1.99) just to break even! How many sales are required then, per day to break even in six months?

TierPrice ($)Price (£)Pre-tax profit (£)Req sales per day
Tier 10.990.590.36114
Tier 21.991.190.7257
Tier 32.991.791.0938
Tier 43.992.391.4528
Tier 54.992.991.8223
Tier 65.993.492.1219
Tier 76.993.992.4317
Tier 87.994.993.0413
Tier 98.995.493.3412
Tier 109.995.993.6511

57 copies a day at the current price doesn't sound too bad until you consider that it needs to maintain this level for six months and that on its best sales day it hasn't come close to this amount. It's looking unlikely that I will break even on this any time soon, if ever.

What are the options then? Raising the price requires fewer sales to break even, but raising the price will result in a decrease in sales too, so this is unlikely to help.

Perhaps lowering the price to 59p ($0.99) will help boost sales? It probably will, but at that price the required number of sales to break even shoots up to 20,833 or 114 a day for six months.

The only other option is to lower the development cost. In other words, spend less time developing it in the first place. Too late for this application, but valuable information to take to the next one. Knocking off 10% or 20% is not going to change the situation much. The decrease needs to be significant (say, an order of magnitude). Putting 20 - 30 hours of effort into building an application means an opportunity cost of around £750, which can more realistically be recouped through App Store sales in an acceptable period of time. But what can be built in 20 - 30 hours? Ah, a crapplication.

Pricing

The above logic of course assumes that a crapplication will sell as well as something that took ten times longer to develop. Unfortunately, the history of the App Store leads me to believe that you are no more certain to sell something good than you are to sell something crap. If it appeals to the masses, it will probably sell, irrespective of how bad or how good it might be (this is the best example of the former).

A popular story that is quoted is the developer that decided to see what could be built in an hour; came up with Sound Grenade in 20 minutes and landed up cracking the App Store top 10 with it.

The blessed / dreaded charts

To break even many sales are required at a low price, or fewer sales at a higher price. In pre-App Store days people would happily pay £5 to £10 ($7 to $15), or even more for a mobile application. Ringtones and images were confined to the price brackets below this. The App Store has changed everything. Customers now expect to be able to buy just about anything for 59p ($0.99). It wasn't always like this - I remember the first few apps on the App Store being more costly. Didn't I purchase Super Monkey Ball and Enigmo for £5.99 ($9.99) each? Yes, I did. What happened?

This slide can arguably be attributed to the App Store charts. The charts are just based on downloads, irrespective of cost. So, a £5.99 ($10.00) application would need 10 times the turnover in order to compete with a 59p ($0.99) application in the charts. Of course, this is a losing battle. Surely charts should take the price into consideration? As it stands, it has clearly become a volume game.

Getting a spot in the App Store charts is essential for every developer. The charts tend to be self-perpetuating. Once an application is in there, it is noticed by more people and therefore downloaded more, boosting its position in the charts, leading to even more people noticing it and so on.

It seems then that developers have little choice but to charge 59p ($0.99) or £1.19 ($1.99), or at a push £1.79 ($2.99) and hope they crack the charts. If they don't, the application is quickly confined to the abyss of unnoticed applications (AUA), picking up the odd few sales here and there, but ultimately not very many.

Summary

It would seem that the best return on investment is to spend as little time as possible building an app, try to give it mass appeal and price it at tier 1 (59p or $0.99). Perhaps then building iPhone software with the aim of making money is viable.

If like me you have spent a significant amount of time building applications, for very little reward, console yourself with the fact that many of us are in the same boat. Let the enjoyment of building the application be your reward and the sales (if there are any) be a nice bonus.

Wednesday 22 April 2009

New iPhone puzzle game

Magnetic Block Puzzle is now available on the App Store for a small amount.

It's a departure from my two previous games, Reversi and Merelles, which were both abstract strategy games. Magnetic Block Puzzle is, as the name implies, a puzzle game. The aim of each of the 110 puzzles is to join the coloured blocks, which are special magnets that stick to other blocks of the same colour, by tilting the puzzle to make the blocks move.

The process of building this game, from inception to completion, went something like this:

I started out this project by building the underlying model of the blocks and the board, including the logic to shift the blocks around. This was done in a test-driven manner, which helped to drive out the model and behaviour correctly and to identify edge and corner cases early. Towards the end of this process, I discovered a similar puzzle game on clickmazes.com and contacted them to ensure I wouldn't be violating a copyright. They were happy for me to proceed and gave me permission to use two of their puzzles too.

The next step was to build a solver and a puzzle builder application on top of the model, to allow me to design and test puzzle layouts. This was done as an iPhone application and many puzzles were designed on my iPhone whilst I was riding on the tube :)

Once enough puzzles had been designed, I tailored the solver to determine various things about each puzzle such as the number of solutions, the number of moves in the best solution, the number of useful positions in the solution tree (positions from which the puzzle can be solved), the number of dead-end positions (positions from which the puzzle cannot be solved), and so on. This proved to be quite CPU-intensive on the more complex puzzles, so it was compiled and run on my Mac rather than on the iPhone. These values were then used to assess the relative difficulty of each puzzle.

So, every puzzle included in the game was designed by hand, but assessed and verified by code.

Finally I wrote the actual application, including in it the 110 puzzles that had been selected for the final application, in a local SQLite database. Like the solver and the puzzle builder, the actual game was built on top of the model built initially.

I started this application in early December 2008 and finished it in late April 2009 - nearly five months of much of my free time went into building it. I am happy with the end product and hope that those who purchase it enjoy playing it as much as I enjoyed creating it. It's available on the App Store now.

Wednesday 15 April 2009

More responsive sliders on the iPhone

One of the best things about the iPhone is you can have thin fingers or fat fingers, and pressing buttons and using controls on the touch screen is still quite easy. On old-school touch screen devices (WinMob phones in particular spring to mind), one had to be very precise as the first location touched on the screen immediately triggered a touch event, which is why a stylus was almost always necessary. The iPhone takes a slightly different approach, where a touch that consists of many points on the screen (as would occur with a finger) is converted into a co-ordinate through some sort of averaging of all the points. In general this works really well. However, using this approach still requires the controls on the screen be sufficiently large that a user will be able to put a finger over it, with the average falling comfortably on the control. From my experiments, I believe a control needs to have a touch area of at least 32x32 pixels.

Unfortunately, some of the standard controls suffer from having a touch area that is too small and subsequently difficult to press. In particular, when creating an info type UIButton with the Interface Builder the dimensions and bounding rect are set to 18x19, which is too small. This can be fixed by changing the bounding rect through code, but not in the interface builder.

I recently ran into a similar issue with a slider control.


When creating a UISlider with the Interface Builder, it is given a fixed height of 23 pixels, which is too low. More troubling is the thumb control in the slider, whose size is not exposed, but is around 20x20 and is too small for the slider to be comfortably moved. I found with a horizontal slider slid all the way to the left or all the way to the right, it would be difficult to move the thumb control off the edge with my finger - and I have fingers that are relatively slender. The bounding rect of the slider and the touchable area of the thumb control are as follows:


Unfortunately, simply changing the bounding rect on the control was not a solution in this case as it simply makes the slider bigger and does not change the touchable area of the thumb control. I tried all sorts of workarounds to make the slider more responsive and eventually settled on extending the UISlider control and overriding some behaviour with the following two-pronged approach (the code follows at the bottom of the post):


  1. Override the pointInside method to make points slightly outside of the control still appear to be inside the control (without changing the size of the actual slider). I decided to extend the touchable area by 10 pixels on either side of the control and 8 pixels above and below it.

  2. Override the beginTrackingWithTouch method, which determines whether the user has clicked on the thumb control, to start tracking. This requires determining where the thumb control is based on the current value, then determining if the user's touch was close enough to it for tracking to begin. I effectively increased the thumb control size to 40x40.

The new effective bounding rect of the slider and the touchable area of the thumb control are now as follows:


To use the code below, use the Interface Builder to drop a slider on the screen and then change its class from UISlider to MySlider. It only supports horizontal sliders, the beginTrackingWithTouch method would need to be changed or extended to use it with vertical sliders.

Granted this is a hack, but until Apple sorts out this issue, I'll continue to resort to this sort of thing. I'm surprised it hasn't been sorted out already. I've seen forum posts from people having this problem with the info button, but none as yet with sliders. Am I the only one that sees this as an issue?

MySlider.h:

#import <UIKit/UIKit.h>

@interface MySlider : UISlider {
}

MySlider.m:

#import "MySlider.h"

#define THUMB_SIZE 10
#define EFFECTIVE_THUMB_SIZE 20

@implementation MySlider

- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent*)event {
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, -10, -8);
return CGRectContainsPoint(bounds, point);
}

- (BOOL) beginTrackingWithTouch:(UITouch*)touch withEvent:(UIEvent*)event {
CGRect bounds = self.bounds;
float thumbPercent = (self.value - self.minimumValue) / (self.maximumValue - self.minimumValue);
float thumbPos = THUMB_SIZE + (thumbPercent * (bounds.size.width - (2 * THUMB_SIZE)));
CGPoint touchPoint = [touch locationInView:self];
return (touchPoint.x >= (thumbPos - EFFECTIVE_THUMB_SIZE) && touchPoint.x <= (thumbPos + EFFECTIVE_THUMB_SIZE));
}

@end