Swift5 Firebaseでチャットアプリを作ろう　

環境
Swift5 
xcode11
データベース　Firestore

今回はFirebaseでチャットアプリを作ります。
![ezgif.com-gif-to-mp4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/39b77810-985f-e19e-040c-a2bf3cc9825a.gif)


まずFIrestoreの使い方についてざっくり説明します。
Firestoreの構造はこのようになっています。
![structure-data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/43963ead-145a-5584-1e30-eb286d372386.png)

 collectionがクラスであり、その中にdocument（構成要素）がありそのdocumentの中にもさらにdata（構成要素）があります。

collectionが一番大きい構成単位でその中に複数のdocumentがあり、そのdocumentの中にも複数のdataが入っています。

理解するより慣れるのが大切なので、データを入れたり呼び出したりして使いこなしていきましょう！！


それでは本題のチャットアプリについて話していきます。

###送信したテキストをFirestoreに保存
テキストを送信するとcollectionのなかに"messages"というクラスを作ります。その中に新しくdocumentを作り、そのdocumentの中に”message”（テキストメッセージ）,”sender”（送り主）,”date”（日付）を入れていきます。



```swift
@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 = ""
                    }
                    
                }
            }
        }
    }
```

Firestoreをみてみるとこんな感じになります。
![スクリーンショット 2020-08-21 16.33.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/0dceed2c-bce7-56b9-2e3d-68eac474a306.png)


ちなみに今はランダムのIDでdocumentに入れていますが、もし特定のIDを入れたいなら
```
db.collection("message").document(userId).setData(data)
```
と書きます。

##データの呼び出し
orderで日付の降順でデータを呼び出します。またデータを呼び出すときは、メッセージが更新されるたびにリアルタイムで呼び出したいので、addSnapshotListenerを使っています。１回しか呼び出さなくていいときはgetDocument ()を使います。
詳しくは　👇
https://firebase.google.com/docs/firestore/query-data/listen?hl=ja

そして、messages = []　でmessagesにいれる配列を一度０にすることで、データが重複して読み込まれるのを防いでいます。
for文で呼び出したデータを1つ1つmeesages配列に入れていきます。そしてこのmessagesの配列を後にtableViewで使います。

DispatchQueue.main.asyncを使いことによって前タスク処理が終わるのを待って次タスクを処理側へ渡せるので、前の処理を着実にしてくれます。
詳しくは👇
https://qiita.com/ShoichiKuraoka/items/bb2a280688d29de3ff18

👇そして常に新しいメッセージがあるところまでスクロールして欲しいのでtableViewのcell（テキストメッセージ）の１番下までスクロールしています。
self.chatTableView.scrollToRow(at: indexPath, at: .top, animated: false)


```swift

    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配列にある数の分だけ表示。

またカスタムセル はこのように作っています。
![スクリーンショット 2020-08-21 15.38.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/d6e2f231-7fd0-6c25-3aa2-b809b2c0a7ae.png)
送り相手が自分だったら、相手の画像（左の画像）を隠します。逆に自分じゃなかったら、自分の画像（右）を隠します。

```swift
 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"を入れてテキストをキーボードの上にあげるようにしています。
![スクリーンショット 2020-08-21 16.00.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/fae63949-0cb9-8fc2-ba61-4c53bdff4106.png)
AppDelegateにこのように書いています。


```swift
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にしています。
![スクリーンショット 2020-08-21 16.08.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694910/b014f068-26da-029f-3d6b-0ed1987ff7fe.png)

テキストメッセージを角丸にするために、カスタムセル のViewを角丸に設定しています。

```swift
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のチャットアプリの作り方でした！！