【Swift】 Firebaseでリアルタイム更新のチャット機能を作ってみる【Realtime Database】

Firebaseにはプッシュ通知の機能や、ユーザー認証、リアルタイムデータベースなど様々な機能があります。

今回はFirebaseの最初の製品である「Firebase Real-time Database」を使って簡単かつシンプルなチャットアプリを作りました。

環境

・MacOS Ventura 13.0
・Xcode 14.2
・Swift version 5.7.2

構成、設定等

【Firebase】
・プロジェクト名: SampleProject

【iOS】
・プロジェクト名: SampleFirebase
・画面構成:1画面(チャットの画面のみ)

1. アカウント作成とFirebaseプロジェクトの作成

まずはFirebaseのサイトでアカウントを作成します。

Googleアカウントがあれば簡単に作成可能です。

アカウントの作成が完了したら「使ってみる」をクリックします。


続いてプロジェクトの追加をクリックします。


Firebaseのプロジェクト名を入力して続行を押します。

今回はSampleProjectとしました。


このあとGoogleアナリティクスの有効化画面が表示されますが使用しないため、チェックを外して続行します。

プロジェクトの作成が完了しました。


2. SDK設定

プロジェクトの作成が完了したらiOS+をクリックします。


手順に従って進めていきます。

2-1. ①アプリの登録

AppleのバンドルID: Xcodeから確認します。

アプリのニックネーム(省略可)

App Store ID(省略可)


バンドルIDはXcodeのここから確認可能です。


2-2. ②設定ファイルのダウンロード

GoogleSevice-info.plistをダウンロードしてプロジェクト内にコピーします。


2-3. Firebase SDKの追加

Firebase SDKをiOSのプロジェクト内に導入します。

今回はCocoaPodsを使用しました。CocoaPodsをインストールしてから以下の作業を行います。

例)
cd /Users/ユーザー名/Documents/Projects/SampleFirebase/SampleFirebase.xcodeproj

〇〇.xcodeprojを削除してプロジェクトのフォルダ直下に移動します。

cd /Users/ユーザー名/Documents/Projects/SampleFirebase

プロジェクトフォルダに移動したらプロジェクトフォルダ内にpodfileを作成

pod init

podfileを開く

open podfile


pod file内に[FirebaseDatabase]を追加。

※RealTimeDatabaseのみ利用するため。

pod 'FirebaseDatabase'

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

pod install

これでFirebaseSDKの導入が完了しました。

プロジェクトを一旦閉じて白色のファイルから開き直します。

ファイル名が.xcodeprojではなく.xcworkspaceとなっています。

プロジェクトを開いたら問題なくビルドできるか確認します。


2-4. RealtimeDatabaseの作成

全て完了したのでコンソールに進みます。


Realtime Databaseを探して、データベースを作成を押します。


ロケーションを選択


データベースの設定はテストモードにしました。


これでFirebase側の設定は完了です。

3. チャット機能を作る

ここからはXcodeでチャットの機能を作っていきます。

3-1. まずはじめに

AppDelegateに次の2行を追加します

import FirebaseCore
FirebaseApp.configure()

追加する場所はこちらです。

import UIKit
//追加
import FirebaseCore
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
	//追加        
        FirebaseApp.configure()
        return true
    }
}

3-2. 画面レイアウトの作成

続いてレイアウトの作成していきます。

配置したUIパーツと役割の説明

・TextView → リアルタイム更新のチャットのメッセージ表示
・TextField → 送信者の名前入力
・TextField → 送信者のメッセージの入力
・Button → ボタンタップでメッセージの送信


レイアウトが完成したら右クリックドラッグアンドドロップでOutletとActionをコードに追加します。


TextViewとTextFieldにはOutlet、ButtonにはActionを設定します。

設定が終わったらFirebaseを使うためのコードを書いていきます。

ViewControllerファイルでFirebaseDatabaseをimportします。

import FirebaseDatabase

