8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

環境
Swift5
xcode11
データベース Firestore

今回はFirebaseでチャットアプリを作ります。
ezgif.com-gif-to-mp4.gif

まずFIrestoreの使い方についてざっくり説明します。
Firestoreの構造はこのようになっています。
structure-data.png

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 = ""
                    }
                    
                }
            }
        }
    }

Firestoreをみてみるとこんな感じになります。
スクリーンショット 2020-08-21 16.33.52.png

ちなみに今はランダムの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配列にある数の分だけ表示。

またカスタムセル はこのように作っています。
スクリーンショット 2020-08-21 15.38.25.png
送り相手が自分だったら、相手の画像(左の画像)を隠します。逆に自分じゃなかったら、自分の画像(右)を隠します。

 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
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にしています。
スクリーンショット 2020-08-21 16.08.35.png

テキストメッセージを角丸にするために、カスタムセル の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のチャットアプリの作り方でした!!

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?