Wednesday, April 22, 2009

Google Reader mobile client with Rhodes

I've been an avid user of Google Reader for many years now, but I've always felt it was missing a good mobile client. Sure, it has a mobile optimised web interface (http://www.google.com/reader/m/view/) but I've always found this pretty ordinary due to the long waits moving from story to story (even on a decent 3G network). What I've always wanted is a mobile client that caches the stories locally on the mobile device and allows me to skim through them quickly without waiting for a page load for each one.

I recently came across the Rhodes mobile framework which allows "building locally executing, device-optimized native mobile applications" in Ruby. There are a couple of nice things about Rhodes that I really like:
  • Once you've written your application it can be built for a variety of mobile applications - iPhone, BlackBerry, Symbian, Android and Windows Mobile.
  • Development is done in Ruby with a Rails-like structure - the mobile GUI is written in HTML but designed to retain the look and feel of each mobile devices native interface.
  • It provides Rhosync - the back-end server component to help syncronise data between the data source and the local cache on the mobile device.
So I finally decided to attempt to write a Google Reader mobile client using the Rhodes framework. I originally tried this a while ago when version 0.3 was out but found it a bit hit and miss and eventually gave up. When 1.0 was recently released I decided to try again.

The Rhomobile wiki is a good place to start, with a fair bit of information on how to get started. The biggest thing that tripped me up was when generating the model in Rhodes - its not really clear, but the source_id parameter must match the id of the source on the rhosync backend.

The only work I really had to do on the Rhosync server was to create a source adapter for the Google Reader items with the following methods:
  • query - this logs into Google Reader using the user's credentials (the Rhosync server allows users to register themselves, subscribe to various sources and provide their credentials for each), and retreives the most recent set of items from Google Reader which are stored in the @result instance variable
  • sync - the sync method takes the @results from query and simply twists them into the object-value format the Rhosync uses (this is so that it can sync each attribute of each object individually)
  • update - this is called by Rhosync when a mobile client has made a change to the data locally that now needs to be propogated to the backend. In my case the only property I cared about was the read state.
One issue I encountered writing the source adapter was that Rhodes/Rhosync can not handle arbitrary characters in the object id, which meant I had to strip any special characters out of the Google Reader id. However I needed to have access to the original Google Reader id for when I was propogating the read state back to Google Reader. In the end I had to store the original Google Reader id as an attribute in each object, and look it up in the update method (it isn't available automatically because the client only passes the modified attributes).

The actual Rhodes client was fairly simple to implement - it is mostly just editing HTML in ERb. Although, I did a couple of things to clean up the generic application:
  • Edit /config.rb to set the page that the app starts on (by default it will start with a list of all your models).
  • In the controller for the start page I added the following to redirect to the login page when required:

if SyncEngine::logged_in > 0
@items = Item.find(:all).sort {|a,b| b.timestamp <=> a.timestamp}
render
else
redirect :controller=>"Settings", :action=>"login"
end

  • By default, the login view has the password field as type "text" meaning the password is visible as it is being typed, I changed it to "password".
So finally, this is what it looks like on the iPhone:
After all this ... I have decided that I'm going to release the source for this, but I am not going to release it as an application. The reason is that I would need to host the Rhosync server, which is not a problem in itself - after a couple of quick changes it seems to work fine on Heroku (although I think I would need to make some changes to restrict users to just subscribing to apps, not creating them). However, the Rhosync server would need to store the Google Reader password for all of the users which is not something I want to deal with. Perhaps one day Google will deign to add OAuth to Google Reader which would mean that Rhosync just needs to store its OAuth token ... maybe at that time I'll revisit this. For now I'll probably just use this myself and release the source. (Note that Rhomobile have announced a hosted version of Rhosync called Rhohub which would make this a bit easier, but I still don't want to be responsible for people's Google password)

If you're interested the Rhosync and client source is hosted in my GitHub account: http://github.com/nonspeaking

1 comment:

  1. Great stuff. By the way, we have a solution for your stored passwords issue coming in the 1.2 release (some time in July). Hope you consider fielding your great app in that timeframe.

    Cheers,

    - A

    ReplyDelete