【FireBase】 FireStoreで簡単なぼっちSNSアプリを作ってみました

現在のアプリ開発ではiOS、Android共にデータ管理にRealmを使用して開発を行っています。慣れてしまえば一通りのことは行えるのですが、やはり様々な点でもう少しこうはならないだろうかという点はちらほら。

昨今ではFireStoreというモバイルアプリケーション向けデータベースが結構主流らしく、Firebaseは通知機能でしかお世話になってなかった身としては気になりますし、通知含めそういったアプリに関する設計をFirebaseでまとめて管理できるのであれば今後の効率もよくなる(はず)と思い今回の社内勉強会を機に少し触れてみました。


1.FireBase側にデータベースを作成する

まずは自身のFirebaseプロジェクトにデータベースを作成します。
※その前に今回作成するアプリの情報をFirebaseに登録するのを忘れずに

今回は社内で発表するためのアプリケーションなので、特にアクセスなどの制限を設ける必要はないのでテストモードを使用します。


2.FireStoreの構造

FireStoreではcollectionという箱の中にdocumentという形でデータが入っており、顧客管理などでは顧客(user)collectionの中に、顧客の数だけdocumentが存在する形になります。

documentの中には、JSON形式のようにkeyとvalueの組み合わせで値を保存することが可能です。
設計画面自体もかなりシンプルなので、結構直感的にデータの追加や削除を行うことができそうです。
また、型などは画面で作成する際には指定が必要ですがアプリからデータを送信する場合には特に型指定の必要がなく、自動で変換してくれるためアプリ側からは[String:Any]の様なDictionary型でまとめて送ってしまえばあとは自動で型を整えてくれるみたいですね。


3.送受信テストのため、一人呟きのSNSアプリ試しに作成

実際にアプリからデータを送信、そしてそれを受け取り表示するという仕組みを作ろうと思います。ただ勉強会自体がだいたい5時間前後なのでそこまで深いのは作れないのでとりあえず、「名前とアイコンを決めて投稿する」「最新のデータが追加されたら即時反映する」くらいのチャットもどきのお手軽一人SNSを作ろうかと思います。

「fireStoreTest」というアプリを作成し(作成したアプリをfirebaseに登録するのを忘れずに)
podには今回メインで使用いたしますFirebase/CoreおよびFirebase/FireStoreを記載し、installを行います。


3-1.画面を作成

起動したらまず自分の名前と、アバターアイコンを決めます。
決めたらチャット画面に移動し、そのユーザーとして投稿を行える...という形を目指します。


3-2.必要な仕組みを実装

 - 呟きを新規で投稿する
 - 呟きをリアルタイム監視し、何かあったら最新に更新する

とりあえずこの2つを実装します。削除や編集機能も入れたいですが時間の都合上割愛...

準備

FireStoreを使う上で最低限必要な初期設定を行います。

import UIKit
import Firebase
import FirebaseAuth

class MainViewController: UIViewController, UITextFieldDelegate {
    
    var defaultstore:Firestore! // ←これ
    var messageBox:[[String:Any]] = []
    var handle:Auth?
    
    @IBOutlet weak var messageText: UITextField!
    @IBOutlet weak var messageTableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //初期設定
        defaultstore = Firestore.firestore() ←これ
    }
}

新規投稿

messageTextに値が入っていれば、sendDataを作成しその情報を
Messageクラスで作成した投稿用の関数に投げます。
最初の画面で設定したユーザー名やアイコンは、UserDefaultsに保存してあるのでそれを引っ張ってくるために作成したDeviceStorageという名前の自作クラスから取得して入れております。

//MainViewController
//投稿ボタンを押したときのアクション
IBAction func sendMessage(_ sender: UIButton) {
        guard let message = self.messageText.text else {
            return
        }
        if self.messageText.text == "" {
            return
        }
        
        let sendData:[String:Any] = [
            "user_name":DeviceStorage.getMyName(),
            "icon_number": DeviceStorage.getMyIcon(),
            "message": message,
            "created_at": Date()
        ]
        //作成した情報をMessage関数に投げる
        Message().sendMessagee(defaultstore: self.defaultstore, messageData: sendData).then { result -> Void in
            print("送信完了")
        }
    }
import Firebase
import Alamofire
import PromiseKit

