I recently shipped a version of PicStroom with a dreaded “unrecognized selector” bug. It was caused by a sloppy mistake and not enough testing of the app on the iOS4 platform. In my case, not realizing that an animated argument on a method had just been introduced in iOS5 – it was in a rarely used area of the codebase, but of course users found it quickly.
This problem got me thinking about the whole area of bugs that only appear at runtime.
To recap, the “unrecognized selector” exception is thrown when you call (usually dynamically) a method on an object that does not exist. But interestingly, the objective-c runtime doesn’t actually crash when you call the non-existent method – that is just the default behavior that is inherited from NSObject.
A simplified outline of the process when objective-c calls a method is:
forwardInvocation method on the object.forwardInvocation implementation that the NSInvalidArgumentException exception is actually generated and thrown from.Its this second chance offered by the forwardInvocation that was interesting. By implementing it in my objects and redirecting the bad method calls to a harmless doNothing method I was able to trap calls and prevent the runtime crash.
The methodSignatureForSelector is just some plumbing1 that allows me to redirect the method selector away from the non-existing method and towards the new existing doNothing method.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
SEL nothingSEL = @selector(doNothing);
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:nothingSEL];
return sig;
}
- (void)forwardInvocation:(NSInvocation *)inv {
}
- (void)doNothing {
}
This approach is very well supported by the objective-c runtime2. No exception is raised as all that is called now is an existing doNothing method which harmlessly runs and – well – does nothing.
I’ve put together a quick XCode project to demonstrate this technique, which you can grab from GitHub at https://github.com/dglancy/MissingSelectorDemo and here is a quick screenshot of the demo running:

I haven’t tried this approach in any production applications yet, but it is certainly an interesting tool to consider for the future – albeit, its no substitute for proper testing of an app.
Extracting it out to an objective-c category is an exercise for the reader.
Use of methodSignatureForSelector explained at http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html
Full documentation from Apple is available http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html.
↩One of the challenges of the Apple app store is that its difficult to open a communications channel with your users. For the most part, they remain unknown to you. At best, they either contact you via your support e-mail or leave feedback on the app store.
When we were building PicStroom, we wanted to give a feedback mechanism that was fluid (read: in-app), but as we were pressed for time, was quick to implement. And of course, totally opt-in/optional for users.
As we were using, Flurry Analytics to do basic stats monitoring of application use, it was the obvious platform on which to build upon.
What we decided to capture is a “happiness score” with values:
We also allow for an text field for optional free form comments.
Implementation details
Flurry allows for the storing of simple key-value pairs, so I piggy-backed on that.
First step is to ensure that Flurry is setup in the normal way. You can get detailed instructions from Flurry when you sign up at their website.
For PicStroom the setup was to set the following when the app starts up (in your App Delegate) :
[FlurryAnalytics startSession:@"<flurry-supplied-app-key-here"];
NSString *randomUserId = [[NSProcessInfo processInfo] globallyUniqueString];
[FlurryAnalytics setUserID:randomUserId];
Next, create an enum that represents the users happiness score.
typedef enum UserHappiness {
UserNoHappiness, UserHappyOutstanding, UserHappyILikeIt, UserHappyCanBeBetter,
UserHappyIWontReturn
} UserHappiness;
Then grab the user comments from the UITextField (called commentTextView in my application), if they have made any, into an NSString:
NSString *comments = self.happinessView.commentTextView;
Now, all you need to do is send the info to Flurry for safe keeping and analysis later:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:@"%d", userHappiness], @"LVL",
[comment length] > 0 ? comment:@"None", @"USER_COMMENTS", nil];
[FlurryAnalytics logEvent:FLURRY_HAPPY_LEVEL withParameters:dictionary];
That’s it!
Weather Channel in PicStroom
The design by Jeroen was influenced by 37signals customer support happiness report. This is what the finished effort looked like to users of PicStroom:

