Encode what cannot be NSCoded

Isn’t it annoying that UIImage can’t be serialized (as evident with the error “UIImage unrecognized encodeWithCoder”).  Really throws a spanner in the works when trying to serialise your UI (or applying this hack) given that images are everywhere…

Well this can easily be fixed with a monkey-patch, and a pretty safe one at that.

Here it is (just throw this in a “.m” file, and include it in your project):

@implementation UIImage (NSCoder)

- (id) initWithCoder:(NSCoder *)coder {

	self = [self initWithData:[coder decodeObjectForKey:@"PNGdata"]];

	return self;
}

- (void) encodeWithCoder:(NSCoder *)encoder {

	// if space is a concern, replace with UIImageJPEGRepresentation

	[encoder encodeObject:UIImagePNGRepresentation(self) forKey:@"PNGdata"];
}

@end

So there you go!  This hack was brought to you by the people behind GPS Log for iPhone (please check it out, it’s free to try!).

Why do I claim that this monkey-patch is safe?  Well it encodes and decodes your image. The method is doing what it is contracted to do.  *If* UIImage were to implement this method, then you would have issues, but *only* if you serialized with one implementation, and deserialized with another (which wouldn’t normally happen, and it’s a pretty big *if* anyway…).

Why is it a “monkey patch”?  Well you’re throwing your own code into UIImage implementing standard methods…  pretty much the text-book definition.

Be aware… serialising images to PNG format will consume a decent amount of memory and disk space, and can be slow, especially if those images are big.  So if you already have these images on disk somewhere else, then you probably should be loading them from disk rather than doing this (I have a feeling this is why Apple did not implement this method, to force people to load from disk).

So why is this method useful?  Well for one, you can use it to Copy what cannot be NSCopied :)

Copy what cannot be NSCopied

Sometimes you need to deep-copy an object in Objective-C.  Normally you just go newObject = [oldObject copy].  But what if oldObject does not support NSCopying (typified by the error “copyWithZone: unrecognized selector”)?

Well.. if your object supports NSCoding, then you can simply serialize it, and deserialize it!  Saves you having to monkey-patch the copyWithZone method (though may be a tiny bit less efficient at runtime).

How can do you perform this voodoo magic?  just one line of code…

newObject = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldObject]];

NB. (for iPhone programmers) ‘newObject’ is autoreleased, so make sure you retain it somewhere!

There you go.  This hack was brought to you by the people behind GPS Log for iPhone (please check it out, it’s free to try!).

Where do I use this?  Well I have some UITableViewCell objects that I defined in an XIB file, but I need lots of copies (this is a pretty common use-case, which the SDK does not handle very well at all).  Instead of sticking the object in it’s own XIB file, and loading it from the XIB each time (as is one workaround), I use the trick above :)  And it’s more more efficient (object is copied in memory, rather than loaded from flash-storage).

In fact, many UI elements don’t support copying, but they do support serializing, so this can be pretty useful. For UI elements, you may need to add this monkey-patch to get UIImage to play nice.

11 Mar 2010, 1:39am

leave a comment

iPhone dev – forcing the user language

From my post on StackOverflow

You can force the language like so:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"en", nil] forKey:@"AppleLanguages"];

And undo this setting by:

[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];

Consider if you need to call [[NSUserDefaults standardUserDefaults] synchronize];

This change is persistant and only needs to be set once.

NB. you will normally have to restart the app for this to take affect.

I agree there is little need to allow the user to specify a language. However the one exception is being able to override the language and set it to the developer’s native language. If the user can speak the developer’s language (e.g. English for me) then they may wish to use the App in that language, if the translations are incorrect.

I reference this answer: http://stackoverflow.com/questions/1669645/how-to-force-nslocalizedstring-to-use-a-specific-language/1670524#1670524 (the answer doesn’t actually work for me, but following the ideas in the comments did. The undo stuff I worked out.

26 Feb 2010, 6:06pm

leave a comment

Slide to reload

I love Tweetie2′s slide to reload feature.

Maybe one day I will use a similar device, fortunately Dr. Touch has an example.

26 Feb 2010, 6:05pm

leave a comment

iPhone Mutex/Sempahore

Looking for Mutex and Semaphore in the iPhone SDK?  The classes you want are NSLock and NSConditionLock.  You can use NSConditionLock just like a semaphore.

Here’s a good NSConditionLock example

For Mutex’s, use @synchronized(self), or another NSLock object.

26 Feb 2010, 2:05pm

leave a comment

How to make pull to reload tableview like Tweetie 2

I really like this feature in Tweetie 2, very innovative. Well if you ever need to do something similar, you can. Here’s how