class Message {
    
    //メッセージを投稿する=FireStoreに保存する
    func sendMessagee(defaultstore:Firestore, messageData:[String: Any]) -> Promise<Bool> {
        return Promise { fulfill, reject in
             defaultstore.collection(Collection.type.message).addDocument(data: messageData)
            fulfill(true)
        }
    }
}

messageというコレクションで作成したいのでdefaultstore.collectionの引数に"message"を指定しますが、取得など他の要素でも使用したいので都度文字列を打つのはタイポ恐れがあるので"message"を構造体で作成してそこから引っ張っております。
(Collection.type.message)

さっそく試してみましょう。

シミュレーターを起動し、テキストボックスに文字を打ち込み送信を押すと...

投稿がシミュレーター画面に表示されると同時に、Firestoreの管理画面にも今送信したデータが保存されます。

これでいったんは投稿機能のようなものが作成できました。
シミュレーターの画面にも新しく投稿が表示されましたが、これは次の「FireStoreの情報が更新されたのを検知して、画面更新を行う」といった機能が動いたため表示されました。
その仕組みの実装が以下の形となります。

自動監視

画面を表示した際に、まずリスナーを登録します。リスナーを登録しておくと、指定したコレクションに何かしらの更新処理(追加や削除)が起きた際に指定したイベントが発生してくれる仕組みです。画面更新などを行わずに、サーバー側にデータ変更が起きただけで画面に投稿が追加されたり削除されたりします。

まずはリスナーを作成します。

//新規メッセージの取得(リアルタイム通信)
    func setListenMessage(defaultstore:Firestore) {
        defaultstore.collection(Collection.type.message).addSnapshotListener { (snapShot, error) in
            guard let data = snapShot else {
                print("\(Collection.type.message) data is nil")
                return
            }
            data.documentChanges.forEach{new in
                if (new.type == .added){
                    //追加タイプを受信したら先頭に追加する
                    self.messageBox.insert(new.document.data(), at: 0)
                    print("新規メッセージを取得しました")
                }
                if (new.type == .removed) {
                    //削除タイプを受信したら削除する。とりあえず全部空にする
                    self.messageBox = []
                    print("メッセージが削除されました")
                }
            }
            let range = NSMakeRange(0, self.messageTableView.numberOfSections)
            let sections = NSIndexSet(indexesIn: range)
            self.messageTableView.reloadSections(sections as IndexSet, with: .automatic)
            self.messageTableView.scrollsToTop = true
        }
    }    

addSnapshotListenerを使用することで、指定したコレクションの自動検知を行ってくれる処理になります。
ここではなにか変更があった際に、変更後の全データを取得しデータごとに持っているタイプを確認し「add(追加)」であれば投稿に使用している配列に加えてあげてtableviewを更新し、
「removed(削除)」があった場合には削除するようにしています。IDなどをもたせて削除があったものをきれいに消したいのですが、時間の都合上とりあえず全部空にする様にしてあります。。。

最後に、このリスナーを画面表示の際のviewDidloadの際に登録を行っておけば完了です。

    override func viewDidLoad() {
        super.viewDidLoad()

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        //リスナーを登録する
        self.setListenMessage(defaultstore: self.defaultstore)
    }


これにより、管理画面上からドキュメントを追加してあげても...

アプリ側が追加を検知し、その情報を読み取ってテーブルに投稿が即座に追加されます。(動画で見せるのが一番ですが投稿できない。。。)


作ってみて思ったこと

これを応用していけば、削除や編集、ソートなど様々な機能を追加して本物のチャットツールのようなものを作成できてしまうんじゃないでしょうか
まだまだ入門レベルの領域でしか触っていませんが、もっと深く知ることで複雑なアプリも今までより容易に作れてしまうんじゃないでしょうか。


何より自分でサーバーを構築しなくとも、FireStoreさえ用意すればこういったデータの保存、管理できるアプリケーションを作れてしまうのはかなり便利です。
FireBaseの別機能でもあるFiraAuth(ユーザー認証機能)も合わせて使用することで、会員制度を設けたアプリを開発することが可能です。


次回はそれらを含めFireBaseが提供している他の様々な機能に関しての記事をちょいちょい書けたらいいなと思います。それでは。

この記事が気に入ったらサポートをしてみませんか?