The user can also leave a short message for us as part of their feedback:

The outcome
It has been a great success with a much larger amount of engagement via our in-app feedback component than via the app store. We publish our results on our website as The Weather Channel.
Flurry have a web services API that allow you to query and analyze your data.
The future
It would be great to see more robust and adaptable components built out in the future. I considered building one, but got side-tracked.
If you build an in-app feedback channel into your app, I’d love to hear of your experiences.
Catch me on Twitter at @dglancy

Focused on new sharing options that users have most requested:
Some new options on managing the floating toolbar, it no longer needs to float if you don’t want it to:
PicStroom’s image cache is now moved out of iCloud’s radar so it won’t waste your valuable cloud storage.
Final polish has been applied to the next version of PicStroom; its been submitted to Apple. Look out for some great new sharing options. @picstroom
PicStroom was reviewed on iPad Today #97 as Leo Laporte’s app of the week.
As the developer, its a bit nerve racking having your app reviewed live in-front of such a large viewership; but of course PicStroom held up well!
See PicStroom in action, its history, and learn some Dutch – it’s a fun watch (4:23).
Parsing HTML is difficult at the best of times. XML parsers often don’t work if the HTML is badly broken. I have found that using regular expressions as a fall back method is surprisingly effective.
The basic idea, is to first look for images using the following regex:
<\\s*?img\\s+[^>]*?\\s*src\\s*=\\s*([\"\'])((\\\\?+.)*?)\\1[^>]*?>
and then get the actual image URL’s from the hits above using:
(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)
(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+
(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s``!()\\[\\]{};:'\".,<>?«»“”‘’]
I know, it all looks a bit crazy, but it works! I’ve captured the logic into the following gist that can easily be included in your own Objective-C code1.
All the regex’s above are escaped to work with NSString
↩Getting code coverage tools to work with Xcode used to be a pain, but since Xcode 4.2 Apple has made the process much easier (albeit undocumented).
Assuming you are using XCode 4.2 and the new LLVM 3.0 compiler here are the simple steps to get code coverage up and running:
In your project target (most likely only your unit test target), go to build settings and set -ObjC on Other Linker Files like so:
Similarly in Apple LLVM compiler 3.0 - Code Generation set Generate Test Coverage Files and Instrument Program Flow to YES:
Now is the slightly messy bit. To enable LLVM to run code coverage it needs to link against a library that for some reason Apple haven’t included in their “standard list”. It’s no problem to include it though. Just go to Build Phases and add the libprofile_rt.dylib in the Link Binary With Libraries section. You will find the library on your hard disk at /MacintoshHD/Developer/usr/lib/libprofile_rt.dylib
Nearly there. Now you can run your application or unit tests and LLVM will generate code coverage results. It puts the results in a special place within standard project build folder, which is a really crazy long path.
For me, that is ~/Library/Developer/Xcode/DerivedData/<projectname>-<randomstring>/Build/Intermediates/<projectname>.build/Debug-iphonesimulator/<projectname>Tests.build/Objects-normal/i386.
Its the .gcda and .gcno files that are the code coverage results files. You can use the Apple command line utility gcov to read them.
Give it a try today.
My hands down favorite font for programming is Inconsolata by @raphlinus. I use it at 11pt. Great price too - free!
New PicStroom video up on www.picstroom.com. Gives a great flavor in a few seconds to what PicStroom is all about.
Check it out!
I’ve recently begun using a well known Swedish service called Pingdom — its such a simple focused service and works very well.
It pings a website (or any URL) on a 1 or 5 minute basis and alerts you if it can’t reach the service. The pings orriginate from multiple locations across Europe and North America, so you can have confidence that a large geographic area can reach your service.
It’s free to monitor one service, check it out on http://www.pingdom.com
You can keep an eye on how PicStroom’s Data Service (static service that drives our gallery and list of initial streams) is doing at http://stats.pingdom.com/r6jyp4gf7civ/397557