Testing iOS 4.0 Location APIs on the iPhone

August 15, 2010, 1:39 am

In the course of investigating the new iOS 4.0 location APIs, I have found that the documentation was not entirely clear when describing the various cases associated with suspended and terminated apps on the device. Combining this lack of clarity with the inability to set location through the iPhone simulator, resulted in a reasonable amount of frustration.

I found the only way to effectively test this stuff is to get on a train and travel around with your app in the debugger.

This is not exactly ideal, so I built a simple test harness to switch between the various location APIs and log all the responses that the app receives. You are welcome to download, use, modify, etc at your own risk. It is pretty straightforward, but might save you some time building something to test this stuff out. If there are any glaring errors, please let me know.

Update 17 August 2010

I have found testing the app that when I am woken up from a terminated state, I get very little time to do anything. My first cut logging code was fairly inefficient so I have updated the app to do the bare minimum to write out log messages.

I also found a bug in my wake up code that instantiated a CLLocation and assigned it to a CLLocationManager, this compiles without no warnings because init returns an id which you can happily assign to anything you like, but fails when the app tries to set a delegate on the object. I have fixed this in the current version so you should get all your wake up log messages.

Update 18 August 2010

With some more testing of the wake from terminate case, I discovered that I was adding the view controller to the window in the wrong place. Adding it in didFinishLaunchingWithOptions is fine if you aren't using background location services, because there isn't a case where you don't want to create the UI. In the background location processing case, you want to be able to start up and not do all the UI overhead. So I have moved that code into applicationDidBecomeActive. That way when the app gets started by the user it adds the subview if the UI has been initialized (by checking if the window has any subviews).

Update 20 August 2010

Thanks to Daniel's suggestion I have added a Github repo for the project.

Permalink - Comments - Tags: iPhone,Development

Respirapedia on the iPhone

April 23, 2010, 8:36 pm

I was very happy to be involved in the development of Respirapedia. It's a glossary of respiratory care terms.

Now available on Android.

Over 1500 important respiratory care terms, for class, clinic, or home, all at your fingertips! An interactive glossary version on the book "Respiratory Care Lexicon" by Kenneth M. Bretl, Professor Emeritus at College of DuPage in Glen Ellyn, Illinois. An invaluable resource for the health care professional, the medical, respiratory, or nursing student, or the health care consumer. Practical, easy to use, and authoritatively accurate.

From apnea to zarfirlukast, from bardycardia to Yankauer, this lexicon serves all of your respiratory care needs and knowledge.

Glossary Engine Developed by Cannonade.Net. Distributed under contract for College of DuPage Press by Gnu Ventures Company and J&S Tech Designs

Permalink - Comments - Tags: Development,iPhone,App,Glossaries

Chomp Connect

April 9, 2010, 11:29 pm

I am a big fan of Chomp. It's social network driven software for iPhone app discovery. I think there are very real pain points with the app store and Chomp does a pretty good job of resolving those issues:

  • Negative Bias - Currently users are offered the option to rate apps when they are deleting them, so it is logical that there is a strong negative bias in ratings.
  • Barriers to review - I don't tend to review apps on the store because the UI is not available in the app.

