Episode #29

RubyMotion

18 minutes
Published on August 16, 2012

This video is only available to subscribers. Get access to this video and 582 others.

RubyMotion is a toolkit that allows you to write native iOS applications using Ruby. Normally I'm pretty skeptical of these alternative frameworks, but RubyMotion is actually quite interesting. In this episode I build a small application and talk about the pros & cons of using the toolkit.

Links

Creating a project

RubyMotion comes with a binary for creating new projects. Just type motion create <projectname> to start a new project.

The project will contain an app folder and an app_delegate.rb with almost nothing in it. To get started, you'll want to create a window:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame UIScreen.mainScreen.bounds
    @window.makeKeyAndVisible
    true
  end
end

Creating a view controller

You are free to create your classes wherever you like, however I like to have some basic structure, so I create a folder view_controllers and place a new file, episodes_view_controller.rb inside.

class EpisodesViewController < UITableViewController
  def init
    super.initWithStyle(UITableViewStylePlain)
  end

  def viewDidLoad
    super
    self.title = "Episodes"

    @episodes ||= []
  end
end

Now, in the app_delegate.rb, you just need to instantiate & show this view controller:

class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame UIScreen.mainScreen.bounds

    <strong>evc = EpisodesViewController.alloc.init
    nav = UINavigationController.alloc.initWithRootViewController evc</strong>

    @window.rootViewController = nav
    @window.makeKeyAndVisible
    true
  end
end

Now, just type rake to run your application.

Adding a Gem

Next, create a Gemfile that looks like the following:

source :rubygems

gem 'bubble-wrap'

Run bundle install, then open up the Rakefile to add the require statements we need to bring in the library:

require 'motion/project'
<strong>require 'bubble-wrap/core'
require bubble-wrap/http'</strong>
...

Creating the Model

Create a folder for models and place a new file called episode.rb inside. The models are plain ruby classes.

class Episode
  attr_accessor :title

  def initialize(attrs)
    attrs.each_pair do |key, value|
      self.send("#{key}=", value)
    end
  end

  def self.from_json(json)
    new(:title => json["title"])
  end
end

Here we added some code for easy initialization and json parsing.

Talking to the API

Next, let's create a folder for our api related classes and place api_client.rb inside.

class ApiClient
  def self.fetch_episodes(&block)
    BubbleWrap::HTTP.get("http://nsscreencast.com/api/episodes.json") do |response|
      if response.ok?
        json = BubbleWrap::JSON.parse(response.body)
        episodes = json.map {|ej| Episode.from_json(ej["episode"])}
        block.call(true, episodes)
      else
        block.call(false, nil)
      end
    end
  end
end

Tying it all together

Now we just need to use the APIClient class to fetch the episodes and render table view rows:

class EpisodesViewController < UITableViewController
  def init
    super.initWithStyle(UITableViewStylePlain)
  end

  def viewDidLoad
    super
    self.title = "Episodes"

    @episodes ||= []
    <strong>
    ApiClient.fetch_episodes do |success, episodes|
      if success
        @episodes = episodes
        p "Received #{@episodes.length} episodes"
        self.tableView.reloadData
      else
        App.alert("Oops!")
      end
    end</strong>
  end

  <strong>
  def tableView(tableView, numberOfRowsInSection:section)
    @episodes.count
  end

  def tableView(tableView, cellForRowAtIndexPath:indexPath)
    cell_id = "cell"
    cell = tableView.dequeueReusableCellWithIdentifier cell_id
    if cell.nil?
      cell = UITableViewCell.alloc.initWithStyle UITableViewCellStyleDefault, reuseIdentifier:cell_id
    end

    episode = @episodes[indexPath.row]
    cell.textLabel.text = episode.title
    cell
  end</strong>
end

That's it! Just run rake to see the application fetch content from an API and use it in a table view.