Saturday, November 7, 2009

Xcode + iPhone SDK: The unsung hero of 100,000 apps

March 6 2008, Apple covered their roadmap for the iPhone SDK: http://www.apple.com/quicktime/qtv/iphoneroadmap/

That was JUST 20 months ago. Unbelievable.

Since then we've all gotten caught up in the AppStore hysteria, overnight millionaires (maybe), 100,000 apps and average Joes (or Janes) developing apps for the iPhone. This is a positively enormous change for mobile application developers, traditional Mac developers and mobile entrepreneurs.

Xcode and the iPhone SDK make all this possible. It's not just a toolkit to allow you to build apps for the iPhone, but an incredibly well crafted suite (lets not forget Instruments) that is designed to allow developers to do what they do best: develop something to meet their business problem.

Everything about Apples developer tools and Frameworks scream "focus on what you're trying to achieve with your software, we'll take care of all the mundane stuff". 1 button is all it takes to get your software compiled, installed on a device and running in debug. This doesn't come as a surprise to any of the 'old school' OS X developers, but sometimes we need to be reminded about how awesome this toolkit is and how much we take it for granted.

The economist in me continually nags that any time I waste messing around with development tools is time I'm not spending on making my software better. If Xcode and the iPhone SDK were pigs to work with, then we'd still have software, there just wouldn't be as much and it wouldn't be as good.

You could even go so far as to suggest that the iPhone SDK has created a significant competitive advantage and barrier to entry in the now hotly contested market of third party application development for mobiles. The iPhone SDK has a long and rich heritage rooted deeply in the NeXT era, it's not something you can just knock out in a few months.

Sure, there will be SDK's for those other devices, but will they be as good? I guess time will tell, but I wouldn't bet on it.

Tuesday, September 22, 2009

OmniStats, Gruber and 10.4 Support

John Gruber of Daring Fireball has a great discussion (as always) on Mac OS X major version use:

DF Stats

Omni

Adium


The 10.4 usage (36.4%) for Omni seems very high and developers might factor this into their decision to continue supporting 10.4. However it can be argued that Omni stats are skewed due to the Apple bundling agreements that existed while 10.4 was being shipped (these agreements ended before or early in the 10.5 era). All Mac hardware shipped with OmniGraffle during 10.4 (but not during 10.5) so the overall market share of 10.4 is probably lower.


If you're a developer (and according to Gruber 11.2% of his readers are) you might not be fortunate enough to be calling your own shots and have some 'platform agnostic' marketing guy or product manager telling you that you must support 10.4 because it's a significant portion of the market or your partners need it.


Supporting 10.4 is probably a bad move for most developers at this point (unless developing for education - maybe). Here's how to argue the case for 10.5+ against someone not fluent in the Mac platform.


1. Your software won't be as good if you're developing for 10.4

The economic problem is always the same - limited resources and unlimited needs. Whilst you can work around 10.4 limitations, doing so means you've got less resources allocated to other things - like solving your business problem better. There's some really cool tech in 10.5 - from the big stuff like Core Animation and NSOperation to the small stuff like gradients and bezier paths with rounded corners. Your software will almost always be better from using these newer technologies.


It's not just the development effort either, you need to QA and support 10.4 too. That really sucks for the poor schmucks who have that job (probably one of whom is you).


Make the choice: Do you want to make good software for 10.4+ users or great software for 10.5+ users? If you're still thinking you can do both, then re-read the above until you realise by definition you can't. Your software will always be better if you develop for 10.5+ because you'll have more resources to spend on solving your business problem better or making your software easier to use.


For many products, you'd get more users from better software than you would from legacy OS support, but that argument really hinges on how many users (particularly paid ones) you're likely to get from supporting an older OS, so market share stats might be important.



2. Current market share

There's no definitive source of how much of the overall market is 10.4 or 10.5+. Omni's stat (36.4%) are probably skewed towards 10.4 due to the old Apple OEM deal and Adium's stats (15.3%) are probably skewed towards an 'early adopter' crowd who stick with the latest OS. So let's assume the overall 10.4 market share is between 20% and 30%.



3. Future market share

