環境
Swift5
xcode11
データベース Firestore
まずFIrestoreの使い方についてざっくり説明します。
Firestoreの構造はこのようになっています。
collectionがクラスであり、その中にdocument(構成要素)がありそのdocumentの中にもさらにdata(構成要素)があります。
collectionが一番大きい構成単位でその中に複数のdocumentがあり、そのdocumentの中にも複数のdataが入っています。
理解するより慣れるのが大切なので、データを入れたり呼び出したりして使いこなしていきましょう!!
それでは本題のチャットアプリについて話していきます。
###送信したテキストをFirestoreに保存
テキストを送信するとcollectionのなかに"messages"というクラスを作ります。その中に新しくdocumentを作り、そのdocumentの中に”message”(テキストメッセージ),”sender”(送り主),”date”(日付)を入れていきます。
@IBAction func send() {
if let message = messageTextField.text,let sender = Auth.auth().currentUser?.email {
db.collection("messages").addDocument(data: ["message" : message,"sender":sender,"date":Date().timeIntervalSince1970]) { (error) in
if error != nil {
print("送信失敗")
} else {
print("送信成功")
DispatchQueue.main.async {
self.messageTextField.text = ""
}
}
}
}
}
ちなみに今はランダムのIDでdocumentに入れていますが、もし特定のIDを入れたいなら
db.collection("message").document(userId).setData(data)
と書きます。
##データの呼び出し
orderで日付の降順でデータを呼び出します。またデータを呼び出すときは、メッセージが更新されるたびにリアルタイムで呼び出したいので、addSnapshotListenerを使っています。1回しか呼び出さなくていいときはgetDocument ()を使います。
詳しくは 👇
https://firebase.google.com/docs/firestore/query-data/listen?hl=ja
そして、messages = [] でmessagesにいれる配列を一度0にすることで、データが重複して読み込まれるのを防いでいます。
for文で呼び出したデータを1つ1つmeesages配列に入れていきます。そしてこのmessagesの配列を後にtableViewで使います。
DispatchQueue.main.asyncを使いことによって前タスク処理が終わるのを待って次タスクを処理側へ渡せるので、前の処理を着実にしてくれます。
詳しくは👇
https://qiita.com/ShoichiKuraoka/items/bb2a280688d29de3ff18
👇そして常に新しいメッセージがあるところまでスクロールして欲しいのでtableViewのcell(テキストメッセージ)の1番下までスクロールしています。
self.chatTableView.scrollToRow(at: indexPath, at: .top, animated: false)
func loadMessages(){
db.collection("messages").order(by: "date").addSnapshotListener { (querySnapshot, error) in
//データの重複を防ぐ
self.messages = []
if error != nil {
print("ロード失敗")
} else {
print("ロード成功")
//documentsの中にデータがあれば
if let snapshotDocuments = querySnapshot?.documents {
for document in snapshotDocuments {
let date = document.data()
//とってきたdateはAny型なのでstring型に直す
if let sender = date["sender"] as? String,let messageBody = date["message"] as? String {
let newMessage = Message(sender: sender, messge: messageBody)
self.messages.append(newMessage)
print(newMessage)
DispatchQueue.main.async {
self.chatTableView.reloadData()
//tanleViewを一番下まで持ってくる
//rowは0,1,2の順番なので-1する
let indexPath = IndexPath(row: self.messages.count - 1, section: 0)
self.chatTableView.scrollToRow(at: indexPath, at: .top, animated: false)
}
}
}
}
}
}
}
##TableViewに表示させる
TableViewのcellの数はmessages配列にある数の分だけ表示。
またカスタムセル はこのように作っています。
送り相手が自分だったら、相手の画像(左の画像)を隠します。逆に自分じゃなかったら、自分の画像(右)を隠します。
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//messagesの数の分だけ
return messages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = messages[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for:indexPath) as! MessageTableViewCell
cell.label.text = messages[indexPath.row].messge
//もし送り相手が自分だったら
if message.sender == Auth.auth().currentUser?.email {
cell.meImageView.isHidden = false
cell.youImageView.isHidden = true
cell.messageBubble.backgroundColor = UIColor(named: "blue")
cell.label.textColor = UIColor(named: "white")
} else {
//送り相手が自分じゃないとき
cell.youImageView.isHidden = false
cell.meImageView.isHidden = true
cell.messageBubble.backgroundColor = UIColor(named: "white")
cell.label.textColor = UIColor(named: "blue")
}
return cell
}
これでデータの保存、呼び出し,TableViewCellに表示ができました。
##その他見た目に関する部分
キーボードをがテキストのところに被らず上にあげるためにライブラリ "IQKeyboardManage"を入れてテキストをキーボードの上にあげるようにしています。
AppDelegateにこのように書いています。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let db = Firestore.firestore()
print(db)
//キーボードを上げる
IQKeyboardManager.shared.enable = true
//キーボードの上の余分なタブを消す
IQKeyboardManager.shared.enableAutoToolbar = false
//キーボード外をタップした時に下げる
IQKeyboardManager.shared.shouldResignOnTouchOutside = true
return true
}
tableViewの線を消すためにtableViewのseparatorをNoneにしています。
テキストメッセージを角丸にするために、カスタムセル のViewを角丸に設定しています。
class MessageTableViewCell: UITableViewCell {
@IBOutlet weak var messageBubble: UIView!
@IBOutlet var label : UILabel!
@IBOutlet var meImageView : UIImageView!
@IBOutlet var youImageView : UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
messageBubble.layer.cornerRadius = messageBubble.frame.size.height / 2
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
以上Firebaseのチャットアプリの作り方でした!!