Prescriber’s Letter Post Mortem

On January 19, 2010, in iPhone Development, by admin

As promised, the following is a description of the process I went through to develop Prescriber’s Letter, the app I developed for Therapeutic Research Center, the client I’ve been talking about for the last few months. This would qualify as an extremely large blog post, so only continue reading if you’re very interested in the technical details of iPhone app development.

Therapeutic Research Center (TRC) publishes Prescriber’s Letter, an online newsletter focused on developments in the pharmaceutical industry read by doctors and other medical professionals. Originally they approached me looking to build an iPhone app that would essentially “cache” their site on the phone so their customers would be able to read the newsletter content even while offline (for example, while in a hospital where communication hardware is prohibited). They ended up revising their needs and provided me with a more detailed design document. The new app they wanted to build would allow them to duplicate the content of the site without simply cacheing HTML content. Rather the content would be displayed in more native looking iPhone screens. Their design called for an app capable of downloading and displaying their content in a completely flexible manner, with the ability to update everything in the app (text on GUI elements, graphics, hierarchy of information, etc..). Their content consists of articles, issues (collections of articles), detail documents (detailed information pertaining to an article), quiz questions, and miscellaneous HTML content. I prepared a second proposal which detailed how I would approach the problem, they liked what they saw and so we got down to work.

I based the entire app on the “Navigation-based Application”. The root view controller handled most of the authentication and network message events, and all other views pushed onto the navigation controller display one of the possible content items. The first problems we tackled were authentication, the manner of distilling their website into downloadable content, content downloading, and the ability to update all text in the app.

Authentication went fairly painlessly in the end. I decided to use a library my technical contact at TRC found for me called ASIHTTPRequest. You can read an earlier blog post of mine about that library. I went with ASIHTTPRequest obviously because it saved me the time to write all of the handling code for starting a request, waiting for it to finish, checking for errors and timeouts, etc… That saved quite a bit of time. The library supports both GET and POST, and in this case the authentication request is a POST with the user’s details entered into the form data. I wrapped the request in a custom NSOperation class I wrote, which made for a very clean asynchronous process. If you haven’t read the Apple “Concurrency Programming Guide” I highly recommend reading that before writing any of your own thread code. You can save a lot of time and hassle by using the right tool for the job here. NSOperation’s bring some nice advantages to the table. They provide very contained asynchronous solutions, no messy thread set ups and overheads. Just instantiate the object, add it to a NSOperationQueue and away you go. If you have dependencies then they really start to shine. While working on this I also discovered a neat way to build custom text fields for the email address and password boxes TRC gave me. Their designer made these nice curved boxes, but they presented a problem in that when the cursor was near the curved edge it would blink over top of the curve. To solve that all I had to do was subclass the UITextField class, and override the two methods editingRectForBounds and textRectForBounds to return a CGRect with the editing box right where I needed it.

Being able to replace all the strings in the app was a simple matter. We agreed on an XML format to describe strings, and in the code I wrote a string table class that, similar to NSLocalizedString() would let me lookup strings by their keywords. We ship the app with default strings which are used the first time the app runs, but once content is downloaded from the TRC servers the app uses any replacement strings file in the content bundle from then on. At any time TRC can change strings and push a new bundle to their server.

