Wednesday, July 10, 2013

Baasdayを使ったチャットアプリ制作

はじめまして。
とある事情で群馬からニャンパスへ出向中の白石です。
今回、iOSのチャットアプリを作ることになりました。
とは言うものの、普段はほとんどフロントエンドのプログラムしか書かないのでサーバーを必要とするチャットアプリを作れと言われても、ぱっぱとこなせません。
そこでBaasdayを使いました。

Baasdayとは

ニャンパスが開発を行っているBaas(Backend as a Service)です。
Baasとはサーバーを立てて、サーバーサイドのプログラミングのコードを書かなくても
アプリからBaasサービスのAPIを呼ぶことでユーザー管理やデータ管理などが行えるサービスです。

コレクションを決める

まずチャットアプリに使うコレクションを
  • ユーザー
  • チャットルーム
  • メッセージ
としました。Baasdayでは動的にコレクションやオブジェクトが作成できるので、SQLのように事前にテーブルを作成してカラムを決めておく必要がなくて、ちょっとオブジェクトにキーを追加したいと思ったときでも柔軟に対応できました。

Baasdayの認証

ここから実際にBaasdayを使用していきます。
Baas dayを使うにあたって最初にしなければいけないのアプリケーション認証です。
[Bastardy setApplicationId:applicationId apiKey:apiKey];
applicationIdとapiKeyには管理画面にあるアプリケーションのキー情報を使用します。

ユーザーの作成

ユーザーに関してBaasdayではすでに専用のコレクションが存在しています。
BDAuthenticatedUser *user = [BDAuthenticatedUser createWithValues: @{@”name” : name}];
認証済みユーザーを作成します。ユーザー情報は、アプリケーション認証では更新できず、 ユーザー認証からしか更新できません。2回目以降アプリを起動する際にこの端末のユーザーを取得するために
NSString *key = [user authenticationKey];
で取得した認証キーをKeychainに保存しました。自分はBaasdayを使い始めたころ、認証キーを保存することを知らなかったので、アプリを起動するたびにユーザーが生成されて、かなり使わないユーザーが増えてしまいました・・・。
保存した認証キーからこの端末のユーザーを取得するには
[BDBaasday setUserAuthenticationKey:key];
BDAuthenticatedUser *user = [BDAuthenticatedUser fetch];
で取得できます。

チャットルームの作成

ユーザーの作成が出来たら次はチャットルームを建てます。チャットルームに必要なは入室したユーザーとそのユーザーがまだ入室中かどうかです。自分をチャットルームのuser1としてroomsのオブジェクトを作成します。
BDItem *room = [BDItem createWithCollectionName:@"rooms" values:@{@”user1” : user.id, @”user2” : @””, @”is_user1” : @YES, @”is_user2” : @YES}];
これでチャットルームが作成されました。また、すでに作成されたチャットルームを探すのには
BDQuery *query = [[BDQuery alloc] init];"
query.filter = @{@”user2” : @””};
 BDListResult *rooms = [BDItem fetchAllWithCollectionName:@"rooms" query:query];
if(rooms & rooms.count == 0){
}
で空いているチャットルームを検索できます。チャットルームが見つかったら、すでに使用中であるチャットルームであることを知らせるためにチャットルームを更新します。
[room update:@{@”user2” : user.id, @”is_user2” : @YES}];
ユーザーが退室するときにも、ユーザーが退室したことを知らせるためにroomを更新します。
[room update:@{@”is_user2” : @NO}];
Baasday上のデータを更新しただけではユーザーが退室したことを相手のユーザーは知ることができないので、NSTimerクラスを使用して定期的に確認するようにします。
NSTimer *tm =
     [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(checkLeft:) userInfo:nil repeats:YES];
     [tm fire];
- (void) checkLeft:(NSTimer *)timer{
    room = [BDItem fetchWithCollectionName:@"rooms" id:room.id];
    BOOL is_user = [[room objectforkey@”is_user2”];
    if(is_user){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"お知らせ" message:[NSString stringWithFormat:@"%@さんが退室しました。",[user2 objectFor@”name”] delegate:self cancelButtonTitle:@"確認" otherButtonTitles:nil, nil];
        [alert show];
    }
}

メッセージの作成

チャットルームにユーザーが入室したので、残りはメッセージです。メッセージにはどの部屋のメッセージなのか、誰のメッセージなのかわかるように
[BDItem createWithCollectionName:@"messages" values:@{@"body" : @"text", @"room_id" : room.id, @"user_id" : user.id}];
としました。このメッセージを受け取るのにもNSTimerクラスを使うので、先ほどのcheckLeftメソッドの中に
BDQuery *query = [[BDQuery alloc] init];
//メッセージのソートを作成日時の降順にする
query.order = @[[[BDFieldOrder alloc] initWithField:@"_createdAt" descending:YES]];
//lastMessageが存在している場合はlastMessageの作成日時以降のメッセージを取得
//存在していない場合はすべて取得
if (lastMessage) query.filter = @{@"_createdAt": @{@"$gt": lastMessage.createdAt}, @"room_id" : [room objectForKey:@"_id"]};
else query.filter = @{ @"room_id" : [room objectForKey:@"_id"]};

*result = [BDItem fetchAllWithCollectionName:@"messages" query:query];
receivedMessages = result ? result.contents : nil;

if (receivedMessages & receivedMessages.count == 0) {
    for (id message in receivedMessages.reverseObjectEnumerator) {
        [messages insertObject:message atIndex:0];
    }
    //UITableViewのCellを更新する
    [chatTable reloadData];
    lastMessage = receivedMessages[0];
}
を追加します。

問題点

Baasdayではユーザーが回線落ちしたかどうか判別できないので、その場合部屋にいる別の人に回線落ちして部屋から抜けたことを知らせることができません。なので企画の段階で部屋の人数を制限しないでワイワイガヤガヤするチャットアプリにしていたら、1人くらいいつの間にかいなくなっていても気にならないと思うので良かったかもしれません・・・。

最後に

Baasdayを使ってフロントエンドのみの実装でチャットアプリを作成できました。Baasdayは決められた範囲の機能しかサービスできません。それでも、Baasdayは何ができて、何ができないからこういうものを作ろうと企画すれば、バックエンドに割く時間とコストを押さえられるので、Baasdayを活用する価値はあると思います。