The Devver Blog

A Boulder startup improving the way developers work.

Building a iPhone web app in under 50 lines with Sinatra and iUI

One awesome thing about the iPhone is that it can display documents very nicely, including Word, Excel, and PDF files. However, the other day I was complaining that it’s not very easy to view the documents you store on your computer on your iPhone. Sure, you could email them to yourself, but then you have to search through your mail on your iPhone to find your documents. And I’m sure there is a snazzy iPhone app from the App Store to do this as well. But instead, let’s build a quick web app using Sinatra and iUI.

Here’s what we’ll be building (screenshot courtesy of iPhoney, which rules, by the way):

A screenshot of the butler iPhone web app

It doesn't look like much, but hey, it's less than 50 lines of code

When you click on git-tutorial.pdf, you'll see the full document

When you click on git-tutorial.pdf, you'll see this

Sinatra is a really awesome minimalist web framework. It lets you build web applications with just a few lines of code. iUI is a collection of JavaScript, CSS, and images that lets you easily make your web sites look great on the iPhone. Using these two tools, it’s really easy to build simple iPhone apps.

To begin, install the Sinatra gem:

$ gem install sinatra

Now, let’s start with a simplest version of our app, which we’ll call ‘butler’. Let’s make a directory for butler.

$ mkdir butler
$ cd butler
$ touch butler.rb

Open up butler.rb in your favorite editor and type:

require 'sinatra'
get "/" do
  "<h1>Your files, sir.</h1>"
end

view this gist

Now start butler on your command line:

 $ ruby -rubygems ./butler.rb 

and point your browser to http://localhost:4567 (you can use your computer’s browser or the one on your iPhone – it doesn’t matter. I find it’s better to use the one on my computer while building the app, since it’s easier to read Sinatra’s debugging messages if something goes wrong). You should see a page that just says “Your files, sir.” Congrats! You’ve made your first Sinatra app. Wasn’t that easy?

OK, let’s make butler a little more useful. Sinatra will serve up any files in a subdirectory named public. Since we’ll eventually be using this public directory for holding other JavaScript and CSS files as well, we’ll actually put our files in ./public/files. We’ll also make a link for convenience. Finally, while we’re at it, let’s put a few test files in there.

$ mkdir -p public/files
$ ln -s public/files files
$ echo "foo" > public/files/foo.txt
$ echo "bar" > public/files/bar.txt 

We want butler to link to each file, so let’s build a little helper for that. In Sinatra, you can include helpers within a helper block. We’ll also try out our helper for one file.

require 'sinatra'
require 'pathname'

get "/" do
  html = "<h1>Your files, sir.</h1>"
  dir = "./files/"
  html += file_link("./files/foo.txt")
  html
end

helpers do
  def file_link(file)
    filename = Pathname.new(file).basename
    "<a href='#{file}'>#{filename}</a><br />"
  end
end

view this gist

Go refresh your browser to see the changes. There’s no need to restart your application, because Sinatra automatically reloads changes (very cool!). You should see a link to foo.txt. Click on it, and you’ll see the contents.

Clearly, we don’t want to hardcode this for just one file. Let’s alter butler to look for every file within the ./files directory.

require 'sinatra'
require 'pathname'

get "/" do
  html = "<h1>Your files, sir.</h1>"
  dir = "./files/"
  Dir[dir+"*"].each do |file|
    html+=file_link(file)
  end
  html
end

helpers do

  def file_link(file)
    filename = Pathname.new(file).basename
    "<a href='#{file}'>#{filename}</a><br />"
  end

end

view this gist

OK, refresh your browser and you should see both foo.txt and bar.txt. This is looking pretty good, but we’re not really creating valid HTML right now. We’re missing html, head, and, body tags at the very least. We could add this all within our “get” handler, but that would clutter up the code.

Instead, let’s put this code into a view. Sinatra actually lets you put the view right after your other code, so you can build an entire application in one file. For simplicity, I’m going to do that for this tutorial. However, if this approach bothers you (or just messes up syntax highlighting in your editor), rest assured you can place the view code in a views directory and it would work the same way.

Let’s add the view to the end of our file, and use it in our handler. Notice that I name the view ‘index’ by beginning my declaration with @@ index – if I wanted a separate file, I would just put it in ./views/index.erb (you can also use Haml, if that’s your cup of tea). Note I assign @links in the handler and it automatically is available in the view.

require 'sinatra'
require 'pathname'

get "/" do
  dir = "./files/"
  @links = Dir[dir+"*"].map { |file|
    file_link(file)
  }.join
  erb :index