The rest of their site’s content was a matter for an SQLite database. Their website is already driven by scripts pulling from a database, so their team wrote code to create a SQLite DB with all of the content we need. They used System.Data.SQLite, an open source ADO.NET provider for SQLite. In the end storing all the content in an SQLite db was a very good decision. We could have devised our own XML scheme or something like that, but I can guarantee anything we’d write would not be as fast or as solid as the SQLite library. Using SQL queries to get the information we need is far simpler than any kind of searching code I would have written to parse through trees of XML data. We also chose SQLite because the database has support on the other major smartphones (Blackberry, Android) so if they ever want to port the app to those devices a great deal of the backend logic will be reusable. Also, in the future if we want to expand features in the app to support anything like searching the content for keywords, SQLite is definitely going to be better at that then any kind of XML system. The database contains all of the raw data, to tie the data together into a menu hierarchy that we could navigate we devised an XML scheme where TRC could create a main menu with submenus, and each of those submenus could contain all of the possible items the app can display: issues, articles, detail documents, content from online URLs, and HTML content stored in the DB (offline other content). Originally we had the entire menu structure with all of the items and captions for the items defined in a single XML file, but this proved too slow to parse and greatly increased the initial load time of the app. We ended up having to split all the submenus into their own separate XML files which are loaded when the user taps a submenu, before the submenu’s view is pushed onto the navigation controller. All of the content is zipped into a single zip file, which is downloaded from their server, unzipped into a folder in the app’s document folder, and deleted once unzipped.

I store the version of the content bundle in a database with all of the other user information, and when the user logs in I check his current version against the server’s reply for the current version, and give the user the option to update his content whenever new versions are pushed to the server. You can read my earlier post about zip strategies to find out why I went with a .zip file. To do the actual downloading I wrote a custom NSOperation class, and used the NSURLConnection class with an NSURLRequest pointed at the content bundle URL on the TRC server. I went this route over ASIHTTPRequest because I wanted to hook into the delegate methods so I could easily update a progress bar with the download progress. Unfortunately you can’t stream directly to the file system on iPhone OS, so I stored the incoming data in an NSData object, and I flush the object to a temporary file every time it got to 1MB. I did that so I wouldn’t raise the high watermark of the amount of memory the app needs too much, since the bundle can be 5 or more MB in size.

Authenticating, downloading and installing content and having the structure of the content and the process for updating content figured out left me with milestone 1 complete. This went quite well and I didn’t run into any major issues. The next milestone involved creating all the base GUI elements I would need to display all of the content types. Milestone 2 would need the main menu, issues and articles in place.

The main menu (which is really just the root “submenu”) is a UITableViewController, with a standard table as the main view. I knew that I would need to come up with a robust and flexible way of figuring out how to display each of the menu items that can occur in a menu (and therefore show up as a cell in the table view). So I hit my Design Patterns book up to try to find the right pattern for managing the menu XML file and all of the tree of menu items. The answer came with the “composite” pattern. Each menu item is a component that knows how to do certain task, tasks like generating a cell for me to add to a table view, and taking appropriate action when the component’s cell is selected. A component can also be a composite, which is a parent with a list of children components. So, submenus are composites, and menu items that display content are components. When the app loads I parse the main menu XML file and begin building a menu hierarchy of composites and components. I then track my current node in the hierarchy at all times. This way all I have to do when I’m displaying a menu screen is ask the current node’s children for the cell they need to add to the tableview.

A lot of work went into making the custom UITableViewCell class used to display the menu items. The main reason the class was difficult to get right was due to the dynamic nature of the content of the cell. I needed a class that could have text of any length, image or no images, an optional second line with different text color and the ability to support a mix of regular text and italics. TRC needed to support italic text because drug brand names must be italicized. I wrote a custom UILabel class that would take in an HTML formatted string, e.g. “<i>Ciofera</i> (Clophedinol/Pseudophedrine) for Cough”, and use NSXMLParser to parse out all the formatting tags. I then took the parsed word / format pairs and built a view with sublabels, using either the normal font or an italic font. So, for example in the top item on the infectious diseases submenu you see above, the word Ciofera is in its own label, while the rest of the words are in separate labels as well with regular non-italic font.

The issue screen was fairly simple to implement. I query the database for all the articles that belong to the issue, and display them in a UITableView set to display in grouped mode. The articles are grouped by their respective category, and they’re displayed with the custom cell class I made for menu items capable of displaying strings of any length, with italics, etc… Tapping a cell here loads an article screen.

