SlideShare a Scribd company logo
RubyMotion
iOS Development with Ruby
      @markbates
RubyMotion
RubyMotion
I HATE iOS
DEVELOPMENT!
Why I Hate iOS Development
Why I Hate iOS Development

✦   XCode
Why I Hate iOS Development

✦   XCode
✦   Objective-C
Why I Hate iOS Development

✦   XCode
✦   Objective-C
✦   Cocoa/iOS APIs
RubyMotion
RubyMotion
RubyMotion
RubyMotion
RubyMotion
RubyMotion
RubyMotion
RubyMotion
RubyMotion
XCode
RubyMotion
RubyMotion
RubyMotion
RubyMotion
Objective-C
# Create a new array (mutable):
myArray = %w{hello world etc}
myArray << "new value"

# Create a new hash (mutable):
myHash = {"key-a" => "value-a", "key-b" => "value-b"}
myHash["key-c"] = "value-c"
// Create a new array:
NSArray *myArray = [NSArray arrayWithObjects:@"hello",@"world",@"etc",nil];

// Create a mutable array:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:@"hello world"];

// Create a dictionary (hash):
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
@"value-a", @"key-a", @"value-b", @"key-b", nil];

// Create a mutable dictionary:
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionary];
[myDictionary setObject: @"value-c" forKey: @"key-c"];
now = -> {
  puts "The date and time is: #{Time.now}"
}

now.call
void (^now)(void) = ^ {
  NSDate *date = [NSDate date];
  NSLog(@"The date and time is %@", date);
};

now();
Cocoa/iOS APIs
require 'net/http'
require 'json'

url = "http://www.example.com/api.json"
response = Net::HTTP.get_response(URI.parse(url))

if response.code.to_i == 200
  json = JSON.parse(response.body)
  puts json.inspect
else
  puts "An Error Occurred (#{response.code}): #{response.body}"
end
- (void)viewDidLoad {
  [super viewDidLoad];
  responseData = [[NSMutableData data] retain];
  NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.com/api.json"]];
  [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
  [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  label.text = [NSString stringWithFormat:@"Connection failed: %@", [error description]];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
  [connection release];

  NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
  [responseData release];
  
  NSError *error;
  SBJSON *json = [[SBJSON new] autorelease];
  NSArray *myArray = [json objectWithString:responseString error:&error];
  [responseString release];
  
  if (myArray == nil)
    label.text = [NSString stringWithFormat:@"JSON parsing failed: %@", [error localizedDescription]];
  else {
    NSMutableString *text = [NSMutableString stringWithString:@"Array Data:n"];
    
    for (int i = 0; i < [myArray count]; i++)
      [text appendFormat:@"%@n", [myArray objectAtIndex:i]];

    label.text =   text;
  }
}
RubyMotion
RubyMotion
Laurent Sansonetti

✦   Worked for Apple for 7 years on iLife and
    OS X

✦   Developed MacRuby

✦   Developed RubyCocoa

✦   Belgian
So What Does it Do?
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)

✦   Keep Your Editor (NO XCODE!)
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)

✦   Keep Your Editor (NO XCODE!)

✦   IRB
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)

✦   Keep Your Editor (NO XCODE!)

✦   IRB

✦   Terminal Based Workflow (Rake)
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)

✦   Keep Your Editor (NO XCODE!)

✦   IRB

✦   Terminal Based Workflow (Rake)

✦   Testing Framework (MacBacon)
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)       ✦   Native Applications

✦   Keep Your Editor (NO XCODE!)

✦   IRB

✦   Terminal Based Workflow (Rake)

✦   Testing Framework (MacBacon)
So What Does it Do?
✦   Ruby!!! (NO OBJECTIVE-C!)       ✦   Native Applications

✦   Keep Your Editor (NO XCODE!)    ✦   App Store Approved

✦   IRB

✦   Terminal Based Workflow (Rake)

✦   Testing Framework (MacBacon)
Suck it Objective-C!
NSString *urlAsString = @"http://www.apple.com";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[NSURLConnection
 sendAsynchronousRequest:urlRequest
 queue:queue
 completionHandler:^(NSURLResponse *response,
                     NSData *data,
                     NSError *error) {
   
   if ([data length] >0 &&
       error == nil){
     
     /* Get the documents directory */
     NSString *documentsDir =
     [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                          NSUserDomainMask,
                                          YES) objectAtIndex:0];
     
     /* Append the file name to the documents directory */
     NSString *filePath = [documentsDir
                           stringByAppendingPathComponent:@"apple.html"];
     
     /* Write the data to the file */
     [data writeToFile:filePath
            atomically:YES];
     
     NSLog(@"Successfully saved the file to %@", filePath);
     
   }
   else if ([data length] == 0 &&
            error == nil){
     NSLog(@"Nothing was downloaded.");
   }
   else if (error != nil){
     NSLog(@"Error happened = %@", error);
   }
   
 }];
url = NSURL.URLWithString("http://www.apple.com")
request = NSURLRequest.requestWithURL(url)
queue = NSOperationQueue.alloc.init

NSURLConnection.sendAsynchronousRequest(request,
  queue: queue,
  completionHandler: lambda do |response, data, error|
    if(data.length > 0 && error.nil?)
      doc_dir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                    NSUserDomainMask,
                                                    true).first
      #p doc_dir
      file_path = doc_dir.stringByAppendingPathComponent("apple.html")

      data.writeToFile(file_path, atomically: true)

      p "Saved file to #{file_path}"
    elsif( data.length == 0 && error.nil? )
      p "Nothing was downloaded"
    elsif(!error.nil?)
      p "Error: #{error}"
    end
  end)
But What About Those Weird
Named Argument Methods?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  // do something
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  // do something
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // do something
}
def tableView(tableView, didSelectRowAtIndexPath: indexPath)
  # do something
end

def tableView(tableView, numberOfRowsInSection: section)
  # do something
end

def tableView(tableView, cellForRowAtIndexPath: indexPath)
  # do something
end
Demo Time
Bubble Wrap
https://github.com/rubymotion/BubbleWrap
HTTP
data = {first_name: 'Matt', last_name: 'Aimonetti'}
BubbleWrap::HTTP.post("http://foo.bar.com/", {payload: data}) do |response|
 if response.ok?
   json = BubbleWrap::JSON.parse(response.body.to_str)
   p json['id']
 elsif response.status_code.to_s =~ /40d/
   App.alert("Login failed")
 else
   App.alert(response.error_message)
 end
end
App
> App.documents_path
# "~/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/Documents"
> App.resources_path
# "~/iPhone Simulator/5.0/Applications/EEC64-1816-451E-BB9A-EE18221A8F/testSuite_spec.app"
> App.name
# "testSuite"
> App.identifier
# "io.bubblewrap.testSuite"
> App.alert("BubbleWrap is awesome!")
# creates and shows an alert message.
> App.run_after(0.5) { p "It's #{Time.now}" }
# Runs the block after 0.5 seconds.
> App.open_url("http://matt.aimonetti.net")
# Opens the url using the device's browser. (accepts a string url or an instance of NSURL.
> App::Persistence['channels'] # application specific persistence storage
# ['NBC', 'ABC', 'Fox', 'CBS', 'PBS']
> App::Persistence['channels'] = ['TF1', 'France 2', 'France 3']
# ['TF1', 'France 2', 'France 3']
Device
> Device.iphone?
# true
> Device.ipad?
# false
> Device.front_camera?
# true
> Device.rear_camera?
# true
> Device.orientation
# :portrait
> Device.simulator?
# true
> Device.retina?
# false
> Device.screen.width
# 320
> Device.screen.height
# 480
> Device.screen.width_for_orientation(:landscape_left)
# 480
> Device.screen.height_for_orientation(:landscape_left)
# 320
Camera
# Uses the front camera
BW::Device.camera.front.picture(media_types: [:movie, :image]) do |result|
 image_view = UIImageView.alloc.initWithImage(result[:original_image])
end

# Uses the rear camera
BW::Device.camera.rear.picture(media_types: [:movie, :image]) do |result|
 image_view = UIImageView.alloc.initWithImage(result[:original_image])
end

# Uses the photo library
BW::Device.camera.any.picture(media_types: [:movie, :image]) do |result|
 image_view = UIImageView.alloc.initWithImage(result[:original_image])
end
JSON

BW::JSON.generate({'foo => 1, 'bar' => [1,2,3], 'baz => 'awesome'})
=> "{"foo":1,"bar":[1,2,3],"baz":"awesome"}"
BW::JSON.parse "{"foo":1,"bar":[1,2,3],"baz":"awesome"}"
=> {"foo"=>1, "bar"=>[1, 2, 3], "baz"=>"awesome"}
Media

# Plays in your custom frame
local_file = NSURL.fileURLWithPath(File.join(NSBundle.mainBundle.resourcePath, 'test.mp3'))
BW::Media.play(local_file) do |media_player|
  media_player.view.frame = [[10, 100], [100, 100]]
  self.view.addSubview media_player.view
end

# Plays in an independent modal controller
BW::Media.play_modal("http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3")
Deferrables
> d = EM::DefaultDeferrable.new
=> #<BubbleWrap::Reactor::DefaultDeferrable:0x6d859a0>
> d.callback { |what| puts "Great #{what}!" }
=> [#<Proc:0x6d8a1e0>]
> d.succeed "justice"
Great justice!
=> nil
Events
> o = Class.new { include EM::Eventable }.new
=> #<#<Class:0x6dc1310>:0x6dc2ec0>
> o.on(:november_5_1955) { puts "Ow!" }
=> [#<Proc:0x6dc6300>]
> o.on(:november_5_1955) { puts "Flux capacitor!" }
=> [#<Proc:0x6dc6300>, #<Proc:0x6dc1ba0>]
> o.trigger(:november_5_1955)
Ow!
Flux capacitor!
=> [nil, nil]
More...
✦   Localization                            ✦   Location

✦   Color                                   ✦   Gestures

✦   UUID Generator                          ✦   RSS Parser

✦   NSNotificationCenter                     ✦   Reactor

✦   Persistence                             ✦   Timers

✦   Observers

✦   String (underscore, camelize, etc...)
Pros & Cons
The Pros
The Pros
✦   Ruby!!
The Pros
✦   Ruby!!

✦   IDE Agnostic
The Pros
✦   Ruby!!

✦   IDE Agnostic

✦   “Wrapper” Gems
The Pros
✦   Ruby!!

✦   IDE Agnostic

✦   “Wrapper” Gems

✦   Fast
The Pros
✦   Ruby!!

✦   IDE Agnostic

✦   “Wrapper” Gems

✦   Fast

✦   IRB
The Pros
✦   Ruby!!               ✦   Easy to test

✦   IDE Agnostic

✦   “Wrapper” Gems

✦   Fast

✦   IRB
The Pros
✦   Ruby!!               ✦   Easy to test

✦   IDE Agnostic         ✦   Growing community

✦   “Wrapper” Gems

✦   Fast

✦   IRB
The Pros
✦   Ruby!!               ✦   Easy to test

✦   IDE Agnostic         ✦   Growing community

✦   “Wrapper” Gems       ✦   Frequent updates

✦   Fast

✦   IRB
The Cons
The Cons
✦   Cost - $199
The Cons
✦   Cost - $199

✦   Existing tutorials in Objective-C
The Cons
✦   Cost - $199

✦   Existing tutorials in Objective-C

✦   Maintainability?
The Cons
✦   Cost - $199

✦   Existing tutorials in Objective-C

✦   Maintainability?

✦   No Debugger (Yet)
The Cons
✦   Cost - $199

✦   Existing tutorials in Objective-C

✦   Maintainability?

✦   No Debugger (Yet)

✦   Difficult to work with Interface Builder
The Verdict
Resources
✦   http://www.rubymotion.com            ✦   http://rubymotionapps.com/projects

✦   http://rubymotion-tutorial.com       ✦   http://pragmaticstudio.com/screencasts/
                                             rubymotion
✦   https://github.com/IconoclastLabs/
    rubymotion_cookbook

✦   https://github.com/railsfactory/
    rubymotion-cookbook

✦   https://twitter.com/RubyMotion

✦   http://rubymotionweekly.com

More Related Content

PDF
Turn your spaghetti code into ravioli with JavaScript modules
PDF
Conceitos e prática no desenvolvimento iOS - Mobile Conf 2014
PDF
Es.next
PDF
Webapps without the web
PDF
Alloy Tips & Tricks #TiLon
PDF
Learn You a Functional JavaScript for Great Good
PDF
The Beauty of Java Script
PDF
iPhone Appleless Apps
Turn your spaghetti code into ravioli with JavaScript modules
Conceitos e prática no desenvolvimento iOS - Mobile Conf 2014
Es.next
Webapps without the web
Alloy Tips & Tricks #TiLon
Learn You a Functional JavaScript for Great Good
The Beauty of Java Script
iPhone Appleless Apps

What's hot (20)

PDF
Boost your angular app with web workers
PDF
The Beauty Of Java Script V5a
PDF
Rails for Beginners - Le Wagon
PDF
How to build an AngularJS backend-ready app WITHOUT BACKEND
PDF
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
PDF
SproutCore and the Future of Web Apps
PDF
"Writing Maintainable JavaScript". Jon Bretman, Badoo
PDF
jQuery in 15 minutes
PPTX
Intro to Ember.JS 2016
PDF
Intro to Ember.js
PDF
What's new in iOS9
PDF
jQuery: Events, Animation, Ajax
PDF
Javascript - The Good, the Bad and the Ugly
PDF
Workshop 12: AngularJS Parte I
PPTX
Vue.js + Django - configuración para desarrollo con webpack y HMR
PDF
Bee Smalltalk RunTime: anchor's aweigh
PDF
Refresh Austin - Intro to Dexy
PDF
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
PPTX
YQL & Yahoo! Apis
PDF
Crafting Quality PHP Applications (PHP Benelux 2018)
Boost your angular app with web workers
The Beauty Of Java Script V5a
Rails for Beginners - Le Wagon
How to build an AngularJS backend-ready app WITHOUT BACKEND
James Thomas - Serverless Machine Learning With TensorFlow - Codemotion Berli...
SproutCore and the Future of Web Apps
"Writing Maintainable JavaScript". Jon Bretman, Badoo
jQuery in 15 minutes
Intro to Ember.JS 2016
Intro to Ember.js
What's new in iOS9
jQuery: Events, Animation, Ajax
Javascript - The Good, the Bad and the Ugly
Workshop 12: AngularJS Parte I
Vue.js + Django - configuración para desarrollo con webpack y HMR
Bee Smalltalk RunTime: anchor's aweigh
Refresh Austin - Intro to Dexy
Crafting Quality PHP Applications (Bucharest Tech Week 2017)
YQL & Yahoo! Apis
Crafting Quality PHP Applications (PHP Benelux 2018)
Ad

Similar to RubyMotion (20)

PDF
Native Phone Development 101
PDF
Webエンジニアから見たiOS5
PDF
Developing iOS REST Applications
PDF
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
PDF
Cocoapods and Most common used library in Swift
ZIP
iPhone and Rails integration
PDF
iOS Swift application architecture
KEY
Rubymotion talk
PDF
MFF UK - Introduction to iOS
PDF
iPhone Coding For Web Developers
PDF
Cucumber meets iPhone
PDF
RubyMotion: Hack Your iOS App Like Never Before
KEY
Objective-C Crash Course for Web Developers
PDF
Irving iOS Jumpstart Meetup - Objective-C Session 2
PDF
JSON and the APInauts
PDF
Swift - the future of iOS app development
PDF
iOS 101 - Xcode, Objective-C, iOS APIs
KEY
Hdc09 I Phone Dev Connecting To Web
PDF
Introduction to Swift 2
PDF
Writing Apps with HotCocoa and MacRuby
Native Phone Development 101
Webエンジニアから見たiOS5
Developing iOS REST Applications
Hızlı Cocoa Geliştirme (Develop your next cocoa app faster!)
Cocoapods and Most common used library in Swift
iPhone and Rails integration
iOS Swift application architecture
Rubymotion talk
MFF UK - Introduction to iOS
iPhone Coding For Web Developers
Cucumber meets iPhone
RubyMotion: Hack Your iOS App Like Never Before
Objective-C Crash Course for Web Developers
Irving iOS Jumpstart Meetup - Objective-C Session 2
JSON and the APInauts
Swift - the future of iOS app development
iOS 101 - Xcode, Objective-C, iOS APIs
Hdc09 I Phone Dev Connecting To Web
Introduction to Swift 2
Writing Apps with HotCocoa and MacRuby
Ad

More from Mark (19)

PDF
Building Go Web Apps
PDF
Angular.js Fundamentals
PDF
Go(lang) for the Rubyist
PDF
Mangling Ruby with TracePoint
PDF
AngularJS vs. Ember.js vs. Backbone.js
PDF
A Big Look at MiniTest
PDF
A Big Look at MiniTest
PDF
GET /better
PDF
CoffeeScript
PDF
Testing Your JavaScript & CoffeeScript
PDF
Building an API in Rails without Realizing It
PDF
5 Favorite Gems (Lightning Talk(
KEY
CoffeeScript for the Rubyist
KEY
Testing JavaScript/CoffeeScript with Mocha and Chai
KEY
CoffeeScript for the Rubyist
KEY
Testing Rich Client Side Apps with Jasmine
KEY
DRb and Rinda
KEY
CoffeeScript - A Rubyist's Love Affair
PDF
Distributed Programming with Ruby/Rubyconf 2010
Building Go Web Apps
Angular.js Fundamentals
Go(lang) for the Rubyist
Mangling Ruby with TracePoint
AngularJS vs. Ember.js vs. Backbone.js
A Big Look at MiniTest
A Big Look at MiniTest
GET /better
CoffeeScript
Testing Your JavaScript & CoffeeScript
Building an API in Rails without Realizing It
5 Favorite Gems (Lightning Talk(
CoffeeScript for the Rubyist
Testing JavaScript/CoffeeScript with Mocha and Chai
CoffeeScript for the Rubyist
Testing Rich Client Side Apps with Jasmine
DRb and Rinda
CoffeeScript - A Rubyist's Love Affair
Distributed Programming with Ruby/Rubyconf 2010

Recently uploaded (20)

PPTX
MYSQL Presentation for SQL database connectivity
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
NewMind AI Monthly Chronicles - July 2025
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPT
Teaching material agriculture food technology
PDF
Empathic Computing: Creating Shared Understanding
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
breach-and-attack-simulation-cybersecurity-india-chennai-defenderrabbit-2025....
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Approach and Philosophy of On baking technology
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
Big Data Technologies - Introduction.pptx
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf
PDF
Advanced IT Governance
MYSQL Presentation for SQL database connectivity
Review of recent advances in non-invasive hemoglobin estimation
NewMind AI Monthly Chronicles - July 2025
“AI and Expert System Decision Support & Business Intelligence Systems”
Teaching material agriculture food technology
Empathic Computing: Creating Shared Understanding
The Rise and Fall of 3GPP – Time for a Sabbatical?
Mobile App Security Testing_ A Comprehensive Guide.pdf
breach-and-attack-simulation-cybersecurity-india-chennai-defenderrabbit-2025....
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Approach and Philosophy of On baking technology
NewMind AI Weekly Chronicles - August'25 Week I
Unlocking AI with Model Context Protocol (MCP)
Big Data Technologies - Introduction.pptx
GamePlan Trading System Review: Professional Trader's Honest Take
Chapter 3 Spatial Domain Image Processing.pdf
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf
Advanced IT Governance

RubyMotion

  • 5. Why I Hate iOS Development
  • 6. Why I Hate iOS Development ✦ XCode
  • 7. Why I Hate iOS Development ✦ XCode ✦ Objective-C
  • 8. Why I Hate iOS Development ✦ XCode ✦ Objective-C ✦ Cocoa/iOS APIs
  • 18. XCode
  • 24. # Create a new array (mutable): myArray = %w{hello world etc} myArray << "new value" # Create a new hash (mutable): myHash = {"key-a" => "value-a", "key-b" => "value-b"} myHash["key-c"] = "value-c"
  • 25. // Create a new array: NSArray *myArray = [NSArray arrayWithObjects:@"hello",@"world",@"etc",nil]; // Create a mutable array: NSMutableArray *myArray = [[NSMutableArray alloc] init]; [myArray addObject:@"hello world"]; // Create a dictionary (hash): NSDictionary *myDictionary = [NSDictionary dictionaryWithObjectsAndKeys: @"value-a", @"key-a", @"value-b", @"key-b", nil]; // Create a mutable dictionary: NSMutableDictionary *myDictionary = [NSMutableDictionary dictionary]; [myDictionary setObject: @"value-c" forKey: @"key-c"];
  • 26. now = -> {   puts "The date and time is: #{Time.now}" } now.call
  • 27. void (^now)(void) = ^ {   NSDate *date = [NSDate date];   NSLog(@"The date and time is %@", date); }; now();
  • 29. require 'net/http' require 'json' url = "http://www.example.com/api.json" response = Net::HTTP.get_response(URI.parse(url)) if response.code.to_i == 200   json = JSON.parse(response.body)   puts json.inspect else   puts "An Error Occurred (#{response.code}): #{response.body}" end
  • 30. - (void)viewDidLoad {   [super viewDidLoad];   responseData = [[NSMutableData data] retain];   NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.com/api.json"]];   [[NSURLConnection alloc] initWithRequest:request delegate:self]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {   [responseData setLength:0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {   [responseData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {   label.text = [NSString stringWithFormat:@"Connection failed: %@", [error description]]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection {   [connection release];   NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];   [responseData release];      NSError *error;   SBJSON *json = [[SBJSON new] autorelease];   NSArray *myArray = [json objectWithString:responseString error:&error];   [responseString release];      if (myArray == nil)     label.text = [NSString stringWithFormat:@"JSON parsing failed: %@", [error localizedDescription]];   else {     NSMutableString *text = [NSMutableString stringWithString:@"Array Data:n"];          for (int i = 0; i < [myArray count]; i++)       [text appendFormat:@"%@n", [myArray objectAtIndex:i]];     label.text = text;   } }
  • 33. Laurent Sansonetti ✦ Worked for Apple for 7 years on iLife and OS X ✦ Developed MacRuby ✦ Developed RubyCocoa ✦ Belgian
  • 34. So What Does it Do?
  • 35. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!)
  • 36. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Keep Your Editor (NO XCODE!)
  • 37. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Keep Your Editor (NO XCODE!) ✦ IRB
  • 38. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Keep Your Editor (NO XCODE!) ✦ IRB ✦ Terminal Based Workflow (Rake)
  • 39. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Keep Your Editor (NO XCODE!) ✦ IRB ✦ Terminal Based Workflow (Rake) ✦ Testing Framework (MacBacon)
  • 40. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Native Applications ✦ Keep Your Editor (NO XCODE!) ✦ IRB ✦ Terminal Based Workflow (Rake) ✦ Testing Framework (MacBacon)
  • 41. So What Does it Do? ✦ Ruby!!! (NO OBJECTIVE-C!) ✦ Native Applications ✦ Keep Your Editor (NO XCODE!) ✦ App Store Approved ✦ IRB ✦ Terminal Based Workflow (Rake) ✦ Testing Framework (MacBacon)
  • 43. NSString *urlAsString = @"http://www.apple.com"; NSURL *url = [NSURL URLWithString:urlAsString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [NSURLConnection  sendAsynchronousRequest:urlRequest  queue:queue  completionHandler:^(NSURLResponse *response,                      NSData *data,                      NSError *error) {        if ([data length] >0 &&        error == nil){            /* Get the documents directory */      NSString *documentsDir =      [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,                                           NSUserDomainMask,                                           YES) objectAtIndex:0];            /* Append the file name to the documents directory */      NSString *filePath = [documentsDir                            stringByAppendingPathComponent:@"apple.html"];            /* Write the data to the file */      [data writeToFile:filePath             atomically:YES];            NSLog(@"Successfully saved the file to %@", filePath);          }    else if ([data length] == 0 &&             error == nil){      NSLog(@"Nothing was downloaded.");    }    else if (error != nil){      NSLog(@"Error happened = %@", error);    }      }];
  • 44. url = NSURL.URLWithString("http://www.apple.com") request = NSURLRequest.requestWithURL(url) queue = NSOperationQueue.alloc.init NSURLConnection.sendAsynchronousRequest(request,   queue: queue,   completionHandler: lambda do |response, data, error|     if(data.length > 0 && error.nil?)       doc_dir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,                                                     NSUserDomainMask,                                                     true).first       #p doc_dir       file_path = doc_dir.stringByAppendingPathComponent("apple.html")       data.writeToFile(file_path, atomically: true)       p "Saved file to #{file_path}"     elsif( data.length == 0 && error.nil? )       p "Nothing was downloaded"     elsif(!error.nil?)       p "Error: #{error}"     end   end)
  • 45. But What About Those Weird Named Argument Methods?
  • 46. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {   // do something } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {   // do something } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {   // do something }
  • 47. def tableView(tableView, didSelectRowAtIndexPath: indexPath)   # do something end def tableView(tableView, numberOfRowsInSection: section)   # do something end def tableView(tableView, cellForRowAtIndexPath: indexPath)   # do something end
  • 50. HTTP data = {first_name: 'Matt', last_name: 'Aimonetti'} BubbleWrap::HTTP.post("http://foo.bar.com/", {payload: data}) do |response| if response.ok? json = BubbleWrap::JSON.parse(response.body.to_str) p json['id'] elsif response.status_code.to_s =~ /40d/ App.alert("Login failed") else App.alert(response.error_message) end end
  • 51. App > App.documents_path # "~/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/Documents" > App.resources_path # "~/iPhone Simulator/5.0/Applications/EEC64-1816-451E-BB9A-EE18221A8F/testSuite_spec.app" > App.name # "testSuite" > App.identifier # "io.bubblewrap.testSuite" > App.alert("BubbleWrap is awesome!") # creates and shows an alert message. > App.run_after(0.5) { p "It's #{Time.now}" } # Runs the block after 0.5 seconds. > App.open_url("http://matt.aimonetti.net") # Opens the url using the device's browser. (accepts a string url or an instance of NSURL. > App::Persistence['channels'] # application specific persistence storage # ['NBC', 'ABC', 'Fox', 'CBS', 'PBS'] > App::Persistence['channels'] = ['TF1', 'France 2', 'France 3'] # ['TF1', 'France 2', 'France 3']
  • 52. Device > Device.iphone? # true > Device.ipad? # false > Device.front_camera? # true > Device.rear_camera? # true > Device.orientation # :portrait > Device.simulator? # true > Device.retina? # false > Device.screen.width # 320 > Device.screen.height # 480 > Device.screen.width_for_orientation(:landscape_left) # 480 > Device.screen.height_for_orientation(:landscape_left) # 320
  • 53. Camera # Uses the front camera BW::Device.camera.front.picture(media_types: [:movie, :image]) do |result| image_view = UIImageView.alloc.initWithImage(result[:original_image]) end # Uses the rear camera BW::Device.camera.rear.picture(media_types: [:movie, :image]) do |result| image_view = UIImageView.alloc.initWithImage(result[:original_image]) end # Uses the photo library BW::Device.camera.any.picture(media_types: [:movie, :image]) do |result| image_view = UIImageView.alloc.initWithImage(result[:original_image]) end
  • 54. JSON BW::JSON.generate({'foo => 1, 'bar' => [1,2,3], 'baz => 'awesome'}) => "{"foo":1,"bar":[1,2,3],"baz":"awesome"}" BW::JSON.parse "{"foo":1,"bar":[1,2,3],"baz":"awesome"}" => {"foo"=>1, "bar"=>[1, 2, 3], "baz"=>"awesome"}
  • 55. Media # Plays in your custom frame local_file = NSURL.fileURLWithPath(File.join(NSBundle.mainBundle.resourcePath, 'test.mp3')) BW::Media.play(local_file) do |media_player| media_player.view.frame = [[10, 100], [100, 100]] self.view.addSubview media_player.view end # Plays in an independent modal controller BW::Media.play_modal("http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3")
  • 56. Deferrables > d = EM::DefaultDeferrable.new => #<BubbleWrap::Reactor::DefaultDeferrable:0x6d859a0> > d.callback { |what| puts "Great #{what}!" } => [#<Proc:0x6d8a1e0>] > d.succeed "justice" Great justice! => nil
  • 57. Events > o = Class.new { include EM::Eventable }.new => #<#<Class:0x6dc1310>:0x6dc2ec0> > o.on(:november_5_1955) { puts "Ow!" } => [#<Proc:0x6dc6300>] > o.on(:november_5_1955) { puts "Flux capacitor!" } => [#<Proc:0x6dc6300>, #<Proc:0x6dc1ba0>] > o.trigger(:november_5_1955) Ow! Flux capacitor! => [nil, nil]
  • 58. More... ✦ Localization ✦ Location ✦ Color ✦ Gestures ✦ UUID Generator ✦ RSS Parser ✦ NSNotificationCenter ✦ Reactor ✦ Persistence ✦ Timers ✦ Observers ✦ String (underscore, camelize, etc...)
  • 61. The Pros ✦ Ruby!!
  • 62. The Pros ✦ Ruby!! ✦ IDE Agnostic
  • 63. The Pros ✦ Ruby!! ✦ IDE Agnostic ✦ “Wrapper” Gems
  • 64. The Pros ✦ Ruby!! ✦ IDE Agnostic ✦ “Wrapper” Gems ✦ Fast
  • 65. The Pros ✦ Ruby!! ✦ IDE Agnostic ✦ “Wrapper” Gems ✦ Fast ✦ IRB
  • 66. The Pros ✦ Ruby!! ✦ Easy to test ✦ IDE Agnostic ✦ “Wrapper” Gems ✦ Fast ✦ IRB
  • 67. The Pros ✦ Ruby!! ✦ Easy to test ✦ IDE Agnostic ✦ Growing community ✦ “Wrapper” Gems ✦ Fast ✦ IRB
  • 68. The Pros ✦ Ruby!! ✦ Easy to test ✦ IDE Agnostic ✦ Growing community ✦ “Wrapper” Gems ✦ Frequent updates ✦ Fast ✦ IRB
  • 70. The Cons ✦ Cost - $199
  • 71. The Cons ✦ Cost - $199 ✦ Existing tutorials in Objective-C
  • 72. The Cons ✦ Cost - $199 ✦ Existing tutorials in Objective-C ✦ Maintainability?
  • 73. The Cons ✦ Cost - $199 ✦ Existing tutorials in Objective-C ✦ Maintainability? ✦ No Debugger (Yet)
  • 74. The Cons ✦ Cost - $199 ✦ Existing tutorials in Objective-C ✦ Maintainability? ✦ No Debugger (Yet) ✦ Difficult to work with Interface Builder
  • 76. Resources ✦ http://www.rubymotion.com ✦ http://rubymotionapps.com/projects ✦ http://rubymotion-tutorial.com ✦ http://pragmaticstudio.com/screencasts/ rubymotion ✦ https://github.com/IconoclastLabs/ rubymotion_cookbook ✦ https://github.com/railsfactory/ rubymotion-cookbook ✦ https://twitter.com/RubyMotion ✦ http://rubymotionweekly.com

Editor's Notes