Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
180
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

@ryotakodaira

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

今回のチャットを作るにあたってはFirebaseという端末同志のやり取りにリアルタイム性を持たせることに特化したバックエンドサービスを使ってリアルタイムチャットを構築していきます。
Firebaseの説明、使い方はこちらの記事の「アプリケーションを登録」まで同じなのでこちら御覧ください。
http://qiita.com/ryotakodaira/items/e41c3a60348a9e1c7616

UIはJSQMessagesViewControllerというライブラリを使っていきます。
こちらのライブラリですが、素晴らしく美しいです笑

ioschat 3.gif

開発環境

  • 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を指定してください。

スクリーンショット 2015-12-01 20.44.51.png

これで設定は完了です。

ソースコード

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)!
    }


}

実行結果

ioschat.gif

左側はブラウザから送信していますが、
ブラウザとiOSシュミレーターの間でリアルタイムでテキスト情報の送受信をされているのが分かるかと思います。
iOSシュミレーターの方ではSendをおした後にTextFieldがクリアされないのが課題です。。。

さいごに

今後はサーバーサイドの言語を使ってユーザー認証を付けて、LINEやMessengerの様にユーザーを選んでそのユーザーとのチャットが出来る様なアプリの作り方を共有していきます。
この記事の内容に間違いや質問があればコメントをしていただけたらと思います!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
180
Help us understand the problem. What are the problem?