14 Feb 2010, 8:52pm

leave a comment

Like This App? Rate it…

MobileOrchard has some great ideas regarding fighting the negative App-Store bias (caused by the ‘rate on delete’ feature).

I tweaked their code a little, localising it, and making it ask a second time if the user clicks ‘No Thanks’ the first time (in case they were simply busy).

My advice would be to make sure you put the dialog in a place where the user isn’t busy doing this.  For GPS Log I don’t ask this immediately on launch, as most people launch when the need to log a location, and rating an app is the last thing on your mind.  Instead I ask it after the user shares some entries. I figure at this point they’re just at home, or somewhere relaxing. I only show it to people who have bought the software too…

This guy has some tips on getting the review URL.

Basically it boilds down to this:

http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=30000000&pageNumber=0&sortOrdering=1&type=Purple+Software&mt=8

Where 30000000 is your App ID.

I have no idea what ‘Purple+Software’ is, but trying to replace it didn’t seem to help… so I just left it in.

Update (2011-01-15)
More info on iTunes links can be found here.

An alternative format for the review links (which I have not tested) is:
itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=337064413

Promo code links can also be created – what a great idea!
e.g. https://phobos.apple.com/WebObjects/MZFinance.woa/wa/freeProductCodeWizard?code=RWAPJ7XLTHN7

22 Dec 2009, 2:25pm

3 comments

Kidney Watch 2.0

After some talking with Mr Spiff in Feb this year, and copping some harsh criticism on the last method, it became apparent that work was needed.

Firstly – accuracy. It is hard to get your address in places like Asia and Latin America (generally the places where you need it the most). Chinese addresses are über-long, but at least they have them. In Peru? Forget about it! Even the taxi drivers can’t find addresses that are printed on Peruvian business cards.

GPS fixes this, with your position down to 47m on average, after 30 seconds. More than enough to guide the cavalry (green smoke would help too – but doesn’t fit in your pocket, and may raise questions on a date). The iPhone GPS can even work without a data connection (handy also if you are lost in the bush).

GPS Log now has an export to SMS option that allows you to send your GPS position to your friend by SMS. I have also created a stand-alone product focusing on this feature (this is the product that evolved out of the stand-alone “kidney watch” product ideas). http://gpsme.cc/ (if you have GPS Log, no need to try this one)

This is actually a very useful, potentially life-saving feature. Mum tells me the sad story of a guy who died in Queensland when he got lost in the bush. He had cell-phone signal and phoned 000 nine times but they couldn’t find him. 2 floats that could save your life…

The second, more concerning feature is verification.

i-think22 proposes duress words. My only problem with that is that if you are under duress you may not be able to reach your phone. I prefer the Dead Man’s Switch approach where if you don’t perform an action in a fixed timeframe, the cavalry roll in. Of course, both together may provide even more optimal coverage.

The issue with the Dead Man’s Switch is the possibility that your antagonists may intercept the signal and duplicate it (e.g. Speed), or that you may accidently forget to activate the switch, and be rather red-faced when the cavalry roll up.

I would like to see a customised version of the SMS feature, designed for Kidney Watch. So tell me – what should be in it?

13 Nov 2009, 7:44pm

3 comments

dSYM Archive Script

Archiving dSYM for production builds is a good idea.

An easy way to do it is to add a build script at the end of your build.

in XCode->Targets->YourBuildName
right click, and select Add -> New Build Phase -> New Run Script Build Phase

and copy in this script. ArchiveDSYMScript

Repeat for all targets. Ensure the order is after the ‘Link Binary With Libaries’ task.

NB. This excludes “Debug” and “Release” build types, as I use “Distribution” and “Distribution Ad-Hoc” for my distribution builds. Easy to change, if you need.

13 Nov 2009, 6:46pm

1 comment

Analysing iPhone Crash Dumps (OS 3.0)

If you are sent a crash dump by the Approval Team, download it through iTunes Connect or some other way you need to be able to interpret them into something usable. Here’s how:

Run
/Developer//Platforms/iPhoneOS.platform/Developer/Library/PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/symbolicatecrash xxxx_2009-11-12-140756_iPhone.crash > crash_with_symbols.txt

This is the difference in knowing the exact line of code that crashed, and knowing it crashed. Make sure your have your dSYM files though – read on.

Useful options for the script are -v (verbose) and -A (just search for Application symbols).

HOWEVER: You MUST keep the dSYM file for every build you do. Even doing a rebuild off the exact same source code produces a dSYM file that is not compatible with the script. So archive those dSYMs!!!

Check out my automatic dSYM script here: dSYM Archive Script