Edited at

swiftでリアルタイムチャット

More than 1 year has passed since last update.

今回のチャットを作るにあたってはFirebaseという端末同志のやり取りにリアルタイム性を持たせることに特化したバックエンドサービスを使ってリアルタイムチャットを構築していきます。

Firebaseの説明、使い方はこちらの記事の「アプリケーションを登録」まで同じなのでこちら御覧ください。

http://qiita.com/ryotakodaira/items/e41c3a60348a9e1c7616

UIはJSQMessagesViewControllerというライブラリを使っていきます。

こちらのライブラリですが、素晴らしく美しいです笑


開発環境


  • xcode7.1

  • swift2.1


ライブラリをインストール

ライブラリ
バージョン

JSQMessagesViewController
7.2.0

Firebase
2.4.3

FirebaseとJSQMessagesViewControllerはGithubからダウンロードして手動でインストールするか

CocoaPodsを使ってインストールすることが出来ます。

pod 'JSQMessagesViewController', :git => 'https://github.com/jessesquires/JSQMessagesViewController', :tag => '7.2.0'

pod 'Firebase', '>= 2.4.3'

swiftファイルでインストールしたライブラリを呼び出すには以下のコードを入力します。

import JSQMessagesViewController

import Firebase


Firebaseについて

基本的な文法を紹介していきたいと思います。

もっと詳しい文法についてはFirebase公式サイトで公開されています。(全て英語ですが。。笑)


Firebaseライブラリを読み込む

import Firebase


データベースと接続する

docs-examplesを自分が作成したアプリケーション名に変更します。

var myRootRef = Firebase(url:"https://docs-examples.firebaseio.com/")


データ保存

データベースに対して、setValue関数でデータを保存します。

//使用するDBのパスを定義

//今回の場合はhttps://docs-examples.firebaseio.com/posts になります。
let postRef = myRootRef.childByAppendingPath("posts")

//保存したデータをDictionary型でつくる
let post1 = ["author": "gracehop", "title": "Announcing COBOL, a New Programming Language"]
let post1Ref = postRef.childByAutoId()

//セーブを実行する
post1Ref.setValue(post1)

以下の様なjsonになってDBに保存されます。

{

"posts": {
{
"author": "gracehop",
"title": "Announcing COBOL, a New Programming Language"
}
}
}


データの取得

データベースに何らかのデータ変更があった場合にリアルタイムにコールバック関数が実行され、変更分のデータを取得出来ます。

ref.observeEventType(.ChildAdded, withBlock: { snapshot in

println(snapshot.value.objectForKey("author"))
println(snapshot.value.objectForKey("title"))
})


JSQMessagesViewControllerの初期設定

ViewController.swiftでJSQMessagesViewControllerを使えるようにします。

import JSQMessagesViewController

JSQMessagesViewControllerクラスを継承します。

以降の記述は全てこのクラスに書いていきます。

class ViewController: JSQMessagesViewController{

}

StoryboardからJSQMessagesViewControllerを使いたいページのCustom ClassにViewControllerを指定してください。

これで設定は完了です。


ソースコード


ViewController.swift

import UIKit

import JSQMessagesViewController
import Firebase

class ViewController: JSQMessagesViewController {

var ref: Firebase!

var messages: [JSQMessage]?
var incomingBubble: JSQMessagesBubbleImage!
var outgoingBubble: JSQMessagesBubbleImage!
var incomingAvatar: JSQMessagesAvatarImage!
var outgoingAvatar: JSQMessagesAvatarImage!

func setupFirebase() {

// firebaseのセットアップ
ref = Firebase(url: "https://docs-examples.firebaseio.com/")

// 最新25件のデータをデータベースから取得する
// 最新のデータ追加されるたびに最新データを取得する
ref.queryLimitedToLast(25).observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) in
let text = snapshot.value["text"] as? String
let sender = snapshot.value["from"] as? String
let name = snapshot.value["name"] as? String
print(snapshot.value!)
let message = JSQMessage(senderId: sender, displayName: name, text: text)
self.messages?.append(message)
self.finishReceivingMessage()
})
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
inputToolbar!.contentView!.leftBarButtonItem = nil
automaticallyScrollsToMostRecentMessage = true

//自分のsenderId, senderDisokayNameを設定
self.senderId = "user1"
self.senderDisplayName = "hoge"

//吹き出しの設定
let bubbleFactory = JSQMessagesBubbleImageFactory()
self.incomingBubble = bubbleFactory.incomingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleLightGrayColor())
self.outgoingBubble = bubbleFactory.outgoingMessagesBubbleImageWithColor(UIColor.jsq_messageBubbleBlueColor())

//アバターの設定
self.incomingAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(named: "Swift-Logo")!, diameter: 64)
self.outgoingAvatar = JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(named: "Swift-Logo")!, diameter: 64)

//メッセージデータの配列を初期化
self.messages = []
setupFirebase()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

//Sendボタンが押された時に呼ばれる
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!) {

//メッセージの送信処理を完了する(画面上にメッセージが表示される)
self.finishReceivingMessageAnimated(true)

//firebaseにデータを送信、保存する
let post1 = ["from": senderId, "name": senderDisplayName, "text":text]
let post1Ref = ref.childByAutoId()
post1Ref.setValue(post1)

}

//アイテムごとに参照するメッセージデータを返す
override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
return self.messages?[indexPath.item]
}

//アイテムごとのMessageBubble(背景)を返す
override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = self.messages?[indexPath.item]
if message?.senderId == self.senderId {
return self.outgoingBubble
}
return self.incomingBubble
}

//アイテムごとにアバター画像を返す
override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
let message = self.messages?[indexPath.item]
if message?.senderId == self.senderId {
return self.outgoingAvatar
}
return self.incomingAvatar
}

//アイテムの総数を返す
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (self.messages?.count)!
}

}



実行結果

左側はブラウザから送信していますが、

ブラウザとiOSシュミレーターの間でリアルタイムでテキスト情報の送受信をされているのが分かるかと思います。

iOSシュミレーターの方ではSendをおした後にTextFieldがクリアされないのが課題です。。。


さいごに

今後はサーバーサイドの言語を使ってユーザー認証を付けて、LINEやMessengerの様にユーザーを選んでそのユーザーとのチャットが出来る様なアプリの作り方を共有していきます。

この記事の内容に間違いや質問があればコメントをしていただけたらと思います!