iPhone用サーバーアプリは ObjectiveResource + Ruby on Rails で超簡単!!

注意 現在ObjectiveResourceは iOS4.0 に対応できてないようです。 詳しくは こちら



iPhoneはゲームのようにiPhoneだけで完結するアプリもありますが、たいていのアプリはサーバーと連携することで大きな可能性を得る事ができると思います。 その際にサーバー側のシステムとiPhone側のサーバーとの通信をどうするかは悩みどころですが、ObjectiveResource + Ruby on Rails を使うと本当に簡単にサーバー側と通信を行う事が出来ます。

サーバー側は Ruby on Rails

ご存知のように、Ruby on Rails を使う事で高機能なWebサイトが短期間で構築できます。

例えば、上の画像のようなToDo を管理するようなWebアプリは 以下の様にコマンドを入力するだけで出来てしまいます。

% rails todo
% cd todo
% ./script/generate scaffold  todo due:datetime task:string
% rake db:migrate  
% ./script/server

しかも、RailsWebサービスとしての機能も持っています。XMLJSONを使ってiPhoneとのデータのやり取りができます。

ObjectiveResource (iPhone側)

iPhoneにはURLを指定してサーバーからデータを取得したり、サーバーに送る API が用意されています。また、XMLのパーサーもあります。しかし、これらを組み合わせてサーバーとのやり取りを行うコードを書くのはそれなりに手間です。
その手間を無くしてくれるのが、 ObjectiveResource です。 ObjectiveResourceを使う Railsのモデルを操作する感じで簡単にサーバーとのやり取りが行えます。

モデルの定義

上のToDoアプリのモデル Todo には 日付 due と タスク task、レコードのid を メンバーを持っていますので Objective-Cで以下のようなモデルを定義します。

Todo.h

#import <Foundation/Foundation.h>
#import "ObjectiveResource.h"

@interface Todo : NSObject {
	NSString *todoId;
	NSDate   *due;
	NSString *task;
}

@property (nonatomic , retain) NSString	*todoId;
@property (nonatomic , retain) NSDate	*due;
@property (nonatomic , retain) NSString	*task;

@end

Todo.m

#import "Todo.h"

@implementation Todo

@synthesize todoId;
@synthesize due;
@synthesize task;

- (NSString *) description {
	return [NSString stringWithFormat:@"due:%@ task:%@", due, task];
}

- (void) dealloc
{
	[todoId release];
	[due release];
	[task release];
	[super dealloc];
}
@end

実装コード実質的には dealloc のみです。description はデバック等で便利なので書いてあります。

一覧表示




右の画像の様に、サーバーからToDoデータを取得し表示してみましょう。



コードは良くある UITableView 表示のコードで
ObjectiveResource と関連するのは下の3カ所だけです!

(1) はサーバーのURL指定。
(2) は Date型データを受け渡すさいのおまじない (何をしてるのかは後ほど書きます)
(3) は Todoデータの全件を取得し todos 配列に代入してます。






#import "TodoViewController.h"
#import "Todo.h"
#import "ObjectiveResourceDateFormatter.h"

@implementation TodoViewController

- (void)viewDidLoad {
	[ObjectiveResourceConfig setSite:@"http://localhost:3000/"]; //  -- (1)
	[ObjectiveResourceDateFormatter setSerializeFormat:DateTime]; //  --  (2)
        [super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated {
	todos = [[Todo findAllRemote] retain]; // --- (3)
	[self.tableView reloadData]; 
	[super viewWillAppear:animated];
}

- (void) viewWillDisappear:(BOOL)animated {
	[todos release];
	[super viewWillDisappear:animated];
}

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


- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	
	static NSString *CellIdentifier = @"Todo";
	
	UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
	if (cell == nil) {
		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
	}
	
	NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
	[dateFormat setDateFormat:@"yyyy/MM/dd"];
	
	Todo *todo = [todos objectAtIndex:indexPath.row];
	cell.textLabel.text = [NSString stringWithFormat:@"%@ : %@", 
						   [dateFormat stringFromDate:todo.due],
						   todo.task];
	cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
	return cell;
}

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
	editViewController.todo = [todos objectAtIndex:indexPath.row];
	[self.navigationController pushViewController:editViewController animated: YES];
}

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	return nil;
}

・・・・

@end
変更画面




右の画像の様に、ToDoデータを変更しサーバーに戻すコードですが、やはりObjectiveResource と関連するのは下の3カ所だけです!

(1) DatePickerから日付を モデル todo に代入
(2) TextFieldからタスクを モデル todo に代入
(3) (1),(2) の値をサーバー側に送る







#import "TodoEditController.h"

@implementation TodoEditController

@synthesize todo;

- (void)viewWillAppear:(BOOL)animated {
	dueDatePicker.date = todo.due;
	taskTextField.text = todo.task;
	[super viewWillAppear:animated];
}

- (IBAction)onPushEditDone:(id)sender {
	todo.due = dueDatePicker.date;   // --  (1)
	todo.task = taskTextField.text;    // --  (2)
	[todo updateRemote];	         // --  (3)
	[self.navigationController popViewControllerAnimated:YES];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
	[textField resignFirstResponder];
	return YES;
}


・・・・

@end


超かんたんでしょ!