FirebaseでiOS版簡易SNSを作成する。(タイムライン編 前編)

# はじめに
今回、Firebaseを使ってiOS版簡易SNSを作成する記事になっています。
この記事ではタイムライン上でFirestoreに対して書き込みと読み込みを行うまでの部分を行なっています。
また、[前回](https://qiita.com/fummicc1-sub/items/876b000fb514927a8df7)の続きという扱いで進めていきます。

# Firestoreを有効にする
- [Firebaseコンソール](https://console.firebase.google.com/u/0/)からプロジェクトを選択し、左のサイドメニューから**Database**を選択。
- データベースの作成を選択。

<img width="1678" alt="スクリーンショット 2019-08-30 9.01.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/28b0bbb7-d6cc-149c-5bf5-5b294b65e34c.png">

- テストモードで開始します。

<img width="1118" alt="スクリーンショット 2019-08-30 9.04.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/4527747a-088c-319b-bc39-cd7c3945bd28.png">

- ロケーションは`asia-northeast1`を選択します。

<img width="791" alt="スクリーンショット 2019-08-30 9.05.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/c0e9a011-6fcc-2ac1-b333-07e627186271.png">

- これで、Firestoreの設定は完了です。

# Main.storyboardを編集

- 以下のようにテーブルビューを`TimelineViewController`に配置します。

<img width="394" alt="スクリーンショット 2019-08-30 8.11.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/c733cf15-e2a8-9732-cdb8-b7ced02a3c6f.png">

- 投稿を行うためのボタンも配置します。

<img width="338" alt="スクリーンショット 2019-08-30 9.13.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/2e9886b2-2c1f-316b-7a09-c2bc14107857.png">

- `AddViewController`を作成し、Segueで接続する。

![demo1](https://media.giphy.com/media/h470N4VLQz4bXSQZTY/giphy.gif)

- 上で繋いだSegueのIdentifierを`Add`にする。

<img width="958" alt="スクリーンショット 2019-08-30 12.45.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/8e65268a-60e7-7301-51dc-ce56507e13e5.png">


- `AddViewController`に`UITextView`と`UIButton`を配置する。(今回はキーボードと重ならないように上の方に配置しておく。)

<img width="379" alt="スクリーンショット 2019-08-30 12.45.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/2bb755e3-c7b0-b859-ace0-57d718878953.png">


# TimelineViewController.swiftを編集

- データベースの宣言を行います。

```TimelineViewController.swift
import UIKit
import Firebase

class TimelineViewController: UIViewController {

    var me: AppUser!
    var database: Firestore! // 宣言
    
    override func viewDidLoad() {
        super.viewDidLoad()
        database = Firestore.firestore() // 初期値代入
    }

    // 投稿追加画面に遷移するボタンを押したときの動作を記述。
    @IBAction func toAddViewController() {
    }
}
```

- `AddViewController`へ画面遷移を行うコードの記述。

```TimelineViewController.swift
@IBAction func toAddViewController() {
    performSegue(withIdentifier: "Add", sender: me)
}
```

- 画面遷移時に値を渡すために`prepare`メソッドを記述。

```TimelineViewController.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let destination = segue.destination as! AddViewController // segue.destinationで遷移先のViewControllerが取得可能。
    destination.me = sender as! AppUser
}
```

# `AddViewController`を作成、編集。

```AddViewController.swift
import UIKit
import Firebase

class AddViewController: UIViewController {

    var me: AppUser!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
```

- `@IBOUtlet``@IBAction`関連を記述します。今回は、`UITextView`が`@IBOutlet`で、`UIButton`が`@IBAction`になります。

```swift
import UIKit
import Firebase

class AddViewController: UIViewController {
    
    @IBOutlet var contentTextView: UITextView! // 追加
    
    var me: AppUser!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // 追加
    @IBAction func postContent() {        
    }
}
```

- `UITextView`をカスタマイズ

`UITextView`は複数行入力することができるため、**returnキーで入力の終了**ができません。
なので、キーボードを閉じる処理として別にボタンを用意します。


```キーボードを閉じる一連の処理のコード.swift
func setupTextView() {
    let toolBar = UIToolbar() // キーボードの上に置くツールバーの生成
    let flexibleSpaceBarButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) // 今回は、右端にDoneボタンを置きたいので、左に空白を入れる
    let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissKeyboard)) // Doneボタン
    toolBar.items = [flexibleSpaceBarButton, doneButton] // ツールバーにボタンを配置
    toolBar.sizeToFit()
    contentTextView.inputAccessoryView = toolBar // テキストビューにツールバーをセット
}

// キーボードを閉じる処理。
@objc func dismissKeyboard() {
    contentTextView.resignFirstResponder()
}
```

# 投稿ボタン (データベースへの書き込み)

投稿ボタンを押したときの処理を記述していきます。

今回は、**posts**コレクションの中に、投稿を保存していきます。(Firestoreの説明に関しては[こちら](https://qiita.com/fummicc1-sub/items/3fa0348fb9c94f14d279#firebasefirestore)をご覧ください。)

```投稿ボタンを押したときの処理.swift
@IBAction func postContent() {
    let content = contentTextView.text!
    let saveDocument = Firestore.firestore().collection("posts").document()
    saveDocument.setData([
        "content": content,
        "postID": saveDocument.documentID,
        "senderID": user.uid,
        "createdAt": FieldValue.serverTimestamp(),
        "updatedAt": FieldValue.serverTimestamp()
    ]) { error in
        if error == nil {
            self.dismiss(animated: true, completion: nil)
        }
    }
}
```

# Main.storyboardで関連付け

- クラスを`AddViewController`に設定する。

<img width="642" alt="スクリーンショット 2019-08-30 13.28.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/239f969d-8ce2-e01d-507d-e7f0ec62526d.png">

- 関連付けを行い、実行する。

- 現在のデモ

![demo2](https://media.giphy.com/media/TdXohEbejfjU3jJXTM/giphy.gif)

![demo3](https://media.giphy.com/media/TLh2gTF0ZgYd2SsT3M/giphy.gif)

- データベース (2回投稿しました。)

<img width="1347" alt="スクリーンショット 2019-08-30 13.37.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/ff95a294-571d-f4b1-9fe7-f8100d4805d7.png">

# TimelineViewControllerで投稿を読み込む

- 投稿を保存する配列の変数の宣言

- viewWillAppearメソッドの中でデータベースから投稿を読み込む処理を記述していきます。

```TimelineViewController.swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    database.collection("posts").getDocuments { (snapshot, error) in
        if error == nil, let snapshot = snapshot {
            self.postArray = []
            for document in snapshot.documents {
                let data = document.data()
                let post = Post(data: data)
                self.postArray.append(post)
            }
            print(self.postArray)
        }
    }
}
```

- 出力結果

<img width="503" alt="スクリーンショット 2019-08-31 23.18.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/8e22678a-74a4-3a5f-1857-34e23db27fad.png">



# TimelineViewControllerで投稿をテーブルビューに表示する。

- UIの宣言

```swift
// デリゲート・データソースを記述。
class TimelineViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet var tableView: UITableView! // 追加

    override func viewDidLoad() {
        super.viewDidLoad()
        database = Firestore.firestore()
        tableView.delegate = self  // 追加
        tableView.dataSource = self // 追加
    }
}
```

- データソースメソッドを追加

```swift
class TimelineViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    (省略)

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return postArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        cell.textLabel?.text = postArray[indexPath.row].content
        return cell
    }
}

```

- データベースからの取得が完了したらテーブルビューを更新する

```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    database.collection("posts").getDocuments { (snapshot, error) in
        if error == nil, let snapshot = snapshot {
            self.postArray = []
            for document in snapshot.documents {
                let data = document.data()
                let post = Post(data: data)
                self.postArray.append(post)
            }
            self.tableView.reloadData() // 先ほど書いたprint文をこちらに変更
        }
    }
}
```


- `Prototype Cells`を1に変更
<img width="901" alt="スクリーンショット 2019-08-31 23.41.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/9264f33c-7b24-deef-d0bc-91f6d6cd3b40.png">



- StoryboardでセルにIdentifierを`Cell`に設定
<img width="911" alt="スクリーンショット 2019-08-31 23.43.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300444/9f787b81-d7ba-031e-d1be-4776a481a483.png">

以上で、関連付けをしてから実行してみてください！

- デモ

![demo](https://media.giphy.com/media/lrgvJRSsulgXZOA8PY/giphy.gif)

この辺で、タイムライン編の前編を終えて、次回は後編として進めて行きたいと思います。

# 投稿して、前の画面に戻ると投稿が反映されている。

![demo](https://media.giphy.com/media/KezWMwOdYJXjnmMSfa/giphy.gif)

# 関連記事
- [メール認証編](https://qiita.com/fummicc1-sub/items/876b000fb514927a8df7)
- [参考プロジェクト](https://github.com/fummicc1/SNSApp)
- [タイムライン編 後編](https://qiita.com/fummicc1-sub/items/74c216ad2158164a7b0c)