【Fire base+swift】学習記録（４）ログイン機能のあるTODOアプリを作る

[【Swift】リアルタイムチャットを実現するFirebaseでCRUD(データ作成、読み込み、更新、削除)をやってみる
](https://qiita.com/ShinokiRyosei/items/f71c73ab8b0de145c5bc)
こちらを参考にログイン機能ありのTODOアプリを作成しました。
今回学んだのはユーザー情報と投稿情報の紐付け方です。

自分なりの理解をコメントに入れています。

#ユーザー情報と投稿情報の紐付け方

```swift:viewcontroller.swift
func create() {
        //textFieldになにも書かれてない場合は、その後の処理をしない
        guard let text = textField.text else { return }

        //ロートからログインしているユーザーのIDをchildにしてデータを作成
        //childByAutoId()でユーザーIDの下に、IDを自動生成してその中にデータを入れる
        //setValueでデータを送信する。第一引数に送信したいデータを辞書型で入れる
        //今回は記入内容と一緒にユーザーIDと時間を入れる
        //FIRServerValue.timestamp()で現在時間を取る
        self.ref.child((Auth.auth().currentUser?.uid)!).childByAutoId().setValue(["user": (Auth.auth().currentUser?.uid)!,"content": text, "date": ServerValue.timestamp()])

        //テスト表示
        print("user\((Auth.auth().currentUser?.uid)!)content\(text)date\(ServerValue.timestamp())")
        
    }
```
一般的にFirebaseでデータを保存するときは、`setValue`で行いますが、今回は`unique-post-id`などを生成するために`childByAutoId`を使いました。おそらく。


| メソッド | 一般的な使用例 |
|:-----------------|:------------------|
| setValue | users/user-id/username など、定義済みのパスへのデータの書き込みや、データの置換を行います。 |
| childByAutoId | データのリストに追加します。childByAutoId を呼び出すたびに、user-posts/user-id/unique-post-idのような一意の識別子としても使用できる一意のキーが生成されます。 |
| updateChildValues | データのすべてを置換することなく、定義済みのパスのキーの一部を更新します。 |
| runTransactionBlock | 同時更新によって破損する可能性がある複合データを更新します。 |
[iOS でのデータの保存](https://firebase.google.com/docs/database/ios/save-data?hl=ja)より

#データの表示の方法
Java および Node.js では、コールバック関数は DataSnapshot を受け取ります。これはデータのスナップショットです。スナップショットとは、ある時点における特定のデータベース参照にあるデータの全体像を写し取ったものです。スナップショットの val() / getValue() を呼び出すと、データの言語固有のオブジェクト表現が返されます。参照先の場所にデータが存在しない場合、スナップショットの値が null になります。

```swift:Listviewcontroller.swift
 //Fetchしたデータを入れておく配列、この配列をTableViewで表示
    var contentArray: [DataSnapshot] = []
    //FetchしたSnapshotsを格納する変数
    var snap: DataSnapshot!
    //Firebaseのルートを宣言しておく
    let ref = Database.database().reference()
```

#xibファイルを使わずに表示する方法
参考にした記事ではxibファイルなどを使ってセル内にcontentを表示してましたが、自分はまだxibファイルの使い方側からなっか他ため、以下のように少し変更して実装しました。

```swift:Listviewcontroller.swift

class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var table: UITableView! //送信したデータを表示するTableView

    var contentArray: [DataSnapshot] = [] //Fetchしたデータを入れておく配列、この配列をTableViewで表示
    var snap: DataSnapshot! //FetchしたSnapshotsを格納する変数
    let ref = Database.database().reference() //Firebaseのルートを宣言しておく

    override func viewDidLoad() {
        super.viewDidLoad()
        //データを読み込むためのメソッド
        self.read()
        table.delegate = self //デリゲートをセット
        table.dataSource = self //デリゲートをセット

        // Do any additional setup after loading the view.

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        //Cellの高さを調節
        table.estimatedRowHeight = 56
        table.rowHeight = UITableView.automaticDimension
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        //画面が消えたときに、Firebaseのデータ読み取りのObserverを削除しておく
        ref.removeAllObservers()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    //ViewControllerへの遷移
    func transition() {
        self.performSegue(withIdentifier: "toView", sender: self)
    }

    //セルの数
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return contentArray.count
    }

    //返すセルを決める
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell (
            withIdentifier: "Cell",
            for: indexPath as IndexPath)
        //contetnArrayにitemを代入
        let item = contentArray[indexPath.row]
        //cell内に代入するものをfirebaseから持ってくる
        let textlabel = item.value as! Dictionary<String, AnyObject>
        //cell内に代入するものを決める
        cell.textLabel?.text = String(describing: textlabel["content"]!)
        return cell
    }
    
    func read()  {
        //FIRDataEventTypeを.Valueにすることにより、なにかしらの変化があった時に、実行
        //今回は、childでユーザーIDを指定することで、ユーザーが投稿したデータの一つ上のchildまで指定することになる
        ref.child((Auth.auth().currentUser?.uid)!).observe(.value, with: {(snapShots) in
            if snapShots.children.allObjects is [DataSnapshot] {
                print("snapShots.children...\(snapShots.childrenCount)") //いくつのデータがあるかプリント

                print("snapShot...\(snapShots)") //読み込んだデータをプリント

                self.snap = snapShots

            }
            self.reload(snap: self.snap)
        })
    }

    //読み込んだデータは最初すべてのデータが一つにまとまっているので、それらを分割して、配列に入れる
    func reload(snap: DataSnapshot) {
        if snap.exists() {
            print(snap)
            //FIRDataSnapshotが存在するか確認
            contentArray.removeAll()
            //1つになっているFIRDataSnapshotを分割し、配列に入れる
            for item in snap.children {
                contentArray.append(item as! DataSnapshot)
            }
            // ローカルのデータベースを更新
            ref.child((Auth.auth().currentUser?.uid)!).keepSynced(true)
            //テーブルビューをリロード
            table.reloadData()
        }
    }
    
}

```


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【Firebase＋swift】<br>ユーザーと投稿コンテンツを紐付けてTableViewに表示できるようになたーーー！<br>これで作りたかったアプリが作れそう！ <a href="https://t.co/LFaP6A1hO8">pic.twitter.com/LFaP6A1hO8</a></p>&mdash; なかむら (@nakamurasssey) <a href="https://twitter.com/nakamurasssey/status/1198138463075201024?ref_src=twsrc%5Etfw">November 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>