I Phone On Rails

3,982 views

Published on

iPhone applications can often benefit by talking to a web service to synchronize data or share information with a community. Ruby on Rails, with its RESTful conventions, is an ideal backend for iPhone applications. In this session you'll learn how to use ObjectiveResource in an iPhone application to interact with a RESTful web service implemented in Rails. This session isn't about how to build web applications that are served up on the iPhone. It's about how to build iPhone applications with a native look and feel that happen to talk to Rails applications under the hood. The upshot is a user experience that transcends the device.

Published in: Technology, Design
1 Comment
9 Likes
Statistics
Notes
  • Good video. Thank you very much.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
3,982
On SlideShare
0
From Embeds
0
Number of Embeds
41
Actions
Shares
0
Downloads
93
Comments
1
Likes
9
Embeds 0
No embeds

No notes for slide

I Phone On Rails

  1. 1. iPhone on Rails Mike Clark clarkware.com
  2. 2. (or Conventions Matter)
  3. 3. Hosted Rails App? Fielded iPhone App? Neither?!
  4. 4. Rails Installation $ sudo gem update --system $ sudo gem install rails $ sudo gem update rake $ sudo gem update sqlite3-ruby
  5. 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. 6. Rails Resource Routes ActionController::Routing::Routes.draw do |map| map.resources :expenses end
  7. 7. Rails Resource CRUD Routing POST /expenses/3 GET /expenses PUT /expenses/3 DELETE /expenses/3
  8. 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. 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. 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. 11. Rails CRUD Conventions POST GET PUT DELETE create find update destroy INSERT SELECT UPDATE DELETE
  12. 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. 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. 14. Rails Security Tip class User < ActiveRecord::Base def to_xml(options={}) options[:only] = [:id, :name, :screen_name] super(options) end end
  15. 15. iPhone Scaffolding
  16. 16. iPhone CRUD
  17. 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. 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. 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. 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. 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. 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. 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. 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. 25. ObjectiveResource Configuration #import "ObjectiveResource.h" [ObjectiveResourceConfig setSite:@"http://your-server.com/"]; [ObjectiveResourceConfig setResponseType:JSONResponse];
  26. 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. 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; }
  28. 28. iPhone Nested Resources
  29. 29. Rails Nested Resources ActionController::Routing::Routes.draw do |map| map.resources :budgets, :has_many => :expenses end
  30. 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. 31. ObjectiveResource Nested Resources @implementation Budget @synthesize budgetId; - (NSArray *)findAllExpenses { return [Expense findRemote:[NSString stringWithFormat:@"%@/%@", self.budgetId, @"expenses"]]; } @end GET /budgets/6/expenses
  32. 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
  33. 33. iPhone & Rails Authentication
  34. 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. 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. 36. ObjectiveResource Configuration #import "ObjectiveResource.h" [ObjectiveResourceConfig setSite:@"http://your-server.com/"]; [ObjectiveResourceConfig setResponseType:JSONResponse]; [ObjectiveResourceConfig setUser:@"John"]; [ObjectiveResourceConfig setPassword:@"Appleseed"];
  37. 37. RESTful web services are resource based, stateless, and scaleable
  38. 38. http://github.com/clarkware
  39. 39. Hands-on iPhone and Rails training from the folks who wrote the books pragmaticstudio.com

×