Some good but sobering progress on the Decaf Sucks app this week: just enough that it's still possible to meet our launch date, but only with the most concerted of efforts over the next few weeks.
Building a Test Server on Heroku
Now that we have an API that supports creating reviews, we need a server for the iPhone app to use during development and testing. Fortunately, I had done some earlier work to test moving the app to Heroku's new Cedar stack, so getting a testing server up and running was short work.
Or so I thought. I quickly ran into a bug where I couldn't submit reviews due to Heroku's PostgreSQL database not supporting the
dmetaphone function that comes as part of PostgeSQL's fuzzystrmatch contrib module. This is an important function in Decaf Sucks: we use it to attach a new review to an existing café if they have similar sounding names (hence the
dmetaphone use) and similar locations.
Fortunately, it didn't turn out to be too hard to support this behaviour on Heroku, using the Ruby text gem's double metaphone implementation instead of the database function. Doing this in Ruby instead of in the database is likely slower, but by saving the double metaphone values into a new indexed database column, we should avoid any large performance penalties. Here's how we do it:
class Cafe < ActiveRecord::Base before_validation :generate_simplified_name before_validation :generate_dmetaphone_name protected def generate_simplified_name self.simplified_name = self.name.gsub(/(and|&|-|'|")/i, '').gsub(/caf.$/i, '').gsub(/\s+/, ' ') end def generate_dmetaphone_name self.dmetaphone_name = Text::Metaphone.double_metaphone(self.simplified_name).first end end
This generates the double metaphone value for a simplified version of the café's name as it is persisted to the database for the first time. Then, when creating an instance of the
Review model, we can check for any nearby café matches like this:
Cafe.nearby(self, 0.2).where(:dmetaphone_name => Text::Metaphone.double_metaphone(self.simplified_name).first).first
With that, the café matching functionality is preserved! I think this is a worthwhile compromise to have our app run on the scalable and easy-to-use Heroku platform.
The other feature I had to remove for Heroku was our PostgreSQL full text searches. If we move to Heroku for our production app (which seems likely), I'll replace these with Pat Allen's excellent Flying Sphinx Heroku add-on.
Choosing Objective-C Libraries
With the web app work done (again), it was time to add review posting functionality to the iPhone app. To support this, I had to choose an Objective-C library for handling HTTP requests. I needed support for sending requests with the various HTTP methods, setting custom headers and passing user credentials for basic authentication. The obvious candidates were ASIHTTPRequest and Resty. At this point, I had Marco's voice in my head warning about the dangers of dependencies, so I picked the more widely-used, though perhaps lower-level, ASIHTTPRequest. So far, it's been great.
The other library I wanted to bring in was for our "write a review" screen: a star rating picker, which is central to the existing Decaf Sucks experience. I took a look at CocoaObjects and found a number of candidates. I picked one from SSToolkit, threw it in the app, and modified it to work with half-stars. So far, it seems to work pretty well, though I might consider replacing it with DLStarRating if we run into any problems.
Un-Designing the App
There's not long left before we need to launch the app: 4 weeks until Swipe Conference means 2 weeks until we should submit to the App Store, and therefore just 1 week until we should have all the features in place! If we're going to get this done, then we'll need to build a true minimum viable product. In fact, this is what we did when we launched the web app for the first time, after the Rails Rumble weekend in 2009. Building something small enabled us to provide a focused, polished experience.
Our plan is to do the same thing with the iPhone app. While our initial feature list for a first version was already relatively short, it's possible to reduce it a little further. The biggest change I've made is to reduce the overall user interface "surface area" of the app. Until now, the app has been built around three sections (Find, Write and Account) managed by a tab bar. I've removed this tab bar and moved to a single navigation bar as the main app control. We've ditched the Account section (the mobile web app lived without this for a long time), and the Write section is now a modal view launched via a compose button in the nav bar.
Removing some UI chrome from the app means we can give greater love to what remains. Same for any reduction in features. Hopefully this puts us on a path to release a small but helpful and beautiful app in a couple of weeks. The next step on that path? Getting back to work. See you next week.