RubyMotion



RubyMotion

0 0


rubymotion-slides

RubyMotion talk on 11/20/2014

On Github TampaRuby / rubymotion-slides

RubyMotion

OSX and iOS Development with Ruby

A talk by @tylerjohnst

Hi!! I'm Tyler Johnston

Owner of Ahoy Consulting and Runner of Suncoast.js Meetup

Psst... you should come!

What is RubyMotion?

RubyMotion allows you as the developer to create and publish native Mac and iOS (and soon Android) applications using Ruby.

Yes, native. The RubyMotion compiler handles converting the Ruby code in to LLVM bytecode.

RubyMotion allows you all the power and expressiveness of the Ruby VM while letting you deploy to other platforms.

Ok but what is RubyMotion really like compared to Objective-C?

// TableViewController.h
#import <UIKit/UIKit.h>
@interface TableViewController : UITableViewController
  @property NSArray *fruits;
@end
          
// TableViewController.m
#import "TableViewController.h"
@interface TableViewController ()
@end

@implementation TableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setFruits:@[@"apple", @"pear", @"banana"]];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.fruits count];
}

- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"foobar"];

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"foobar"];
    }

    NSString *cellValue = [self.fruits objectAtIndex:indexPath.row];

    cell.textLabel.text = cellValue;

    return cell;
}

@end
          
class FruitsViewController < UITableViewController
  attr_reader :fruits

  def viewDidLoad
    super
    @fruits = %w(apple pear banana)
  end

  def tableView(tableView, numberOfRowsInSection: section)
    fruits.length
  end

  def tableView(tableView, cellForRowAtIndexPath: indexPath)
    tableViewCell.tap do |cell|
      cell.textLabel.text = fruits[indexPath.row]
    end
  end

  private

  def tableViewCell
    tableView.dequeueReusableCellWithIdentifier('MyCell') ||
      UITableViewCell.alloc.initWithStyle(UITableViewCellStyleValue1, reuseIdentifier: 'MyCell')
  end
end
          

Ruby eliminates the need for dealing with implementing interfaces or juggling headers. Just implement the correct method and everything is good to go. Look in to code completion for your favorite editor.

Even though you are using Ruby you still need to know and understand the Cocoa API.

Well, some of it. Thats what documentation is for!

This is the least impressive part of the toolchain. It just looks like awkward Ruby, and it is.

Everything I've tried to use has been supported 100%. They seem to have about a month lag behind Apple's release shedule. The new iPhone sizes took about that long to land in RubyMotion.

Ruby REPL

RubyMotion binds the running iOS simulator to a Ruby REPL.

$ rake

Lets do the worst thing you can do in an talk and live demo!

Full Cocoa API Support!

label = UILabel.alloc.init
label.text = 'My Super Cool String'

views = { 'label' => label }

view.addConstraints(
  NSLayoutConstraint.constraintsWithVisualFormat('H:|-20-[label]-20-|',
    options: 0,
    metrics: nil,
    views:   views
  )
)
          

RubyMotion hides a lot of the work it's doing for you. All of the Cocoa API's accept and convert the Ruby primiatives.

Cocoa Ruby NSString String NSInteger Integer NSDictionary Hash NSArray Array

NSLog("Foo#{:bar}")

Full support for CocoaPods!

CocoaPods is the dependency manager for Objective-C projects. It has thousands of libraries and can help you scale your projects elegantly.

# Gemfile
gem 'motion-cocoapods'
          
# Rakefile
app.pods do
  pod 'afnetworking'
end

          
$ rake pod:install
          

Full support for Storyboards/Interface Builder!

The gem handles generating a stub xcodeproj and allows you to use all of the features of Interface Builder. It handles creating stub implementation headers for the drag and drop Interface Builder features. (Outlets, Actions, Segues, etc)

# Gemfile
gem 'ib'
          
$ rake ib
          

The only problem being how much I hated interface builder. (I don't reccomend it.)

Now most of that stuff doesn't seem very compelling, You can do all of the same things within Xcode.

And while thats true, after this point you get all of the benefits of Ruby.

Almost all of the Ruby features are available with the exception of two things:

String Eval
eval("whyudodis = MyBadIdea.infect_my_system")
Require
require 'my_other_file'

Apple does not allow the dynamic loading of code so using require throws an exception. There is a gem called motion-require which allows you to use require_relative within your projects.

This solved all of my code load order problems.

But everything else is available:

  • class_eval (non string version)
  • instance_eval (non string version)
  • define_method
  • const_get/const_set
  • public_send

Stand back Node.js, there is a new player in town:

Code Sharing!

In a project I just deployed the initial release of, the majority of the business logic is in a shared gem between the iOS application and the Rails backend.

Blazing fast testing with Rspec

 $ bundle exec rspec
...................................................................................................................

Finished in 0.16608 seconds (files took 0.35396 seconds to load)
115 examples, 0 failures
          

I'd recommend asking a veteran iOS develpoer about testing and watch the look of horror in their face. If they do test, ask if their runner is done in 0.1 seconds.

Caveats

It's not free. The license is $200 but is worth every penny. If you would have bought it during early registration it was only $100.

The RubyMotion team has also been slowly open sourcing parts of the framework. I believe their goal is to get almost all of it in the open. I think they are keeping secret compiler related things closed.

Not all of Ruby standard library is available. Where overlaps were visible between iOS and Ruby the solution was just to use those. Missing things can be patched with a CocoaPod or a Ruby gem.

Most Ruby gems won't work in the Runtime but they can be used before the compilation process starts. One example being I18n that I used in my project.

Integration testing just sucks in general on iOS. I haven't gotten it working to my liking so I've retired to just manually testing the UI. All of the business logic is tested outside of the RubyMotion app.

RubyMotion handles implementing automatic reference counting so you must watch your references. If you have a cyclical reference you will need to create a WeakRef.new to prevent memory leaks. (This is true of Objective-C as well). Best Ruby practices apply here as well.

RubyMotion Gems!

It's not all doom and gloom. Lets look at some awesome tools people have created!

gem 'rmq'

RubyMotionQuery is a library built to be familiar to anyone who has used the jQuery API.

rmq.append(UILabel).on(:tap) { |sender, event| sender.hide }

rmq(my_view).find(UITextField).hide
          

gem 'motion_model'

Motion Model implements a very ActiveRecord like interface to CoreData. For anyone who has worked with CoreData before, this is a wonderful option.

class Task
  include MotionModel::Model
  include MotionModel::ArrayModelAdapter
  columns     :name => :string
  has_many    :assignees
end

class Assignee
  include MotionModel::Model
  include MotionModel::ArrayModelAdapter
  columns     :assignee_name => :string
  belongs_to  :task
end

a_task = Task.create(:name => "Walk the Dog")
a_task.assignees.create(:assignee_name => "Howard")
          

gem 'bubble-wrap'

This gem is wonderful for wrapping some annoying Cocoa APIs with nice interfaces.

BW::Location.enabled? # Whether location services are enabled on the device
#=> true
BW::Location.authorized? # If your app is authorized to use location services
#=> false

File.join(App.documents_path, 'database.sqlite')
          

gem 'motion-support'

A port of useful features of ActiveSupport.

1.week.ago
17.days.from_now
Date.yesterday
Time.beginning_of_week
          

Thanks for Coming!

Questions? Comments?