SwiftとFirebaseでiOS向けリアルタイムチャットサービスを作るの続きです。

今回のゴール

  • 前回のチャットにアクセス権限を付与する。
  • 匿名ユーザ同士の、1対1のチャット機能を作成する。

前回のおさらい

おさらい画像

Swiftで簡易的なリアルタイムチャットを実装しました。 今回はこれに1対1の会話機能を実装していきます。

Firebaseのルールの記述

前回変更したルールを元に戻します。 Firebaseのプロジェクトページより、Database > ルールを選択し、内容を以下のように書き換えます。

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

これによって、認証がない状態のアクセスを禁止します。

今回のチャット機能について

今回のチャットの仕様を以下のように定めます。

  • アプリを起動すると自動で認証をおこない、候補ユーザを検索する。
  • 他の候補ユーザが見つかったら、画面上に表示される。
  • 表示されたユーザをタップすると、1対1のチャットが始まる。

今回は書き込みだけではなく、マッチング用のユーザ情報、チャットを行うルーム情報も保存する必要があります。 前回の実装ではデータベースのルートにひたすら書き込みの情報を追加していましたが、今回は「書き込み」「ルーム」「ユーザの」複数の値を分けて格納していきます。

DBは以下のような内容を想定します。

-rules
 -users
  -userId     //ユーザ
   name
   inRoom
 ...
 -rooms
  -roomId     //ルーム
   -messages  //書き込み(ルームに含まれる)
    userId
   	name
   	text
   ...
  ...

匿名アカウントの認証

Podfileに以下を追記します。

pod 'Firebase/Auth'

pod installします。 これによって認証周りの実装が追加されます。

それでは、匿名アカウントとしての認証を実装していきます。 これはFIRAuth.signInAnonymouslyWithCompletion(FIRAuthResultCallback?)によって実装します。

//匿名アカウントを認証する
FIRAuth.auth()?.signInAnonymouslyWithCompletion() { (user, error) in
	if error != nil {
		//エラー時の処理
		return
	}
	//成功時の処理
}

試しに実行してみると、FirebaseのプロジェクトページのAuthから認証されたユーザを確認することができます。

auth

ユーザの追加

前回のメッセージ送信の要領でユーザを追加します。

let userPath = "users"
let userRef = rootRef.child(userPath) //ユーザを保存する場所を指定
let user = ["name": name!,
			"inRoom": isInRoom]
userRef.child(uid).setValue(user)

inRoomのフラグはユーザが会話中かどうかの判定に用います。

他のユーザの表示

ログインが成功したら、他のマッチングしていないユーザを表示します。

これは前回の書き込みの更新と同様に、FIRDatabaseReference.observeEventType(eventType: FIRDataEventType, withBlock block: (FIRDataSnapshot) -> Void) -> UIntを利用します。

let userPath = "users"
let ref = FIRDatabase.database().reference().child(userPath)
ref.observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
let uid = snapshot.key
let name = snapshot.value!["name"] as! String
if ((snapshot.value!["inRoom"] as? Bool) != nil)	// ルームに入っていない
			&& uid != self.auth?.uid {						//自分でない
				//表示するユーザの追加
}

他のユーザを選択

ユーザを選択すると、そのユーザとのチャットが始まります。 選択された側にそれを通知する必要があるため、以下を実装しておきます。

let ref = FIRDatabase.database().reference().child(roomPath)
findUserHandle = ref.observeEventType(FIRDataEventType.ChildAdded, withBlock: { (snapshot) in
  let users = snapshot.value!["user"] as! Array<String>
  if (users.indexOf((self.auth?.uid)!) != nil) {
  self.roomId = snapshot.key
    //選択完了後の処理
    return
  }
})

また、選択した側のユーザは以下を実行します。

let roomRef = self.rootRef.child(roomPath).childByAutoId()
let room = ["user": [(self.auth?.uid)! as String, outcomingUid]]
roomRef.setValue(room)

以上で認証〜チャット開始までの処理が出来上がりました! 実際に動かして確認してみます。

1on1

できました!

まとめ

  • Firebaseはrulesで権限を記述する。
  • FIRDatabaseReference.child(String)でアクセスするデータをうまく指定する。

まだまだ続きます。