iOSではじめてのFirestore [Swift]

Firestoreを使えばバックエンドが楽に実装できるという事で、Firestoreを触ってみました。
導入がとても楽で、使いこなせれば爆速でアプリ開発出来そうだなという印象です。

#1. Cloud Firestore プロジェクトの作成
こちらの公式ドキュメント内「Cloud Firestore プロジェクトを作成する」を参考にFirestoreのセットアップを行なっていきます。  
https://firebase.google.com/docs/firestore/quickstart?hl=ja  
ドキュメント通りに進めていけば、特に問題なくプロジェクトを作成できると思います。
<img width="555" alt="スクリーンショット 2019-05-04 2.52.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/99683836-1676-7daf-32af-4e11ef74b617.png">  

今回は簡易的にFirestoreを使いたい為、Authentication のログインプロバイダにて、「匿名」を有効にします。
<img width="1438" alt="スクリーンショット 2019-05-04 2.59.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/ecc0e22d-b4b1-9be1-46e7-1cb14d852d03.png">

こちらも簡易的に使いたい為、テストモードで開始します。
<img width="1437" alt="スクリーンショット 2019-05-04 3.03.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/a915ee3c-4862-947c-f455-8e3335e7e2a5.png">  
#2. iOSアプリへFirebaseの導入  
iOSアプリ側の導入の方も公式ドキュメント通りに進めていけば、特に問題なく導入できると思います。

Firestoreをアプリで使用するにはFirebaseの導入が必要な為、導入していきます。  
https://firebase.google.com/docs/ios/setup?hl=ja
https://firebase.google.com/docs/auth/ios/custom-auth?hl=ja

ドキュメント通りに構成ファイル```GoogleService-Info.plist```を作成し、プロジェクトへ追加。
Podfile に下記を追加し、```pod install```

```

pod 'Firebase/Core'
pod 'Firebase/Auth'
```


認証を行う為に、AppDelegate内にて```import Firebase```し、次を追加

```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    FirebaseApp.configure()
    Auth.auth().signInAnonymously()
    
    return true
}
```
これでFirebaseの導入ができました。


#3. iOSアプリへFirestoreの導入 
Firebaseの導入ができていれば、Podfile に下記を追加し```pod install```を行うだけでFirestoreの利用が可能になります。

```

pod 'Firebase/Firestore'
```

#4. Firestoreを使ってみる

今回はとりあえずデータの書き取り、読み取りを行なってみたい為メッセージの送受信をできる様にしてみます。

UIはメッセージを送受信表示する為に必要最低限の物を配置してあります。
![スクリーンショット 2019-05-04 4.23.59（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/a3974935-5ad4-07a5-6d5a-3deded73e0db.png)

StoryBoard に対応したソースはこちらになります。

```swift

import UIKit
import Firebase

class ChatViewController: UIViewController {
    
    struct Message {
        var senderId: String
        var name: String
        var message: String
    }

    @IBOutlet weak var chatTableView: UITableView!
    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var messageTextField: UITextField!
    
    private var messages : [Message] = []
    private var messageListener: ListenerRegistration?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        chatTableView.register(UINib(nibName: "ChatTableViewCell", bundle: nil), forCellReuseIdentifier: "ChatTableViewCell")
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.messageListener = Firestore.firestore().collection( "chat" ).order( by: "date" ).addSnapshotListener { snapshot, e in
            if let snapshot = snapshot {
                
                self.messages = snapshot.documents.map{ message -> Message in
                    let data = message.data()
                    return Message(senderId: data["sender_id"] as! String, name: data["name"] as! String, message: data["text"] as! String)
                }
                self.chatTableView.reloadData()
            }
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear( animated )
        if let mUnsubscribe = messageListener { mUnsubscribe.remove() }
    }
    
    @IBAction func trappedSendButton(_ sender: Any) {
        sendChatMessage(name: nameTextField.text ?? "", message: messageTextField.text ?? "")
    }
    
    private func sendChatMessage(name: String, message: String) {
        guard let id = UIDevice.current.identifierForVendor?.uuidString else { return }
        
        let dataStore = Firestore.firestore()
        dataStore.collection("chat").addDocument(data: [
            "text": message,
            "name": name,
            "sender_id": id,
            "date": Date()
        ]) { err in
            DispatchQueue.main.async {
                if let err = err {
                    print("Error writing document: \(err)")
                } else {
                    self.messageTextField.text = ""
                }
            }
        }
    }
    
}

extension ChatViewController : UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ table: UITableView, numberOfRowsInSection section: Int) -> Int {
        return  messages.count
    }
    
    func tableView(_ table: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = table.dequeueReusableCell(withIdentifier: "ChatTableViewCell",for: indexPath) as! ChatTableViewCell
        let message = messages[indexPath.row]
        cell.set(name: message.name)
        cell.set(message: message.message)
        return cell
    }
    
}

```
こちらの部分でデータベースの読み取り、変更を取得しています。

```swift
Firestore.firestore().collection( "chat" ).order( by: "date" ).addSnapshotListener{ snapshot, e in }
```

こちらの部分でメッセージの送信を行なっております。

```swift
private func sendChatMessage(name: String, message: String) {
        guard let id = UIDevice.current.identifierForVendor?.uuidString else { return }
        
        let dataStore = Firestore.firestore()
        dataStore.collection("chat").addDocument(data: [
            "text": message,
            "name": name,
            "sender_id": id,
            "date": Date()
        ]) { err in
            DispatchQueue.main.async {
                if let err = err {
                    print("Error writing document: \(err)")
                } else {
                    self.messageTextField.text = ""
                }
            }
        }
    }
```

# 5. 動作確認
先ほどのコードで実行したものがこちらになります。
![simu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/20a8b64d-4e23-5578-5753-a333fe322872.gif)

メッセージが送信されるとデータベースの変更を取得し、TableViewが更新されている事がわかります。

<img width="1438" alt="スクリーンショット 2019-05-04 4.38.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301673/39ffb040-5534-ab96-8660-d652a8360625.png">
Firebase コンソールからもデータが送信されていることが確認できます。

# まとめ
Firestoreを用いれば、とても簡単にデータの書き取り、読み取りができる。

用いたソースはGithubに載せておきます。
https://github.com/taka011002/firestore_test_ios

# 参考
・[Firebase / Firestore を使って簡単な Chat を作ってみる。(iOS)](https://qiita.com/Satachito/items/7112ef63a42d301abc21)