With Chomp Connect I was able to add some UI to my about box which lets a user leave a review without closing my app. There is the initial barrier of a Chomp signup (if the user doesn't already use Chomp), but I think this barrier is still less than the multiple steps needed to review something on the app store.

The code to implement this functionality was trivial. I just had to include a bundle ,containing the code to show the Chomp dialog and some resources. Then add a few lines attached to my "Review on Chomp" button to show the dialog:

NSString *appID = [infoDictionary valueForKey:@"CFBundleIdentifier"]; ChompDialog *cDialog = [[[ChompDialog alloc] initWithApiKey:@"" forApp:appID] autorelease];

I did encounter a rather obscure problem when I went to submit my newly Chomified apps to the store. When I submitted the new binary, I got the dreaded generic submission message:

The binary you uploaded was invalid. The signature was invalid, or it was not signed with an Apple submission certificate.

If I didn't include the Chomp bundle, I was fine. With the Chomp bundle, I got the error. The Chomp dev team were extremely helpful sorting out this issue, which they hadn't seen for any of the numerous devs who had submitted apps without the problem. They asked me to check the contents of the bundle for any hidden files that might be screwing up the signature. What I found was quite interesting:

I downloaded the bundle, unzipped it, checked for hidden files and found everything ok. Then I copied the bundle from the downloads folder (on my MacOS HFS drive) to my dev directory (on a Windows drive). Boom! Suddenly there was a bunch of hidden files in the bundle. It turns out, when you copy from an HFS drive to a FAT32, MacOS needs to create a hidden file for some additional metadata that it can't put in a FAT32 file (fork data attributes).

So mystery solved. For any other lunatics out there that keep there source code on a FAT32 partition of their macbook, beware the lurking fork data attribute files.

Permalink - Comments - Tags: Development,iPhone

Subscripts and Superscripts in a UITableView

February 28, 2010, 11:47 pm

I have written a handful of iPhone apps now and used UITableView extensively in one of them. Until recently, I had never needed to put any rich text into a UITableViewCell.

The data I am using for this project is basically an index list of mini HTML documents, with an index term for each document. I use the UITableView to render the index. Most of the index terms are in plain text, but there are handful of them that include HTML tags to indicate sub-scripted or super-scripted characters:

Glucose (C6H12O6)

My initial reaction to cases like this was to create a UITableViewCell that had a UIWebView as a subview. I could just stick my HTML in the sub view and voila! my index would be rendered perfectly:

// get the term so we can check what is in it NSMutableString * term = [[[NSMutableString alloc] initWithFormat:@"%@", [dataController objectInListAtIndex:[termIndex intValue]].term] autorelease]; // need to work out what sort of cell this needs to be NSRange textRange =[term rangeOfString:@"<"]; bool foundHtml = NO; if(textRange.location != NSNotFound) foundHtml = YES; NSString * cellIdentifier = [[[NSString alloc] initWithFormat:@"%@", (foundHtml ? @"HtmlCell" : @"Cell") ] autorelease]; // Dequeue or create cell of the appropriate type. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { // didn't find a reusable cell, create one based on the type if (foundHtml) { // Grab the UITableViewCell from the HtmlCell XIB NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"HtmlCell" owner:self options:nil]; cell = [topLevelObjects objectAtIndex:0]; } else { // This cell doesn't have any markup, just use a regular cell cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor whiteColor]; } cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; } // set the text if (foundHtml) { // grab the htmlView from the cell and set it's content HtmlCell * htmlCell = (HtmlCell*) cell; [htmlCell.htmlView loadHTMLString:[NSString stringWithFormat:@"%@", term] baseURL:nil]; } else { // set the cell text normally cell.textLabel.text = term; } return cell;

I had a nagging feeling about approaching the problem this way and after the writing the code and seeing the woeful performance of UIWebView I remembered what it was. Months ago I listened to this mobile orchard interview with the Joe Hewitt, creator of the Facebook iPhone app and the open source Three20 Project. Seeing the slugglish rendering of my beautifully marked up index terms, I recalled that the unsuitability of UITableView for web content, was one of the motivations for the Three20 project (or it's original implementation in the Facebook app).

I decided against using Three20 partially because of an unreasonable case of not invented here syndrome, but also because I didn't have the time to invest in learning a new framework. I also felt like my problem was a pretty simple subset of the rich text rendering problem (just the sub-scripts and super-scripts), so there must be a simple solution.

It occurred to me that UITableView does a great job of rendering unicode characters (both Ballet Index and Karate Index render unicode characters in the table and they look great). So maybe there are sup-scripted and super-scripted unicode characters for the small set of characters that I needed (+,- and the numbers)?

Yep, there is! All I needed to do was recurse my way through each of the HTML elements in my index and replace them with the appropriate unicode character. Problem solved:

- (unichar) mapCharacter:(char)sourceChar forElement:(NSString*)element { if ([element localizedCaseInsensitiveCompare:@"sup"] == NSOrderedSame) { if (sourceChar == '2') return 0xB2; if (sourceChar == '3') return 0xB3; if (sourceChar >= '0' && sourceChar <= '9') return (0x2070 + (sourceChar - '0')); if (sourceChar == '+') return 0x207A; if (sourceChar == '-') return 0x207B; if (sourceChar == -82) return 0x00AE; } if ([element localizedCaseInsensitiveCompare:@"sub"] == NSOrderedSame) { if (sourceChar >= '0' && sourceChar <= '9') return (0x2080 + (sourceChar - '0')); if (sourceChar == '+') return 0x208A; if (sourceChar == '-') return 0x208B; if (sourceChar == -82) return 0x00AE; } return sourceChar; } // N.B. This code works for a very specific set of data. It doesn't handle // nested html tags for instance. - (NSMutableString*) processMarkupCell:(NSMutableString*) cellText { NSRange openRange =[cellText rangeOfString:@"<"]; if (openRange.location == NSNotFound) return cellText; // found an angle bracket ... need to remove the elements and map the sub/sup characters NSRange closeRange =[cellText rangeOfString:@">"]; NSRange elementRange = openRange; elementRange.location++; elementRange.length = closeRange.location - elementRange.location; // we get the element so we can use the appropriate mapping for each character (sup or sub) NSString * element = [[[NSString alloc] initWithFormat:@"%@", [cellText substringWithRange:elementRange]] autorelease]; // set up the new cell text NSRange prefixRange; prefixRange.location = 0; prefixRange.length = openRange.location; // stick everything before the element in the newCellText NSMutableString * newCellText = [[[NSMutableString alloc] initWithFormat:@"%@", [cellText substringWithRange:prefixRange]] autorelease]; // find out where we need to stop NSRange stopRange; stopRange.location = closeRange.location + 1; stopRange.length = [cellText length] - (closeRange.location + 1) ; NSRange nextOpenRange = [cellText rangeOfString:@"<" options:(NSStringCompareOptions)0 range:stopRange]; NSRange nextCloseRange = [cellText rangeOfString:@">" options:(NSStringCompareOptions)0 range:stopRange]; NSRange postfixRange; postfixRange.location = nextCloseRange.location + 1; postfixRange.length = [cellText length] - (nextCloseRange.location + 1); // start mapping the first character after the closed element for (int currentPosition = closeRange.location + 1; currentPosition < nextOpenRange.location; currentPosition++) { // append each mapped character in the newCellText [newCellText appendFormat:@"%C", [self mapCharacter:[cellText characterAtIndex:(NSUInteger)currentPosition] forElement:element]]; } // stick everything after the closing element in newCellText (other tags will be resolved recursively) [newCellText appendFormat:[cellText substringWithRange:postfixRange]]; // recurse through the string .. getting all the elements return [self processMarkupCell:newCellText]; }

Permalink - Comments - Tags: Development,iPhone

American Civil War Daily on the iPhone

December 10, 2009, 12:04 am

American Civil War Daily answers two fundamental questions:

  • What happened today during the American Civil War?
  • Where did it happen? Show me a map!

A lite version version is available free, which only supports today's date and shows 5 daily cards.

Flip through daily cards showing important events leading up to and during the deadliest conflict in American history.

Each card shows the date, a succinct description and a zoomable map for the event. You can flip the card over to read the Wikipedia article describing the event.

You can sort your deck of events using two mechanisms:

  • Sort by day and month for a this day in history view.
  • Select a start date and then sort by day, month and year for a true chronological sequence of events from that date onwards.

The data for American Civil War Daily comes from the American Civil War Timeline Project. This is a community contributed project to collect time and place data for the war. You can come to the site to browse the timeline or even log in and contribute new data.

The Timeline Project currently indexes over 400 hundred data points with new entries being added daily. If a significant event isn't in the database yet, it soon will be.

Permalink - Comments - Tags: Development,iPhone,American Civil War,App