By the generous Omni stats, 10.4 is showing a roughly linear decline from 50% on 1 Jan 2009, translating to a decline of 18% per annum. In a year from now, 10.4 will be really inconsequential. You'll need to make back all of your extra costs from these users the next year*, or you're at a loss. This isn't just the explicit costs like development, QA & support, it's the implicit costs too - like your software not being as good. Add to that the future costs of updating to newer technologies in 10.6 like Grand Central that are automatic if you use NSOperation (which is included in 10.5).


*If you're at the stage of deciding whether to support 10.4 or not, then it's probably a few months before your software ships anyway so you need to take into account what the market will be when you launch the software.



4. The OS market share argument is rubbish

Just like the Windows vs Mac market share rhetoric, the total market % isn't important anyway. What's important is the market share of users in your target / addressable market.


Think about it… Any consumer** using 10.4 is probably too frugal to buy Apple's software, a late adopter or using an old system. Do you really think they are going to install and (more importantly) buy your software?


**You'll notice the emphasis is on consumers. There are still some big educational customers with 10.4. If this is the case for you, then you should probably take the decline in market share into account and make an assessment based on that. You might still be stuck with 10.4 for a while longer, but make an informed choice.



But what if I already support 10.4?

This stuff is all still relevant. If you're keeping your software up to date, then you'll probably realise a lot of this already.

As Wayne Gretzky said "skate to where the puck is going to be", this is still relevant to all of your current and future development efforts.



Conclusion

There are some 10.4 users and some of them might buy your software, but are there enough to regain the costs, time and headaches of developing for 10.4 worthwhile?




Sunday, August 23, 2009

Coolest thing I learnt at WWDC 2009

Cocoa's support for KVO and Properties is awesome, but every now and then I find something I can't do. I frequently use 'derived' properties, where a read only property is derived from a combination of instance variables. For example, we might have an object with firstName and lastName, then want a KVO compliant fullName.

Cocoa did offer some support for this in your own classes by doing a willChangeValueForKey: and didChangeValueForKey:. The real challenge comes when you want to have the derived accessor in a class category in the app layer where the base class is in a framework. The framework base class has no knowledge of class extension in the app layer, so can't post the willChange/didChange, so your derived accessor is not KVO compliant.

Let's look at how things used to work with a basic example using firstName, lastName, fullName in our own object.

Header...

#import


@interface Person : NSObject {

NSString *firstName;

NSString *lastName;

}


- (void)setFirstName:(NSString *)value;

- (NSString *)firstName;


- (void)setLastName:(NSString *)value;

- (NSString *)lastName;


- (NSString *)fullName;


@end



Implementation....

#import "Person.h"


@implementation Person


- (void)dealloc{

[firstName release], firstName = nil;

[lastName release], lastName = nil;

[super dealloc];

}


- (void)setFirstName:(NSString *)value{

if ([firstName isEqualToString:value]) return;

[self willChangeValueForKey:@"fullName"];

[firstName release];

firstName = [value retain];

[self didChangeValueForKey:@"fullName"];

}


- (NSString *)firstName{

return [[firstName retain] autorelease];

}


- (void)setLastName:(NSString *)value{

if ([lastName isEqualToString:value]) return;

[self willChangeValueForKey:@"fullName"];

[lastName release];

lastName = [value retain];

[self didChangeValueForKey:@"fullName"];

}


- (NSString *)lastName{

return [[lastName retain] autorelease];

}


- (NSString *)fullName{

return [NSString stringWithFormat:@"%@ %@",self.firstName,self.lastName];

}


@end



I presented this quandary to an Apple engineer at WWDC 09 and after a few minutes he found this in NSKeyValueObserving.h and became my new hero. (I love WWDC for just this reason... the guy saved me an unbelievable amount of work).