end

helpers do

  def file_link(file)
    filename = Pathname.new(file).basename
    "<a href='#{file}'>#{filename}</a><br />"
  end

end

use_in_file_templates!

__END__

@@ index
<html>
  <head>
  </head>

  <body>
    <h1>Your files, sir.</h1>
    <%= @links %>
  </body>
</html>

view this gist

Refreshing the browser now isn’t really that exciting, since things look the same, but if you wanted, you could easily play around with the view to make things look different.

One glaring problem is that this page isn’t very usable on the iPhone itself. That’s where iUI comes in. Start by downloading it (URL is in instructions below) to your butler directory, unzipping it, and copying the necessary files into your public directory.

$ mkdir iui
$ cd iui
$ wget http://iui.googlecode.com/files/iui-0.13.tar.gz
$ tar -xzvf iui-0.13.tar.gz
$ cd ..
$ mkdir public/images
$ cp iui/iui/*.png public/images
$ cp iui/iui/*.gif public/images
$ mkdir public/javascripts
$ cp iui/iui/*.js public/javascripts
$ mkdir public/stylesheets
$ cp iui/iui/*.css public/stylesheets

To use iUI, you’ll need to include the JavaScript and CSS in your view. You’ll also need to add some elements to the body of your view. When you’re done, the view will look like this:

<html>
  <head>
    <meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
    <style type="text/css" media="screen">@import "/stylesheets/iui.css";</style>
    <script type="application/x-javascript" src="/javascripts/iui.js"></script>
  </head>

  <body>
    <div class="toolbar">
    <h1 id="pageTitle"></h1>
    </div>
    <ul id="home" title="Your files, sir." selected="true">
       <%= @links %>
    </ul>
  </body>

</html>

view this gist

This html is probably a bit confusing, but don’t worry. There are a few examples in ./iui/samples/ to learn from (and good iUI tutorials on the web). Finally, you’ll want to alter the file_link helper to print out iUI code, like so:

helpers do

  def file_link(file)
    filename = Pathname.new(file).basename
    "<li><a href='#{file}' target='_self'>#{filename}</a></li>"
  end

end

view this gist

Note that target='_self' code. You need that to get iUI to open a link in a normal way. If you leave it off, it will use an AJAX call to load the file within the current page, which looks really funny when you try to open a binary file like a PDF.

The final code looks like this:

require 'sinatra'
require 'pathname'

get "/" do
  dir = "./files/"
  @links = Dir[dir+"*"].map { |file|
    file_link(file)
  }.join
  erb :index
end

helpers do

  def file_link(file)
    filename = Pathname.new(file).basename
    "<li><a href='#{file}' target='_self'>#{filename}</a></li>"
  end

end

use_in_file_templates!

__END__

@@ index
<html>
  <head>
    <meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
    <style type="text/css" media="screen">@import "/stylesheets/iui.css";</style>
    <script type="application/x-javascript" src="/javascripts/iui.js"></script>
  </head>

  <body>
    <div class="toolbar">
    <h1 id="pageTitle"></h1>
    </div>
    <ul id="home" title="Your files, sir." selected="true">
       <%= @links %>
    </ul>
  </body>

</html>

view this gist

And there you have it – an iPhone web app in less than 50 lines of code, thanks to Sinatra and iUI. Now, whenever you want to view some files on your iPhone, either copy the file:

$ cp path/to/my_file ./files

or if you prefer, link it:

$ ln -s path/to/my_file ./files

… and then run butler

$ ruby -rubygems ./butler.rb

Figure out the IP address of your computer and simply point your iPhone browser to http://<ip&gt;:4567

I use butler primarily within my home network, but if you want to be able to view your files on the go, you’ll need to poke a hole in your firewall. That’s a bit outside the scope of this tutorial, but a quick Google search should give you some good results.

Enjoy!

Update: Removed an unused parameter from the code after pmccann called it to my attention.
Update: Added -z option to tar after Peter pointed out the omission. The tar command without -z worked for me on OS X 10.5, but this is definitely more correct.
Update: Added -rubygems option to ruby command. If you’d prefer to not use this option, check the comments below for ways to use RubyGems in a Ruby script.

About these ads

Written by Ben

November 25, 2008 at 9:14 am

Posted in Hacking, Tools

46 Responses

Subscribe to comments with RSS.

  1. Great way to show off iUI and Sinatra. Is this on GitHub yet?

    edavis10

    November 25, 2008 at 9:33 am

  2. bhb

    November 25, 2008 at 9:50 am

  3. Nice example of the beauty and ease of use of Sinatra. Keep spreading the word

    Craig Jolicoeur

    November 25, 2008 at 10:01 am

  4. Sweet.

    Adam Wiggins

    November 25, 2008 at 4:43 pm

  5. Nice tutorial, but one oddity: (warning, my ruby isn't great, so perhaps I'm missing something). Why is "dir" being passed to the repeated definitions of the function "file_link"? It doesn't seem to be referred to inside that function — instead "file" is assumed to be a full path relative to "/" in the web app.

    pmccann

    November 26, 2008 at 5:15 am

  6. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog Sinatra is a really awesome minimalist web framework. It lets you build web applications with just a few lines of code. iUI is a collection of JavaScript, CSS, and images that lets you easily make your web sites look great on the iPhone. Using these two tools, it’s really easy to build simple iPhone apps. (tags: rubyonrails iphone sinatra iui) [...]

  7. Good catch. That parameter was from a previous version of the method and I accidentally left it in. I'll fix that today. Thanks!

    bhb

    November 26, 2008 at 8:12 am

  8. $ sudo gem install sinatra
    Successfully installed sinatra-0.3.2
    1 gem installed
    Installing ri documentation for sinatra-0.3.2…
    Installing RDoc documentation for sinatra-0.3.2…

    $ ruby ./butler.rb
    ./butler.rb:1:in `require': no such file to load — sinatra (LoadError)
    from ./butler.rb:1

    Can you tell me why is this happening?

    bada-bing

    November 28, 2008 at 9:22 am

  9. try adding: require 'rubygems'

    jeff

    November 28, 2008 at 9:32 am

  10. doh, add that to the very top of the file.

    jeff

    November 28, 2008 at 9:32 am

  11. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI [...]

  12. Jeff is right (thanks, Jeff!) – you need RubyGems. You can either use Jeff's fix or run ruby like this:

    ruby -rubygems ./butler.rb

    If you're going to be using Ruby a lot, the easiest fix is to put the folowing line in your .bash_profile or equivalent

    export RUBYOPT=rubygems

    Then, whenever you run ruby, it'll use RubyGems. This is why my example doesn't explicitly require RubyGems. Hope that helps – and sorry for the confusion!

    bhb

    November 28, 2008 at 3:07 pm

  13. You could skip "your favorite editor" detour with a here document?
    <pre>
    cat << EOF > butler.rb
    require 'sinatra'
    get "/" do
    "<h1>Your files, sir.</h1>"
    end
    EOF
    </pre>

    Bil Kleb

    November 28, 2008 at 4:33 pm

  14. Your final code is missing the "Your files, sir." bit from inside the H1 (to make it look like the screenshot at the top of the post).

    Also, shouldn't "tar -xvf iui-0.13.tar.gz" have a z option too (to unzip)?

    Minor things – sorry ;-) Very good otherwise!

    Peter Cooper

    December 3, 2008 at 11:28 pm

  15. Peter – Thanks for the feedback. Actually, the "Your files, sir." string is in the final version, just no longer in an H1. I moved it to make it look all snazzy on the iPhone.

    You're right about the -z option for tar. For some reason, the command works without that option on OS X 10.5, but it's definitely more correct your way. I've changed it.

    Thanks!

    bhb

    December 4, 2008 at 10:09 am

  16. By the way, the easy way to view files that are on your computer, from anywhere, even if your computer is turned off is SugarSync. Install it on your Mac and/or PC and view your files on your iPhone anytime, anywhere. Go to the iPhone App Store and download it free.

    Dave

    December 8, 2008 at 10:58 am

  17. [...] webapp (since the files are all hosted on your own servers), but Ben Brinckerhoff has put together an excellent tutorial demonstrating how to build an iPhone webapp in just 50 lines of code using Ruby, Sinatra (a lightweight Web framework) and iUI (an iPhone-specific style and JavaScript [...]

  18. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog (tags: webdev webdesign webapps webapp web2.0 web ui tutorial) [...]

  19. This info needs to be right up front. Maybe it's obvious to you, but I'm no Ruby expert, and got to about step 1 of this tutorial, and it failed mysteriously. I'm glad the solution was here in the comments, but it needs to be more obvious.

    thx,
    steve

    Steve

    December 12, 2008 at 2:32 pm

  20. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog [...]

    Diigo Update (weekly) «

    December 13, 2008 at 5:32 pm

  21. Scott – Good call. I've fixed that.

    bhb

    December 14, 2008 at 12:06 pm

  22. Steve – Good call. I've fixed that.

    bhb

    December 14, 2008 at 12:07 pm

  23. [...] Building an iPhone Web app in under 50 lines with Sinatra and iUI – A comprehensive walkthrough of developing an iPhone friendly Web application based on Sinatra. Even if developing an iPhone app doesn’t interest you much, this tutorial is so well written that it could act as a good introduction for anyone. [...]

  24. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI by the Devver blog Sinatra is a really awesome minimalist web framework. It lets you build web applications with just a few lines of code. iUI is a collection of JavaScript, CSS, and images that lets you easily make your web sites look great on the iPhone. Using these two tools, it’s really easy to build simple iPhone apps. [...]

  25. Thanks! Fun tutorial. BTW, I could only get the "Your files, sir" to appear if it was in the H1, not in the UL.

    Jim Clark

    December 26, 2008 at 9:26 am

  26. Jim – what browser are you using? I tried Firefox 3.0.5 and Safari 3.2.1 on my laptop and Safari on my iPhone and can always see the "Your files, sir" at the top of the page. Sorry it's not working for you!

    bhb

    December 28, 2008 at 9:44 am

  27. I found the problem. The instructions above say "mkdir public/javascript". However, the HTML points to "javascripts", notice the "s", so the JavaScript wasn't being picked up. I've made the correction and all is working. Thanks.

    Jim Clark

    December 28, 2008 at 8:29 pm

  28. Jim – Good catch. I've corrected the instructions. Thanks!

    bhb

    December 28, 2008 at 9:03 pm

  29. [...] Via The Devver Blog [...]

  30. [...] Ben Brinckerhoff escreveu um excelente tutorial exemplificando isso. No artigo, ele mostra como montar uma aplicação web para iPhone com apenas 50 linhas de código, usando a linguagem Ruby em conjunto com o framework web Sinatra e [...]

  31. [...] pensou em desenvolver uma aplicação web para iPhone rapidinho? O Ben Brinckerhoff escreveu um excelente tutorial exemplificando [...]

  32. [...] of our Ruby development surveyMontando um site para iPhone em minutos com Ruby | Ruby Brasil on Building a iPhone web app in under 50 lines with Sinatra and iUI « Boulder CTO January Lunch with Jud [...]

  33. [...] There are some great tutorials listed on the Sinatra site, though. In particular, I found Building an iPhone web app in under 50 lines with Sinatra and iUI to be just the ticket for quickly getting up to speed on what Sinatra does. As an added bonus, [...]

  34. I made a small change to permit folder browsing:

    http://gist.github.com/107435

    Macario

    May 6, 2009 at 1:30 am

  35. Folder browsing change doesn't work. Throws error:

    …./uri/common.rb:289:in `encode': private method `gsub' called for /^(?!/files)(.+$)/:Regexp (NoMethodError)

    johnrclark

    May 10, 2009 at 11:10 am

  36. False alarm. My ruby/gems installation was out of date. Everything works fine after updating…

    johnrclark

    May 11, 2009 at 5:44 am

  37. Thanks for the tut, I've mounted a home media server for my pod. :)

    Macario

    May 11, 2009 at 7:44 am

  38. [...] [upmod] [downmod] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog (devver.net) 1 points posted 5 months, 1 week ago by SixSixSix tags mobile imported saved by [...]

  39. Recomendation to "www.ReadMyNeed.com" a Customized Subscription for iPhone/iTouch

    ReadMyNeed

    June 9, 2009 at 12:24 am

  40. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog (tags: iphone sinatra) [...]

  41. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI – The Devver Blog [...]

    AK47 » links for 2009-12-27

    December 27, 2009 at 5:57 pm

  42. Hi,
    thx for the great tutorial. To use it online, I created a heroku.com account and deployed the butler app there.
    Your can see it here: http://butler.heroku.com

    Carsten

    April 2, 2010 at 6:10 am

  43. [...] won’t be going into detail on how to build a iPhone webapp using Sinatra and iUI, because Ben already created an excellent post detailing all of those steps. In fact I used his old [...]

  44. hi
    i’m so thrilled that i saw this blog. that post was so great. thanks again i saved this article.
    are you going to write similar articles?

    Gewinne

    May 16, 2010 at 1:48 pm

  45. [...] Building a iPhone web app in under 50 lines with Sinatra and iUI (tags: ruby sinatra mobile web iphone programming) [...]


Comments are closed.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: