SlideShare a Scribd company logo
iPhone on Rails




    Mike Clark clarkware.com
(or Conventions Matter)
Hosted Rails App?


Fielded iPhone App?


     Neither?!
Rails
Installation




               $ sudo gem update --system
               $ sudo gem install rails
               $ sudo gem update rake
               $ sudo gem update sqlite3-ruby
Rails
Scaffold App




         $ rails expenses
         $ cd expenses


         $ script/generate scaffold expense 
                name:string amount:decimal


         $ rake db:migrate

         $ script/server



               http://localhost:3000/expenses
Rails Resource
Routes




     ActionController::Routing::Routes.draw do |map|
       map.resources :expenses
     end
Rails Resource
CRUD Routing




                 POST     /expenses/3

                 GET      /expenses

                 PUT      /expenses/3

                 DELETE   /expenses/3
Rails Resource
Controller


             class ExpensesController < ApplicationController

                 # GET /expenses
                 def index
                 end

                 # POST /expenses
                 def create
                 end

                 # GET /expenses/3
                 def show
                 end

                 # PUT /expenses/3
                 def update
                 end

                 # DELETE /expenses/3
                 def destroy
                 end

             end
Rails Resource
Model


   class Expense < ActiveRecord::Base
     validates_presence_of :name
     validates_numericality_of :amount,
                               :greater_than_or_equal_to => 0
   end




   Expense.create(:name => "iPod touch", :amount => 249.00)

   Expense.find(3)

   Expense.update(3, :amount => 199.00)

   Expense.destroy(3)
Rails Resource
View




       <p>
         <b>Name:</b>
         <%=h @expense.name %>
       </p>

       <p>
         <b>Amount:</b>
         <%=h @expense.amount %>
       </p>

       <%= link_to 'Edit', edit_expense_path(@expense) %> |
       <%= link_to 'Back', expenses_path %>
Rails
CRUD Conventions




         POST       GET      PUT     DELETE


        create     find     update   destroy


        INSERT     SELECT   UPDATE   DELETE
>> print Expense.find(3).to_xml

<?xml version="1.0" encoding="UTF-8"?>
<expense>
  <id type="integer">2</id>
  <name>iPod touch</name>
  <amount type="decimal">199.0</amount>
  <created-at type="datetime">2009-09-15T16:01:54Z</created-at>
  <updated-at type="datetime">2009-09-15T16:01:54Z</updated-at>
</expense>




>> print Expense.find(3).to_json

{"expense":
  {"id":2,
    "name":"iPod touch",
    "amount":199.0,
    "created_at":"2009-09-15T16:01:54Z",
    "updated_at":"2009-09-15T16:01:54Z"
  }
}
Rails Resource
Multi-Format Responses



      class ExpensesController < ApplicationController

         def show
           @expense = Expense.find(params[:id])

           respond_to do   |format|
             format.html   # show.html.erb
             format.xml    { render :xml => @expense }
             format.json   { render :json => @expense }
           end
         end

      end




               GET /expenses/3.{html|xml|json}
Rails
Security Tip




       class User < ActiveRecord::Base

          def to_xml(options={})
            options[:only] = [:id, :name, :screen_name]
            super(options)
          end

       end
iPhone
Scaffolding
iPhone
CRUD
iPhone CRUD
Roll Your Own

@implementation Expense

static NSString *siteURL = @"http://localhost:3000";

- (NSString *)params {
    return [NSString stringWithFormat:@"{"expense":{"name":"%@","amount":"%@"}}",
            self.name, self.amount];
}

- (void)createRemote {
    NSString *url = [NSString stringWithFormat:@"%@/expenses.json", siteURL];
    [Resource post:[self params] to:url];
}

+ (NSArray *)findAllRemote {
    NSString *url = [NSString stringWithFormat:@"%@/expenses.json", siteURL];
    NSString *jsonString = [Resource get:url];
    NSArray *expenses = // create expenses from JSON string
    return expenses;
}

- (void)updateRemote {
    NSString *url = [NSString stringWithFormat:@"%@/expenses/%@.json", siteURL, self.expenseId];
    [Resource put:[self params] to:url];
}

- (void)destroyRemote {
    NSString *url = [NSString stringWithFormat:@"%@/expenses/%@.json", siteURL, self.expenseId];
    [Resource delete:url];
}

@end
HTTPRiot

 Supports generic RESTful operations
 Converts JSON response to NSDictionary
 You decide what to do with the data
 Asynchronous



          http://github.com/Caged/httpriot
HTTPRiot
Configuration and Requests


[HRRestModel setDelegate:someObject];
[HRRestModel setBaseURL:[NSURL URLWithString:@"http://your-server/api"]];


[HRRestModel getPath:@"/expenses.json" withOptions:nil object:nil];



NSDictionary *options =
  [NSDictionary dictionaryWithObject:[expense JSONRepresentation] forKey:@"body"];
[HRRestModel postPath:@"/expenses" withOptions: options object:nil];



NSDictionary * options =
  [NSDictionary dictionaryWithObject:[expense JSONRepresentation] forKey:@"body"];
[HRRestModel putPath:@"/expenses/3" withOptions:options object:nil];




[HRRestModel deletePath:@"/expenses/3" withOptions:nil object:nil];
HTTPRiot
Callbacks


    - (void)restConnection:(NSURLConnection *)connection
         didReturnResource:(id)resource
                    object:(id)object {

        for (id item in resource) {
            Expense *expense = [[Expense alloc] initWithDictionary:item];
            // do something with expenses
        }
    }

    - (void)restConnection:(NSURLConnection *)connection
          didFailWithError:(NSError *)error
                    object:(id)object {
        // Handle connection errors
    }

    - (void)restConnection:(NSURLConnection *)connection
           didReceiveError:(NSError *)error
                  response:(NSHTTPURLResponse *)response
                    object:(id)object {
        // Handle invalid responses: 404, 500, and so on
    }

    - (void)restConnection:(NSURLConnection *)connection
      didReceiveParseError:(NSError *)error
              responseBody:(NSString *)string {
        // Request was successful, but couldn't parse the data returned by the server
    }
ASIHTTPRequest

 Super flexible with lots of features
 File uploads, progress indictors, etc.
 Uses CFNetwork API
 Asynchronous


        http://github.com/pokeb/asi-http-request
ASIHTTPRequest
Usage




 - (void)sendRequest {
    NSURL *url = [NSURL URLWithString:@"http://your-server.com/expenses.json"];
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url];
    [request setDelegate:self];
    [request setDidFinishSelector:@selector(requestSucceeded:)];
    [request setDidFailSelector:@selector(requestFailed:)];
    [[self queue] addOperation:request];
    [request release];
 }

 - (void)requestSucceeded:(ASIHTTPRequest *)request {
    NSString *response = [request responseString];
 }

 - (void)requestFailed:(ASIHTTPRequest *)request {
    NSError *error = [request error];
 }
ObjectiveResource

 Objective-C port of ActiveResource
 Serializes to/from objects (XML or JSON)
 Assumes Rails RESTful conventions
 Asynchronous option in 1.1 branch


      http://github.com/yfactorial/objectiveresource
ObjectiveResource
Remote Resource


         #import "ObjectiveResource.h"

         @interface Expense : NSObject {
             NSString *expenseId;
             NSString *name;
             NSString *amount;
             NSDate   *createdAt;
             NSDate   *updatedAt;
         }

         @property   (nonatomic,   copy)     NSString   *expenseId;
         @property   (nonatomic,   copy)     NSString   *name;
         @property   (nonatomic,   copy)     NSString   *amount;
         @property   (nonatomic,   retain)   NSDate     *createdAt;
         @property   (nonatomic,   retain)   NSDate     *updatedAt;

         @end




                                                  * NSObject+ObjectiveResource category
ObjectiveResource
Configuration




 #import "ObjectiveResource.h"

 [ObjectiveResourceConfig setSite:@"http://your-server.com/"];
 [ObjectiveResourceConfig setResponseType:JSONResponse];
ObjectiveResource
CRUD Operations




        NSArray *expenses = [Expense findAllRemote];


        Expense *expense = [[Expense alloc] init];
        expense.name = @"iPod touch";
        [expense createRemote];


        Expense *expense = [Expense findRemote:@"3"];
        expense.name = @"iPhone";
        [expense updateRemote];



        [expense destroyRemote];
ObjectiveResource
Asynchronous Way




     [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
     [[ConnectionManager sharedInstance] runJob:@selector(loadExpenses)
                                       onTarget:self];



                                                * NSInvocationOperation in an NSOperationQueue




 - (void)loadExpenses {
     self.expenses = [Expense findAllRemote];

       [self.tableView performSelectorOnMainThread:@selector(reloadData)
                                        withObject:nil
                                     waitUntilDone:NO];

       [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 }
iPhone
Nested Resources
Rails
Nested Resources




     ActionController::Routing::Routes.draw do |map|
       map.resources :budgets, :has_many => :expenses
     end
Rails
Nested Resource


      class ExpensesController < ApplicationController

        before_filter :find_budget

        def show
          @expense = @budget.expenses.find(params[:id])

          respond_to do |format|
            format.html
            format.xml { render :xml => @expense }
            format.json { render :json => @expense }
          end
        end

      private

        def find_budget
          @budget = current_user.budgets.find(params[:budget_id])
        end

      end


                   GET /budgets/6/expenses
ObjectiveResource
Nested Resources




    @implementation Budget

    @synthesize budgetId;

    - (NSArray *)findAllExpenses {
      return [Expense findRemote:[NSString stringWithFormat:@"%@/%@",
                                      self.budgetId, @"expenses"]];
    }

    @end




                    GET /budgets/6/expenses
ObjectiveResource
Nested Resources

     @implementation Expense

     @synthesize expenseId;
     @synthesize budgetId;

     + (NSString *)getRemoteCollectionName {
         return @"budgets";
     }

     - (NSString *)nestedPath {
         NSString *path = [NSString stringWithFormat:@"%@/expenses", self.budgetId];
         if (self.expenseId) {
             path = [path stringByAppendingFormat:@"/%@", self.expenseId];
         }
         return path;
     }

     - (BOOL)createRemoteWithResponse:(NSError **)aError {
         return [self createRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]]
                            withResponse:aError];
     }

     - (BOOL)updateRemoteWithResponse:(NSError **)aError {
         return [self updateRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]]
                            withResponse:aError];
     }

     - (BOOL)destroyRemoteWithResponse:(NSError **)aError {
         return [self destroyRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]]
                             withResponse:aError];
     }

     @end
                                                                               * yeah, itʼs kinda clunky
iPhone & Rails
Authentication
Rails
Authentication



   class BudgetsController < ApplicationController

      before_filter :authenticate

      def index
        @budgets = current_user.budgets.all
      end

      def show
        @budget = current_user.budgets.find(params[:id])
      end

   private

      def current_user
        @current_user ||= User.find(session[:user_id])
      end

   end
Rails
Authentication


     def authenticate
       return if session[:user_id]

       respond_to do |format|
         format.html do
           redirect_to login_url
         end
         format.any(:xml, :json) do
           user = authenticate_with_http_basic do |username, password|
             User.authenticate(username, password)
           end
           if user
             session[:user_id] = user.id
           else
             request_http_basic_authentication
           end
         end
       end
     end
ObjectiveResource
Configuration




#import "ObjectiveResource.h"

[ObjectiveResourceConfig   setSite:@"http://your-server.com/"];
[ObjectiveResourceConfig   setResponseType:JSONResponse];
[ObjectiveResourceConfig   setUser:@"John"];
[ObjectiveResourceConfig   setPassword:@"Appleseed"];
RESTful web services are
resource based, stateless,
      and scaleable
http://github.com/clarkware
Hands-on iPhone and Rails
training from the folks who
      wrote the books

       pragmaticstudio.com

More Related Content

PDF
Introducing Assetic (NYPHP)
PDF
Silex Cheat Sheet
PDF
Advanced symfony Techniques
PDF
Doctrine MongoDB ODM (PDXPHP)
PPT
Slim RedBeanPHP and Knockout
PDF
Keeping it Small: Getting to know the Slim Micro Framework
PDF
SPL: The Missing Link in Development
PPTX
A Functional Guide to Cat Herding with PHP Generators
Introducing Assetic (NYPHP)
Silex Cheat Sheet
Advanced symfony Techniques
Doctrine MongoDB ODM (PDXPHP)
Slim RedBeanPHP and Knockout
Keeping it Small: Getting to know the Slim Micro Framework
SPL: The Missing Link in Development
A Functional Guide to Cat Herding with PHP Generators

What's hot (20)

PDF
Ethiopian multiplication in Perl6
KEY
PDF
With a Mighty Hammer
KEY
ISUCONアプリを Pythonで書いてみた
PDF
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
ODP
KEY
SQLite Techniques
PDF
Building Cloud Castles
PDF
A Little Backbone For Your App
PDF
Symfony tips and tricks
PDF
Keeping it small - Getting to know the Slim PHP micro framework
PPTX
A Functional Guide to Cat Herding with PHP Generators
PDF
Controlling The Cloud With Python
PPTX
Webrtc mojo
PPT
Nashvile Symfony Routes Presentation
KEY
Keeping it small: Getting to know the Slim micro framework
PDF
The State of Lithium
PDF
Perl6 Regexen: Reduce the line noise in your code.
PDF
To Batch Or Not To Batch
PDF
Forget about loops
Ethiopian multiplication in Perl6
With a Mighty Hammer
ISUCONアプリを Pythonで書いてみた
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
SQLite Techniques
Building Cloud Castles
A Little Backbone For Your App
Symfony tips and tricks
Keeping it small - Getting to know the Slim PHP micro framework
A Functional Guide to Cat Herding with PHP Generators
Controlling The Cloud With Python
Webrtc mojo
Nashvile Symfony Routes Presentation
Keeping it small: Getting to know the Slim micro framework
The State of Lithium
Perl6 Regexen: Reduce the line noise in your code.
To Batch Or Not To Batch
Forget about loops
Ad

Viewers also liked (19)

PPTX
Clase de RoboCup
PPTX
Internet como fuente fiable de información económica
PDF
Silicon Solar Brochure - EnviroLight Solar Billboard Lighting Systems
PDF
Strategic innovation in financial sector: Blockchain and the case of Spanish ...
DOC
Joseph Martin CV
PDF
Identity Commons Presentation
PPTX
Autobiografía de Ainilda Patricia Ávila Laguna
PDF
Proyecto la recogida del olivo
PDF
Brochure P2S
DOC
Stopyra Resume
PDF
OSGi - Four Years and Forward - J Barr
PDF
El proceso de romanización de la provincia de huesca Labitolosa y La Vispera...
PPTX
PDF
José María de las Heras - La integridad documental de los proyectos de ingeni...
PDF
A new class of inkjet with HP PageWide Web Presses at drupa 2016
 
PPTX
OneDrive for Business vs Arbeitsordner
PPTX
Como configurar una IP Estatica en Windows XP y 7
PDF
Lenguaje y tdah
PPT
Chapter 2 overview
Clase de RoboCup
Internet como fuente fiable de información económica
Silicon Solar Brochure - EnviroLight Solar Billboard Lighting Systems
Strategic innovation in financial sector: Blockchain and the case of Spanish ...
Joseph Martin CV
Identity Commons Presentation
Autobiografía de Ainilda Patricia Ávila Laguna
Proyecto la recogida del olivo
Brochure P2S
Stopyra Resume
OSGi - Four Years and Forward - J Barr
El proceso de romanización de la provincia de huesca Labitolosa y La Vispera...
José María de las Heras - La integridad documental de los proyectos de ingeni...
A new class of inkjet with HP PageWide Web Presses at drupa 2016
 
OneDrive for Business vs Arbeitsordner
Como configurar una IP Estatica en Windows XP y 7
Lenguaje y tdah
Chapter 2 overview
Ad

Similar to I Phone On Rails (20)

PDF
Rails 3 overview
PDF
Silex Cheat Sheet
PDF
Rails 3: Dashing to the Finish
PDF
Doctrine For Beginners
PDF
Bootstrat REST APIs with Laravel 5
PDF
Bag Of Tricks From Iusethis
KEY
SQLite Techniques
KEY
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
KEY
Zend framework service
KEY
Zend framework service
PDF
Building Lithium Apps
KEY
Phpne august-2012-symfony-components-friends
PDF
Symfony2 from the Trenches
ODP
Aura Project for PHP
PDF
WordPress REST API hacking
PDF
関西PHP勉強会 php5.4つまみぐい
PPT
GHC Participant Training
PDF
WordPress REST API hacking
KEY
Zend Framework Study@Tokyo #2
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Rails 3 overview
Silex Cheat Sheet
Rails 3: Dashing to the Finish
Doctrine For Beginners
Bootstrat REST APIs with Laravel 5
Bag Of Tricks From Iusethis
SQLite Techniques
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Zend framework service
Zend framework service
Building Lithium Apps
Phpne august-2012-symfony-components-friends
Symfony2 from the Trenches
Aura Project for PHP
WordPress REST API hacking
関西PHP勉強会 php5.4つまみぐい
GHC Participant Training
WordPress REST API hacking
Zend Framework Study@Tokyo #2
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)

More from John Wilker (20)

PDF
Cranking Floating Point Performance Up To 11
PDF
Introtoduction to cocos2d
PDF
Getting Started with OpenGL ES
PDF
User Input in a multi-touch, accelerometer, location aware world.
PDF
Physics Solutions for Innovative Game Design
PDF
Getting Oriented with MapKit: Everything you need to get started with the new...
PDF
Getting Started with iPhone Game Development
PDF
Internationalizing Your Apps
PDF
Optimizing Data Caching for iPhone Application Responsiveness
PDF
Integrating Push Notifications in your iPhone application with iLime
PDF
Starting Core Animation
PDF
P2P Multiplayer Gaming
PDF
Using Concurrency To Improve Responsiveness
PDF
Leaving Interface Builder Behind
PDF
Mobile WebKit Development and jQTouch
PDF
Accelerometer and OpenGL
PDF
Deep Geek Diving into the iPhone OS and Framework
PDF
NSNotificationCenter vs. AppDelegate
PDF
Using SQLite
PDF
From Flash to iPhone
Cranking Floating Point Performance Up To 11
Introtoduction to cocos2d
Getting Started with OpenGL ES
User Input in a multi-touch, accelerometer, location aware world.
Physics Solutions for Innovative Game Design
Getting Oriented with MapKit: Everything you need to get started with the new...
Getting Started with iPhone Game Development
Internationalizing Your Apps
Optimizing Data Caching for iPhone Application Responsiveness
Integrating Push Notifications in your iPhone application with iLime
Starting Core Animation
P2P Multiplayer Gaming
Using Concurrency To Improve Responsiveness
Leaving Interface Builder Behind
Mobile WebKit Development and jQTouch
Accelerometer and OpenGL
Deep Geek Diving into the iPhone OS and Framework
NSNotificationCenter vs. AppDelegate
Using SQLite
From Flash to iPhone

Recently uploaded (20)

PPT
Teaching material agriculture food technology
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Modernizing your data center with Dell and AMD
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Big Data Technologies - Introduction.pptx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Approach and Philosophy of On baking technology
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Electronic commerce courselecture one. Pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Teaching material agriculture food technology
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Modernizing your data center with Dell and AMD
Agricultural_Statistics_at_a_Glance_2022_0.pdf
“AI and Expert System Decision Support & Business Intelligence Systems”
Big Data Technologies - Introduction.pptx
Understanding_Digital_Forensics_Presentation.pptx
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
NewMind AI Weekly Chronicles - August'25 Week I
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Approach and Philosophy of On baking technology
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Electronic commerce courselecture one. Pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
Building Integrated photovoltaic BIPV_UPV.pdf
Bridging biosciences and deep learning for revolutionary discoveries: a compr...

I Phone On Rails

  • 1. iPhone on Rails Mike Clark clarkware.com
  • 3. Hosted Rails App? Fielded iPhone App? Neither?!
  • 4. Rails Installation $ sudo gem update --system $ sudo gem install rails $ sudo gem update rake $ sudo gem update sqlite3-ruby
  • 5. Rails Scaffold App $ rails expenses $ cd expenses $ script/generate scaffold expense name:string amount:decimal $ rake db:migrate $ script/server http://localhost:3000/expenses
  • 6. Rails Resource Routes ActionController::Routing::Routes.draw do |map| map.resources :expenses end
  • 7. Rails Resource CRUD Routing POST /expenses/3 GET /expenses PUT /expenses/3 DELETE /expenses/3
  • 8. Rails Resource Controller class ExpensesController < ApplicationController # GET /expenses def index end # POST /expenses def create end # GET /expenses/3 def show end # PUT /expenses/3 def update end # DELETE /expenses/3 def destroy end end
  • 9. Rails Resource Model class Expense < ActiveRecord::Base validates_presence_of :name validates_numericality_of :amount, :greater_than_or_equal_to => 0 end Expense.create(:name => "iPod touch", :amount => 249.00) Expense.find(3) Expense.update(3, :amount => 199.00) Expense.destroy(3)
  • 10. Rails Resource View <p> <b>Name:</b> <%=h @expense.name %> </p> <p> <b>Amount:</b> <%=h @expense.amount %> </p> <%= link_to 'Edit', edit_expense_path(@expense) %> | <%= link_to 'Back', expenses_path %>
  • 11. Rails CRUD Conventions POST GET PUT DELETE create find update destroy INSERT SELECT UPDATE DELETE
  • 12. >> print Expense.find(3).to_xml <?xml version="1.0" encoding="UTF-8"?> <expense> <id type="integer">2</id> <name>iPod touch</name> <amount type="decimal">199.0</amount> <created-at type="datetime">2009-09-15T16:01:54Z</created-at> <updated-at type="datetime">2009-09-15T16:01:54Z</updated-at> </expense> >> print Expense.find(3).to_json {"expense": {"id":2, "name":"iPod touch", "amount":199.0, "created_at":"2009-09-15T16:01:54Z", "updated_at":"2009-09-15T16:01:54Z" } }
  • 13. Rails Resource Multi-Format Responses class ExpensesController < ApplicationController def show @expense = Expense.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @expense } format.json { render :json => @expense } end end end GET /expenses/3.{html|xml|json}
  • 14. Rails Security Tip class User < ActiveRecord::Base def to_xml(options={}) options[:only] = [:id, :name, :screen_name] super(options) end end
  • 17. iPhone CRUD Roll Your Own @implementation Expense static NSString *siteURL = @"http://localhost:3000"; - (NSString *)params { return [NSString stringWithFormat:@"{"expense":{"name":"%@","amount":"%@"}}", self.name, self.amount]; } - (void)createRemote { NSString *url = [NSString stringWithFormat:@"%@/expenses.json", siteURL]; [Resource post:[self params] to:url]; } + (NSArray *)findAllRemote { NSString *url = [NSString stringWithFormat:@"%@/expenses.json", siteURL]; NSString *jsonString = [Resource get:url]; NSArray *expenses = // create expenses from JSON string return expenses; } - (void)updateRemote { NSString *url = [NSString stringWithFormat:@"%@/expenses/%@.json", siteURL, self.expenseId]; [Resource put:[self params] to:url]; } - (void)destroyRemote { NSString *url = [NSString stringWithFormat:@"%@/expenses/%@.json", siteURL, self.expenseId]; [Resource delete:url]; } @end
  • 18. HTTPRiot Supports generic RESTful operations Converts JSON response to NSDictionary You decide what to do with the data Asynchronous http://github.com/Caged/httpriot
  • 19. HTTPRiot Configuration and Requests [HRRestModel setDelegate:someObject]; [HRRestModel setBaseURL:[NSURL URLWithString:@"http://your-server/api"]]; [HRRestModel getPath:@"/expenses.json" withOptions:nil object:nil]; NSDictionary *options = [NSDictionary dictionaryWithObject:[expense JSONRepresentation] forKey:@"body"]; [HRRestModel postPath:@"/expenses" withOptions: options object:nil]; NSDictionary * options = [NSDictionary dictionaryWithObject:[expense JSONRepresentation] forKey:@"body"]; [HRRestModel putPath:@"/expenses/3" withOptions:options object:nil]; [HRRestModel deletePath:@"/expenses/3" withOptions:nil object:nil];
  • 20. HTTPRiot Callbacks - (void)restConnection:(NSURLConnection *)connection didReturnResource:(id)resource object:(id)object { for (id item in resource) { Expense *expense = [[Expense alloc] initWithDictionary:item]; // do something with expenses } } - (void)restConnection:(NSURLConnection *)connection didFailWithError:(NSError *)error object:(id)object { // Handle connection errors } - (void)restConnection:(NSURLConnection *)connection didReceiveError:(NSError *)error response:(NSHTTPURLResponse *)response object:(id)object { // Handle invalid responses: 404, 500, and so on } - (void)restConnection:(NSURLConnection *)connection didReceiveParseError:(NSError *)error responseBody:(NSString *)string { // Request was successful, but couldn't parse the data returned by the server }
  • 21. ASIHTTPRequest Super flexible with lots of features File uploads, progress indictors, etc. Uses CFNetwork API Asynchronous http://github.com/pokeb/asi-http-request
  • 22. ASIHTTPRequest Usage - (void)sendRequest { NSURL *url = [NSURL URLWithString:@"http://your-server.com/expenses.json"]; ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url]; [request setDelegate:self]; [request setDidFinishSelector:@selector(requestSucceeded:)]; [request setDidFailSelector:@selector(requestFailed:)]; [[self queue] addOperation:request]; [request release]; } - (void)requestSucceeded:(ASIHTTPRequest *)request { NSString *response = [request responseString]; } - (void)requestFailed:(ASIHTTPRequest *)request { NSError *error = [request error]; }
  • 23. ObjectiveResource Objective-C port of ActiveResource Serializes to/from objects (XML or JSON) Assumes Rails RESTful conventions Asynchronous option in 1.1 branch http://github.com/yfactorial/objectiveresource
  • 24. ObjectiveResource Remote Resource #import "ObjectiveResource.h" @interface Expense : NSObject { NSString *expenseId; NSString *name; NSString *amount; NSDate *createdAt; NSDate *updatedAt; } @property (nonatomic, copy) NSString *expenseId; @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *amount; @property (nonatomic, retain) NSDate *createdAt; @property (nonatomic, retain) NSDate *updatedAt; @end * NSObject+ObjectiveResource category
  • 25. ObjectiveResource Configuration #import "ObjectiveResource.h" [ObjectiveResourceConfig setSite:@"http://your-server.com/"]; [ObjectiveResourceConfig setResponseType:JSONResponse];
  • 26. ObjectiveResource CRUD Operations NSArray *expenses = [Expense findAllRemote]; Expense *expense = [[Expense alloc] init]; expense.name = @"iPod touch"; [expense createRemote]; Expense *expense = [Expense findRemote:@"3"]; expense.name = @"iPhone"; [expense updateRemote]; [expense destroyRemote];
  • 27. ObjectiveResource Asynchronous Way [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; [[ConnectionManager sharedInstance] runJob:@selector(loadExpenses) onTarget:self]; * NSInvocationOperation in an NSOperationQueue - (void)loadExpenses { self.expenses = [Expense findAllRemote]; [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; }
  • 29. Rails Nested Resources ActionController::Routing::Routes.draw do |map| map.resources :budgets, :has_many => :expenses end
  • 30. Rails Nested Resource class ExpensesController < ApplicationController before_filter :find_budget def show @expense = @budget.expenses.find(params[:id]) respond_to do |format| format.html format.xml { render :xml => @expense } format.json { render :json => @expense } end end private def find_budget @budget = current_user.budgets.find(params[:budget_id]) end end GET /budgets/6/expenses
  • 31. ObjectiveResource Nested Resources @implementation Budget @synthesize budgetId; - (NSArray *)findAllExpenses { return [Expense findRemote:[NSString stringWithFormat:@"%@/%@", self.budgetId, @"expenses"]]; } @end GET /budgets/6/expenses
  • 32. ObjectiveResource Nested Resources @implementation Expense @synthesize expenseId; @synthesize budgetId; + (NSString *)getRemoteCollectionName { return @"budgets"; } - (NSString *)nestedPath { NSString *path = [NSString stringWithFormat:@"%@/expenses", self.budgetId]; if (self.expenseId) { path = [path stringByAppendingFormat:@"/%@", self.expenseId]; } return path; } - (BOOL)createRemoteWithResponse:(NSError **)aError { return [self createRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]] withResponse:aError]; } - (BOOL)updateRemoteWithResponse:(NSError **)aError { return [self updateRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]] withResponse:aError]; } - (BOOL)destroyRemoteWithResponse:(NSError **)aError { return [self destroyRemoteAtPath:[[self class] getRemoteElementPath:[self nestedPath]] withResponse:aError]; } @end * yeah, itʼs kinda clunky
  • 34. Rails Authentication class BudgetsController < ApplicationController before_filter :authenticate def index @budgets = current_user.budgets.all end def show @budget = current_user.budgets.find(params[:id]) end private def current_user @current_user ||= User.find(session[:user_id]) end end
  • 35. Rails Authentication def authenticate return if session[:user_id] respond_to do |format| format.html do redirect_to login_url end format.any(:xml, :json) do user = authenticate_with_http_basic do |username, password| User.authenticate(username, password) end if user session[:user_id] = user.id else request_http_basic_authentication end end end end
  • 36. ObjectiveResource Configuration #import "ObjectiveResource.h" [ObjectiveResourceConfig setSite:@"http://your-server.com/"]; [ObjectiveResourceConfig setResponseType:JSONResponse]; [ObjectiveResourceConfig setUser:@"John"]; [ObjectiveResourceConfig setPassword:@"Appleseed"];
  • 37. RESTful web services are resource based, stateless, and scaleable
  • 39. Hands-on iPhone and Rails training from the folks who wrote the books pragmaticstudio.com