10
9

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.

【iOS】Firebaseど素人のFirebase入門 Firestore編

Last updated at Posted at 2021-01-23

【iOS】Firebaseど素人のFirebase入門 認証編に続き、今回はFirestoreを学びながらサンプルアプリ作りに挑戦してみました。

##作ったサンプルアプリのデモ
イメージ.GIF

Firestore上のデータがTableView上に表示されるアプリです。

Set Buttonを押すと、Firestoreに情報が書き込み or 更新(setdata)されます。
Add Buttonを押すと、Firestoreに情報が追加(addDocument)されます。
Firestoreからドキュメントを取得を押すを押すと、Firestoreに保存されている情報を取得(getDocuments)して、TableViewに反映します。

リアルタイムアップデートスイッチをOnにすると、Firestore上の情報が更新される度に自動的にTextViewを更新します。

##まずはFirebaseに登録
1.Firebase公式ドキュメントに沿って、Firebase登録とFirebase SDKをアプリに追加する。
Firebase を iOS プロジェクトに追加する

2.Firebaseに追加したアプリのコンソールからCloud Firestoreを使用できるように設定する

##AppDelegateでFirestoreの初期化
AppDelegate.swiftにFirebaseをインポート

AppDelegate.swift
import Firebase

didFinishLaunchingWithOptions内でFirestoreの初期化

AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        FirebaseApp.configure()
        let db = Firestore.firestore()
        print(db)
        
        return true
    }

##ViewControllerにもFirebaseをインポート
作成を進めるファイルにもFirebaseをインポートする。

FirestoreViewController.swift
import Firebase

##データの書き込み or 更新
データの書き込み or 更新にはsetData(documentData: [String : Any], completion:)を使用します。

db.collection("users").document("HiroshiTachi").setData([
                "name": name,
                "age": age,
            ]) { error in
                if let error = error {
                    print("ドキュメントの書き込みに失敗しました:", error)
                } else {
                    print("ドキュメントの書き込みに成功しました")
                }
            }

setDataでの書き込みの場合は、ドキュメント内に何もデータがない場合は新規作成を行い、すでにデータがある場合はそのデータの中身を更新します。

##データの追加
データの追加にはaddDocument(data: , completion: )を使用します。

var ref: DocumentReference? = nil
ref = db.collection("users").addDocument(data:[
                "name": name,
                "age": age,
            ]) { error in
                if let error = error {
                    print("ドキュメントの追加に失敗しました:", error)
                } else {
                    print("ドキュメントの追加に成功しました:", ref?.documentID as Any)
                }
            }

こちらはsetDataとは違い、指定したcollection内に問答無用でどんどん新規追加してくれます。
なので、データを更新したりするのには使用出来ません。

##データを1回取得
データを1回取得するには、getDocuments()を使用します。

db.collection("users").getDocuments() { (querySnapshot, error) in
                if let error = error {
                    print("ドキュメントの取得に失敗しました:", error)
                } else {
                    print("ドキュメントの取得に成功しました")
                    for document in querySnapshot!.documents {
                        let data = document.data()
                        //取得したデータに対しての処理を書く
                        print(data["name"])
                        }
                    }
                }
            }

##データをリアルタイムで取得する
データが更新される度にリアルタイム取得をするには、addSnapshotListener(listener:)を使用します。

###1.グローバル変数としてリスナーListenerRegistrationを定義

var listener: ListenerRegistration?

###2.リスナーをアタッチ

listener = db.collection("users").addSnapshotListener { documentSnapshot, error in
                if let error = error {
                    print("ドキュメントの取得に失敗しました", error)
                } else {
                    self.queriedDataArray = []
                    if let documentSnapshots = documentSnapshot?.documents {
                        for document in documentSnapshots {
                            let data = document.data()
                            //アップデートされた際に行いたい処理を書く
                            print(data["name"])
                                }
                            }
                        }
                    }
                }
            }

###3.リスナーを切り離し
リスナーは必要がなくなったら切り離し(デタッチ)しましょう。

listener?.remove()

公式ドキュメントより

データをリッスンする必要がなくなったら、イベント コールバックが呼び出されないようにリスナーをデタッチしなければなりません。これにより、クライアントは更新を受信するための帯域幅の使用を停止できます。

