【Swift】Firebaseを使ってチャットアプリを作る。4 〜メッセージを順番通りに表示する〜
この記事は、以下の記事の続きとなっております。
【Swift】Firebaseを使ってチャットアプリを作る。1 〜画面を作る〜 - 茶漬けの技術メモ
【Swift】Firebaseを使ってチャットアプリを作る。2 〜Firebase をアプリに追加する〜 - 茶漬けの技術メモ
【Swift】Firebaseを使ってチャットアプリを作る。3 〜メッセージを送受信できるようにする〜 - 茶漬けの技術メモ
前回までで、メッセージを送受信できるようになりました。
しかし、メッセージの順番がなぜかめちゃくちゃになっていました。
今回は、そこの修正をしていきます!!
辞書のmap は順番が保証されない
前回 Firebase から取得したデータを取り出すときに以下のコードを書きましたが。
調べたところ、どうやらここが問題のようです。
self.messages = posts.values.map { dic in let senderId = dic["senderId"] ?? "" let text = dic["text"] ?? "" let displayName = dic["displayName"] ?? "" return JSQMessage(senderId: senderId, displayName: displayName, text: text) }
辞書型でmap を使った場合、実行される処理は順番が保証されていないようです。
本当かどうかplayground で調べてみます。
確かに順番がめちゃくちゃになっていますね。
ちなみに、配列では順番が保証されています。
配列を利用するとメッセージを順番に表示することができそうです。
Firebase にタイムスタンプを送る
順番通りにソートするために、タイムスタンプを送信します。
「Send」ボタンを押した時の処理を以下に変更。
参考
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) { inputToolbar.contentView.textView.text = "" let ref = FIRDatabase.database().reference() ref.child("messages").childByAutoId().setValue(["senderId": senderId, "text": text, "displayName": senderDisplayName, "date": [".sv": "timestamp"]]) }
タイムスタンプを使ってメッセージデータを順番に表示
ViewController を以下に変更します。
var messages = [JSQMessage]() override func viewDidLoad() { super.viewDidLoad() senderDisplayName = "tsuru" senderId = "Tsuru" let ref = FIRDatabase.database().reference() ref.observe(.value, with: { snapshot in guard let dic = snapshot.value as? Dictionary<String, AnyObject> else { return } guard let posts = dic["messages"] as? Dictionary<String, Dictionary<String, AnyObject>> else { return } // keyとdateが入ったタプルを作る var keyValueArray: [(String, Int)] = [] for (key, value) in posts { keyValueArray.append((key: key, date: value["date"] as! Int)) } keyValueArray.sort{$0.1 < $1.1} // タプルの中のdate でソートしてタプルの順番を揃える(配列で) これでkeyが順番通りになる // messagesを再構成 var preMessages = [JSQMessage]() for sortedTuple in keyValueArray { for (key, value) in posts { if key == sortedTuple.0 { // 揃えた順番通りにメッセージを作成 let senderId = value["senderId"] as! String! let text = value["text"] as! String! let displayName = value["displayName"] as! String! preMessages.append(JSQMessage(senderId: senderId, displayName: displayName, text: text)) } } } self.messages = preMessages self.collectionView.reloadData() }) }
各データのアドレスとタイムスタンプを持ったタプルを作成し、タイムスタンプを使ってアドレスをソートしていきます。
これで順番通りにメッセージが表示されるようになりました。
めでたしめでたし。。。。
なのですが、
いやいやいや、そもそもposts.valuesは配列を返すだろ! と思った方がいるのではないでしょうか?
実は、posts.valuesが返すのは、配列ではなく、、、
LazyMapCollection というものなのです。が、
そこについてはまた別記事で書いていこうと思いますー!
チャットアプリについてはここまでです! ありがとうございましたー!!