The article screen queries the HTML content for an article from the database and loads it into the central UIWebView. When displayed from an issue (rather than from a submenu) you can navigate through all the issue’s articles by hitting next/previous. You can also load up a detailed information document if the article has one. The user can also click links in the article to load external sites. I had to intercept these clicks to bring up a navigation control that would let the user navigate back to the article they came from (because the webview can’t load it, since it’s loaded from the database). When the UIWebView canGoBack method reports false I know I need to reload the content from the DB.

Also when viewing an article from an issue the user is presented with quiz questions which count towards credits they can earn for continuing medical education. The quiz screen presents the question in a group style table view and records the user’s answer. I use the ASIHTTPRequest SDK to POST a form with the user’s quiz results when they submit them.

Once the article, issue screens and the main menu structure were in place milestone 2 was complete. By far the trickiest thing from milestone 2 was getting the HTMLLabel class I wrote to behave and wrap lines properly. Milestone 3 was the final milestone, and to complete it I needed to finish the detail document screen, the offline other content, and online URL menu item screens.

The detail document and detail document section screens were relatively simple to implement. Once I had all the database queries set there was a lot of code I could reuse from the previous screens. The detail document screen is simply a table view that lists all of the detail document section titles. Tapping a cell pushes the detail document section’s screen onto the navigation controller. The detail document section screen displays the content for the section in the central UIWebView.

The offline other content and URL menu items were implemented with the same screen, one pulling content from HTML in the database, the other pointing directly to an online URL. Each one displays the content in a central UIWebView.

Once all the screens were in place I had some internal work to finish before the project was complete. The most difficult thing on the list was the matter of storing the user’s current location in the menu hierarchy when they exit the application, and restoring that location when they next run the app. I devised an XML scheme where I had a nested hierarchy of “Items”, and each item had a descriptor. I then went through all of my menu component classes and wrote a method that would save the current state of the menu item (and its view) to a descriptor string, that when passed back to the item would allow it to restore itself to its current state. Basically I wrote my own serialization system. I think if I took the time to investigate I would probably find that Cocoa would solve the serialization (or XML) part of this process, I know for certain from a previous reader’s comments that the Facebook API has some support for serialization of your hierarchy as well. My system works well, the XML is dumped to the user database when the app delegate receives the applicationWillTerminate: message. When the application loads in the future it checks for a screen stack, and then passes the XML to a screen “factory” class that rebuilds the navigation controller’s hierarchy, almost as if the user were quickly tapping the controls he would if browsing to the same location. Because I use all the same methods that are called when the user taps cells to get to a spot in the menu hierarchy, I can rest assured that the state of things in the application should be identical.

The final task involved firing up Clang to look for memory leaks, and some testing with instruments as well to catch any leaks. I broke my rule (of course) and left this to the last minute, and as before the job was made much more difficult than it should have been. I absolutely need to do this throughout the project in the future. It’s much easier to be sure that your “blocks” of logic are memory leak free than to try to figure out whether an entire app is clean. One thing that made the task trickier as well was that this app makes use of quite a few singleton objects, and several cases of lazy initialization. This meant that going into a screen could potentially allocate some large object, that wouldn’t be freed when leaving the screen. So simply testing whether the memory allocation came back to the starting amount after leaving a screen was not enough. I had to get right in there to investigate which objects were left laying around to make sure they were all legitimate. Really though in the end with Clang and manually testing with Instruments I only found a few minor leaks, mostly of strings, no large objects so I was fairly happy with myself.

I found the project quite interesting, and I was really glad to get some experience interacting with a remote web server, as I have so many app ideas that need to do that sort of thing. Also, most clients would want that kind of functionality in their apps, so now I can say I’ve done it and show a very good example.

Share:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Slashdot
  • TwitThis
 

2 Responses to “Prescriber’s Letter Post Mortem”

  1. I can vouch for Robert’s excellent work on this project. We’re thrilled with his work!

    David Prothero
    IT Director
    Therapeutic Research Center

  2. [...] you haven’t already read the iPhone post mortem you should probably do that first as this post will largely be discussing the differences of the [...]

Leave a Reply