ルートの指定をします。スクリーンショットの赤色の部分がルートだそうです。



var databaseRef: DatabaseReference!
override func viewDidLoad() {
        super.viewDidLoad()
        //ルートの指定
        databaseRef = Database.database().reference()
    }

これでデータベースにアクセスできるようになりました。

ここからは実際にデータベースを追加してみます。

送信ボタンのAction内で送信者の名前とメッセージの内容を送信しています。

        //TextFieldに入力したテキストをセット
        let messageInfo = ["name": nameTextField.text, "message": messageTextField.text]
        //ルート→chat_room→AutoID(自動でID生成)の下に名前とメッセージデータを書き込み
        self.databaseRef.child("chat_room").child(roomID).childByAutoId().setValue(messageInfo)

新しいデータが追加されたらチャット欄に反映する処理を作成します。

observeはデータの監視、.childAddedの部分は監視する条件の指定(データが追加されたとき)、snapshotは追加データを表します。

func observeChatMessage(databaseRef: DatabaseReference) {
        //snapshotが追加データを表します
        databaseRef.child("chat_room").child(roomID).observe(.childAdded) { snapshot in
            //データのnilチェック、データが存在する場合key:valueの形で受け取ります
            if let value = snapshot.value as? [String: Any] {
                //名前とメッセージを指定したkeyで受け取ります。ない場合は空文字""としています。
                let name = value["name"] as? String ?? ""
                let message = value["message"] as? String ?? ""
                //チャットのメッセージ表示用のTextViewにテキストを追加します。
                self.realTimeChatTextView.text = self.realTimeChatTextView.text + "\n\(name):\(message)"
            }
            
        }
    }

全体のコード

import UIKit
import FirebaseDatabase
class ViewController: UIViewController, UITextFieldDelegate {
    
    var databaseRef: DatabaseReference!
    //チャットルームのID 固定値としました
    let roomID = "room1"
    
    @IBOutlet weak var realTimeChatTextView: UITextView!
    
    @IBOutlet weak var nameTextField: UITextField!
    
    @IBOutlet weak var messageTextField: UITextField!
    
    @IBAction func sendButtonAction(_ sender: Any) {
        //TextFieldに入力したテキストをセット
        let messageInfo = ["name": nameTextField.text, "message": messageTextField.text]
        //ルート→chat_room→AutoID(自動でID生成)の下に名前とメッセージデータを書き込み
        self.databaseRef.child("chat_room").child(roomID).childByAutoId().setValue(messageInfo)
        
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        nameTextField.delegate = self
        messageTextField.delegate = self
        
        //ルート
        databaseRef = Database.database().reference()
        //初回データセット+追加データの監視
        observeChatMessage(databaseRef: databaseRef)
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
            // キーボードを閉じる
            textField.resignFirstResponder()
            return true
    }
    
    func observeChatMessage(databaseRef: DatabaseReference) {
        //snapshotが追加データを表します
        databaseRef.child("chat_room").child(roomID).observe(.childAdded) { snapshot in
            //データのnilチェック、データが存在する場合key:valueの形で受け取ります
            if let value = snapshot.value as? [String: Any] {
                //名前とメッセージを指定したkeyで受け取ります。ない場合は空文字""としています。
                let name = value["name"] as? String ?? ""
                let message = value["message"] as? String ?? ""
                //チャットのメッセージ表示用のTextViewにテキストを追加します。
                self.realTimeChatTextView.text = self.realTimeChatTextView.text + "\n\(name):\(message)"
            }
            
        }
    }
}

3-3. 実行結果

シミュレータ


Firebase


4. まとめ

いかがでしたでしょうか。

今回はFirebaseを使用して簡単なチャット機能を作成してみました。

正直作り始める前はこれだけ少ないコード量で実装できるとは思っていませんでした。

FirebaseであればSQLの知識がなくてもデータを扱えます。

Realtime Databaseを初めて扱う方の参考になれば幸いです。