チャットアプリとFirebaseデータベースをうまく連携できずに困っています。
TextFieldにメッセージを入力し、EnterまたはButtonを押すとFirebaseにメッセージが保存され、Firebaseに保存されている今までのメッセージ履歴を表示するようなチャットアプリを作っています。
↓
Firebase間でのデータ送受信は実装できるのですが、
・TableViewに表示されるメッセージの順番がバラバラ
という問題に直面しています。
当初は、childByAutoId()で乱数で命名された階層に保存されるようにしました。
しかし、Firebaseからデータを取り出す場合、Firebaseに保存された順番でデータを取り出すことができないのです。
そこで、指定した数字に命名された階層を作り、データを取り出す際、ソートを利用して正しい順番でデータを取り出そうと試みました。
そのために、
・次にデータを保存する場合、階層名は"データ数+1"と命名する
・階層名が小さい順に取り出す
を実装しようと考えました。
ですが、データを保存するとき&データを取り出すとき、Firebaseのデータ数を変数で取得し、次にデータを保存するときにその変数名をchild(変数)として設定したのですが、
・新たな階層が作られない
・すでに作られている階層に代入される
という事態に直面し、悩んでいます。
↓
上記の場合、
・1回目:aaあああ、と入力して保存
・2回目:あああああああああ、と入力して保存したら、1回目のmessageに上書き
・3回目:げwgじぇあwl;gじゃwlgじゃwl;gk、と入力して保存したら、2回目のmessageに上書き
という流れになっています。
どこが原因なのか、ご指摘願います!!
swift
1import UIKit 2import Firebase 3 4 5 6class ChatViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate { 7 8 @IBOutlet weak var tableView: UITableView! 9 @IBOutlet weak var textField: UITextField! 10 11 //Firebase関連 12 //Firebaseに保存する変数を持つクラスインスタンス 13 var Post = Object() 14 //Objectクラスのインスタンスを持つ配列 15 var Posts = [Object]() 16 17 //階層名 18 var childNumber = 0 19 20 //UDからユーザーネームを取得 21 let username = UserDefaults.standard.object(forKey: "userName") as! String 22 23 override func viewDidLoad() { 24 super.viewDidLoad() 25 26 //デリゲート 27 textField.delegate = self 28 //カスタムセルを登録 29 tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "customcell") 30 //データを取得 31 loadData_Firebase() 32 //メッセの数を取得 33 childNumber = Posts.count 34 //リロード 35 tableView.reloadData() 36 37 } 38 39 //セルの数 40 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 41 return Posts.count 42 } 43 44 //セルを読み込む 45 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 46 guard let cell = tableView.dequeueReusableCell(withIdentifier: "customcell", for: indexPath) as? TableViewCell else { return UITableViewCell() } 47 //TableViewCellのtalk関数にユーザー名とメッセを渡す 48 cell.talk(Posts[indexPath.row].username, Posts[indexPath.row].message) 49 return cell 50 } 51 52 //メッセを投稿 53 @IBAction func send(_ sender: Any) { 54 55 //メッセが入力されてれば 56 if let _ = textField.text { 57 //Firebaseに[ユーザー名:本文]を保存 58 saveData_Firebase(username, textField.text!) 59 loadData_Firebase() 60 } else {return} 61 62 } 63 64 //Enterを押したとき 65 func textFieldShouldReturn(_ textField: UITextField) -> Bool { 66 67 68 //メッセが入力されてれば 69 if let _ = textField.text { 70 //Firebaseに[ユーザー名:本文]を保存 71 saveData_Firebase(username, textField.text!) 72 loadData_Firebase() 73 self.childNumber = self.Posts.count 74 } else {return true} 75 76 77 return true 78 79 } 80 81 //Firebaseにデータを保存する関数 82 func saveData_Firebase(_ username:String, _ message:String) { 83 //TextFieldの値をnil 84 textField.text = "" 85 //データベースの階層URL 86 let ref = Database.database().reference(fromURL: "https://realtimechat-e6e96.firebaseio.com/").child("post").child("(childNumber)") 87 //データを保存するときの辞書 88 let data = ["username":username, "message": message] 89 //データベースにデータを保存 90 ref.setValue(data) 91 } 92 93 94 //Firebaseからデータを取得する関数 95 func loadData_Firebase() { 96 //データベースの参照URL 97 let ref = Database.database().reference(fromURL: "https://realtimechat-e6e96.firebaseio.com/") 98 //データを初期化 99 self.Post = Object() 100 self.Posts = [Object]() 101 ref.child("post").observeSingleEvent(of: .value) { (snap,error) in 102 let snapdata = snap.value as? [String:NSDictionary] 103 if snapdata == nil { 104 return 105 } 106 for (_, snap) in snapdata! { 107 self.Post = Object() 108 if let username = snap["username"] as? String, let message = snap["message"] as? String { 109 self.Post.username = username 110 self.Post.message = message 111 } 112 self.Posts.append(self.Post) 113 } 114 115 self.tableView.reloadData() 116 } 117 118 } 119 120 121 122 123} 124
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。