Rails4で、iOS用にクラウドデータベースを作る

2014年あけましておめでとうございます。
私は正月とか関係なくプログラミングの勉強をしている所存ですが、iOSのプログラミングの勉強をしている際に、ネット通信についての本や簡易なドキュメントが少ないと思ったので、Railsを使ってクラウドデータベースとiOSとの通信について簡単に書いていこうと思います。


今回作成するミニアプリは、Railsで作成したBookmarkデータベースにiOSからアクセスして、BookmarkされているWebページのURLを取得、表示するというものです。

Rails4では、自動でJSONでのHTTP通信に対応したアプリケーションを作成してくれるので、初心者にも非常に簡単にクラウドデータベースを作成することができます。


1. Railsアプリを作成する

まずは、Railsのコマンドで簡単にデータストアを作成していきましょう

$ rails new Bookmarker
Takeuchis-MacBook-Air:casts kousuke$ rails new Bookmarker
      create  
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
....
Using uglifier (2.4.0) 
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

URLを保存するBookmarkモデルを作成します。

$ cd Bookmarker
$ rails g scaffold Bookmark title:string bookmark_url:string
      invoke  active_record
      create    db/migrate/20140103123223_create_bookmarks.rb
      create    app/models/bookmark.rb
....

$ rake db:migrate
==  CreateBookmarks: migrating ================================================
-- create_table(:bookmarks)
   -> 0.0058s
==  CreateBookmarks: migrated (0.0059s) =======================================

最後に、ローカル環境で実行します。RailsアプリのURLは"http://localhost:3000"になるので覚えておいてください

$ rails s

2. iOSアプリのインターフェイスを作成する

通常、iOSのUIはxibファイルで作成するのですが、今回は説明を簡単にするためにStroyboardを使って作成していきます。

まずはXcodeでプロジェクトを作成していきましょう。私の環境ではXcode 5を使用しています

i. Master-Detail Applicationを作成

f:id:u651601f:20140103214550p:plain

ii. Storyboardを開いて、DetailのViewにWebViewを追加

f:id:u651601f:20140103214643p:plain

iii. DetailControllerにWebViewのプロパティを追加

f:id:u651601f:20140103214809p:plain

UIに関しては以上です。

3. 試しにRailsのデータをiOSから取得してみる。

さて、いよいよデータ通信をしてみようと思います。
はじめに、サンプルデータを最初に作成したRailsに追加してみましょう。

localhost:3000/bookmarksにアクセス > New Bookmarks > Create Bookmarks で作成します。
f:id:u651601f:20140103215235p:plain

それでは、iOSからこのデータを取得するプログラムを書いていきます。

i. HTTPでデータを取得する

#MasterViewController.h

#import <UIKit/UIKit.h>

@class DetailViewController;

@interface MasterViewController : UITableViewController
@property (strong, nonatomic) NSMutableArray *bookmarks;
- (void)initBookmarks;
@end

#MasterViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    [self initBookmarks];
}

- (void)initBookmarks
{
    // RailsにHTTPリクエストを送信する
    NSURL *url = [NSURL URLWithString:@"http://localhost:3000/bookmarks.json"];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSError *error;
    NSData *bookmarks = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:&error];
    if (!error) {
        // データが取得で来たら、JSONに変換してプロパティに追加
        id json = [NSJSONSerialization JSONObjectWithData:bookmarks options:NSJSONReadingMutableContainers error:nil];
        _bookmarks = [[NSMutableArray alloc] init];
        for (id data in json) {
            NSMutableDictionary *bookmark = [NSMutableDictionary dictionary];
            bookmark[@"title"] = data[@"title"];
            bookmark[@"url"] = data[@"url"];
            [_bookmarks addObject:bookmark];
        }
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"エラー"
                                                        message:@"ネットに接続できません"
                                                       delegate:nil
                                              cancelButtonTitle:nil
                                              otherButtonTitles:@"OK", nil];
        [alert show];
    }
    
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    NSDictionary *bookmark = _bookmarks[indexPath.row];
    cell.textLabel.text = bookmark[@"title"];
    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
    DetailViewController *destination = [segue destinationViewController];
    [destination setUrl:_bookmarks[indexPath.row][@"url"]];
    [destination setTitle:_bookmarks[indexPath.row][@"title"]];
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}

ii. 取得したURLにアクセス

続いて、取得したURLをDetailViewで表示できるようにします。

#DetailViewController.h

#import <UIKit/UIKit.h>

// Webページを表示するために、プロトコルを追加
@interface DetailViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webviewer;
@property (weak, nonatomic) NSString *url;
@end

#DetailViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    _webviewer.delegate = self;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    // Bookmarkのurlを取得
    NSURL *url = [NSURL URLWithString:_url];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];
    NSData *bookmark = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];
    id bookmarkJson = [NSJSONSerialization JSONObjectWithData:bookmark options:NSJSONReadingMutableContainers error:nil];
    
    // Bookmarkのurlにアクセス
    NSURL *bookmarkURL = [NSURL URLWithString:bookmarkJson[@"bookmark_url"]];
    NSURLRequest *webReq = [NSURLRequest requestWithURL:bookmarkURL];
    [_webviewer loadRequest:webReq];
}


## iii. 実行

最後にシミュレータを立ち上げて、確認してみましょう。

きちんとリストに追加されています
f:id:u651601f:20140104014708p:plain

セルをタップすると、Webページにアクセスできます!!
f:id:u651601f:20140104014741p:plain