##まとめ
公式ドキュメントの情報は本当に充実しているので、この記事より遥かに充実して間違いのない情報が得られますので、参考にする際は是非とも公式ドキュメントを参考にしていただければと思います。笑

また、セキュリティルールについてはまだまだなので知識を深めていきたい。

何か間違いがありましたら、優しく訂正していただけると幸いです🙇‍♂️

##参考
Cloud Firestore を使ってみる
Cloud Firestore にデータを追加する
Cloud Firestore でデータを取得する
Cloud Firestore でリアルタイム アップデートを入手する

##サンプルアプリのコード全体

FirestoreViewController.swift
import UIKit
import Firebase

class FirestoreViewController: UIViewController {
    
    @IBOutlet weak var queryDataTableView: UITableView!
    
    @IBOutlet weak var setTextLabel: UILabel!
    @IBOutlet weak var setAgeTextField: UITextField!
    @IBOutlet weak var setButton: UIButton!
    
    @IBOutlet weak var addTextField: UITextField!
    @IBOutlet weak var addAgeTextField: UITextField!
    @IBOutlet weak var addButton: UIButton!
    
    @IBOutlet weak var getButton: UIButton!
    @IBOutlet weak var realTimeQuerySwitch: UISwitch!
    
    let db = Firestore.firestore()
    var queriedDataArray = [String()]
    
    var listener: ListenerRegistration?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        queryDataTableView.dataSource = self
        addButton.layer.cornerRadius = 15
        setButton.layer.cornerRadius = 15
        getButton.layer.cornerRadius = 15
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        listener?.remove()
    }
    
    @IBAction func didTapButton(_ sender: UIButton) {
        
        switch sender.currentTitle {
        
        //SetButton Action
        case "Set":
            
            guard let name = setTextLabel.text, let age = setAgeTextField.text else { return }
            db.collection("users").document("HiroshiTachi").setData([
                "name": name,
                "age": age,
            ]) { error in
                if let error = error {
                    print("ドキュメントの書き込みに失敗しました:", error)
                } else {
                    print("ドキュメントの書き込みに成功しました")
                }
            }
            
        //AddButton Action
        case "Add":
            
            guard let name = addTextField.text, let age = addAgeTextField.text else { return }
            
            var ref: DocumentReference? = nil
            ref = db.collection("users").addDocument(data:[
                "name": name,
                "age": age,
            ]) { error in
                if let error = error {
                    print("ドキュメントの追加に失敗しました:", error)
                } else {
                    print("ドキュメントの追加に成功しました:", ref?.documentID as Any)
                }
            }
            
        //GetButton Action
        default:
            queriedDataArray = []
            db.collection("users").getDocuments() { (querySnapshot, error) in
                if let error = error {
                    print("ドキュメントの取得に失敗しました:", error)
                } else {
                    for document in querySnapshot!.documents {
                        let data = document.data()
                        guard let name = data["name"] as? String, let age = data["age"] as? String else {
                            return
                        }
                        let nameAndAge = name + " " + age + "歳"
                        self.queriedDataArray.append(nameAndAge)
                        DispatchQueue.main.async {
                            self.queryDataTableView.reloadData()
                        }
                    }
                }
            }
        }
    }
    
    @IBAction func didChangeRealTimeQueryState(_ sender: UISwitch) {
        
        if sender.isOn {
            print("リアルタイムアップデートOn")
            listener = db.collection("users").addSnapshotListener { documentSnapshot, error in
                if let error = error {
                    print("ドキュメントの取得に失敗しました", error)
                } else {
                    self.queriedDataArray = []
                    if let documentSnapshots = documentSnapshot?.documents {
                        for document in documentSnapshots {
                            let data = document.data()
                            if let name = data["name"] as? String, let age = data["age"] as? String {
                                let nameAndAge = name + " " + age + "歳"
                                self.queriedDataArray.append(nameAndAge)
                                DispatchQueue.main.async {
                                    self.queryDataTableView.reloadData()
                                }
                            }
                        }
                    }
                }
            }
        } else {
            print("リアルタイムアップデートOff")
            listener?.remove()
        }
    }
}

extension FirestoreViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return queriedDataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = queryDataTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = queriedDataArray[indexPath.row]
        return cell
    }
}
10
9
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
10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?