/* Return a set of key paths for properties whose values affect the value of the keyed property. When an observer for the key is registered with an instance of the receiving class, KVO itself automatically observes all of the key paths for the same instance, and sends change notifications for the key to the observer when the value for any of those key paths changes. The default implementation of this method searches the receiving class for a method whose name matches the pattern +keyPathsForValuesAffecting, and returns the result of invoking that method if it is found. So, any such method must return an NSSet too. If no such method is found, an NSSet that is computed from information provided by previous invocations of the now-deprecated +setKeys:triggerChangeNotificationsForDependentKey: method is returned, for backward binary compatibility.


This method and KVO's automatic use of it comprise a dependency mechanism that you can use instead of sending -willChangeValueForKey:/-didChangeValueForKey: messages for dependent, computed, properties.

You can override this method when the getter method of one of your properties computes a value to return using the values of other properties, including those that are located by key paths. Your override should typically invoke super and return a set that includes any members in the set that result from doing that (so as not to interfere with overrides of this method in superclasses).


You can't really override this method when you add a computed property to an existing class using a category, because you're not supposed to override methods in categories. In that case, implement a matching +keyPathsForValuesAffecting to take advantage of this mechanism.

*/


In a nut shell, all you have to do is add a class method and return the key paths for the value that affect your derived accessor....

+ (NSSet *)keyPathsForValuesAffectingFullName{

return [NSSet setWithObjects:@"firstName",@"lastName"];

}



So the implementation can be simplified to this...

#import "Person.h"


@implementation Person


@synthesize firstName, lastName;


- (void)dealloc{

[firstName release], firstName = nil;

[lastName release], lastName = nil;

[super dealloc];

}


- (NSString *)fullName{

return [NSString stringWithFormat:@"%@ %@",self.firstName,self.lastName];

}


+ (NSSet *)keyPathsForValuesAffectingFullName{

return [NSSet setWithObjects:@"firstName",@"lastName"];

}


@end



And the header this....

#import


@interface Person : NSObject {

NSString *firstName;

NSString *lastName;

}


@property (retain) NSString *firstName;

@property (retain) NSString *lastName;

@property (readonly) NSString *fullName;


@end



The moral:
- Much less code
- Can do it in a class category

Only downside is 10.5+ only, but you shouldn't be worried about 10.4 by now right :-)

Friday, March 13, 2009

Wheels for the Mind

Mathieu Tozer (from Plasq) and I were interviewed for Wheels for the Mind magazine about being Australian Mac developers. Both of us received Scholarships from the AUC to attend WWDC in 2006.

Monday, March 2, 2009

Cheeky Cocoa

Pushed a nil error to [NSApp presentError:] and it resulted in this console fart:

"-[NSAlert alertWithError:] called with nil NSError. A generic error message will be displayed, but the user deserves better."

Translation: "Don't be such a lazy prick. I'll deal with it, but I'm not happy about it."

Tuesday, January 13, 2009

Macworld Memeo Share Review

Rob Griffiths stopped by the Memeo Share booth at Macworld last week and very promptly posted a couple of reviews...

- Macworld (podcast - iTunes) skip through to 12:10 if you're interested

We did a number of interviews, but no one is quite as quick as Macworld!

Sunday, December 21, 2008

NovaMind Review

Macworld gave a great review of NovaMind (particularly the UI) last week.  

The part that I found particularly interesting was this....

"Tools for customizing your mind map are abundant and deep, yet they don’t overwhelm. The many palettes can be docked to the working window or dragged out to float independently. Individual palettes can be collapsed to just a title bar, or removed all together. Click a small button to see a palette’s advanced options and it flips over in a cool Quartz animation. The net result is an interface that is remarkably clean and easy to use despite the large feature set."

We had a continuous problem at NovaMind - how do you add more features without increasing product complexity.  I don't think this is specific to NovaMind, in fact it's a common software engineering problem.

The big surprise is that (according to the article) we actually achieved what we set out to achieve.  Excuse my skepticism, but it's very rare in software engineering that you get to do this.  Usually you come out with something that's either almost there, or not even close but a way awesome bi-product.

Anyway, we did this with NovaMind by:
- Group like controls into separate groups in a separate space (a palette)
- Group those groups by changing the background color of the palette
- Only show palettes that are relevant to the selected item on screen
- Allow the user to customise it...
- User can collapse palettes to hide stuff they don't use
- User can reorder the palettes into the order they like
- User can have the palettes 'docked' in their document window or floating like an inspector
- Making the interface reveal advanced or low used features show up only when you need them.  (We did this by 'flipping' the palette to show advanced features).

The resulting interface was this...




BIIIIG thanks to Keith Lang for his help (or rather complete and total input) in creating the quartz composition for the flip.