Firebaseでお手軽サーバレスアプリ（Realtime Database+CloudFunctions+Cloud Messaging+Android(kotlin)+iOS(swift)）その3


# はじめに
今回はiOSからFirebaseのRealtime Databaseに接続しデータの登録と参照を行ってみます。
必要なのは、MacとXCodeです。
今回はMacBook Air (Retina, 13-inch, 2018)、XCode Version 11.3.1 (11C504)を使っています。

# XCodeでアプリのベースを作成
- Create new project を実行
- iOSのSingle View Appを選択
- Product nameを適当につけます
- 言語はSwift、User InterFaceはStoryBoardを選択
- 実行してスケルトンが動作することを確認します

# Firebaseの準備
- ブラウザでFirebaseのコンソールにアクセスし、iOSを追加します
![InkedScreen Shot 2020-02-24 at 9.48.30_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577898/fd8d967a-c7a3-a389-2798-7f6199cc7d33.jpeg)
![InkedScreen Shot 2020-02-24 at 9.49.28_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577898/8278db92-eb9a-17f1-5f8a-a216a1fb9257.jpeg)


- iOSアプリにFireBaseを追加するで、iOSアプリのバンドルIDを入力します
- バンドIDはプロジェクトを選択しGeneralのタブで参照できます
- 設定ファイルをダウンロードせよと出るのでFireBaseのページで指示されるとおりに操作します
- plistを追加できたら「次へ」を押します
- cocoapodsが必要なので、入れてない場合はインストールします
- terminalでプロジェクトのフォルダを開きます
- terminalでの手順はFireBaseのページの通です
    - pod init
    - Podfilesの編集
    - pod install
- xcodeを一旦閉じて「.xcworkspace」で開きます
- AppDelegateにFirebaseへ接続するコードを追加します


```Swift:AppleDelegate.swift
import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FirebaseApp.configure()
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}
```

- ここまで来たらビルドして実行して、firebaseのページでアクセスできたか確認します
- firebaseがアプリに正常に追加されました　と表示されればO
K


<img width="735" alt="Screen Shot 2020-02-24 at 10.16.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577898/26ab98f2-0ecd-4391-d67a-7328dc44ff06.png">


# iOSアプリにUIを追加
- Main.stoyboardにTextViewを追加します
- Constraintsは上左右は0、下は200
- 下のスペースにButtonを追加します
- Constrainsは上下左右5に設定します


<img width="969" alt="Screen Shot 2020-02-24 at 10.27.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577898/8f129bf7-da53-d098-6703-0ec1ac4694b2.png">



# DBへの書き込みを追加
- ViewControllerにボタンを押した際のイベントハンドラーを追加
- DBに書き込むコードを書きます

```swift:ViewController.swift
import UIKit
import FirebaseDatabase

class ViewController: UIViewController {

    var databaseRef: DatabaseReference!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
       databaseRef = Database.database().reference().child("/log")

    }

    @IBAction func WriteToDB(_ sender: Any) {
        
        view.endEditing(true)
        let MyTime:Int = Int(NSDate().timeIntervalSince1970*1000)
        let messageData = ["name": "user2(ios)", "message": "だれ？","timestamp":String(MyTime)]
        databaseRef.childByAutoId().setValue(messageData)
    
    }
    
}
```

- FirebaseDatabaseが解決できないとエラーになると思います。PodsのprofileにFirebaseDatabaseを追加します。
- Podsに追加しらPod installをします


```swift:profile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'otameshi-ios' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for otameshi-ios
pod 'Firebase/Analytics'
pod 'Firebase/Database'
end
```

- ビルドして動作させます。ボタンを押してDBにデータが書き込まれれば成功です

# データの取得と表示


- データの更新前にTextViewを更新します

```swift:ViewContoller.swift
import UIKit
import FirebaseDatabase

class ViewController: UIViewController {

    var databaseRef: DatabaseReference!
    @IBOutlet weak var TextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        databaseRef = Database.database().reference().child("/log")
        databaseRef.observe(.childAdded, with: { snapshot in
        if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"], let time = obj["timestamp"] {
            let currentText = self.TextView.text
            self.TextView.text = (currentText ?? "") + "\n \(time) \(name) : \(message)"
            }
        })
    }
    
    @IBAction func WriteToDB(_ sender: Any) {
        self.view.endEditing(true)
        let MyTime:Int = Int(NSDate().timeIntervalSince1970*1000)
        let messageData = ["name": "user2(ios)", "message": "だれ？","timestamp":String(MyTime)]
        self.databaseRef.childByAutoId().setValue(messageData)
    }
    
}
```

# TextViewをオートスクロールに
- 一番下まで行った時に勝手にスクロールするようにします

```swift:ViewContoller.swift
import UIKit
import FirebaseDatabase

class ViewController: UIViewController {

    var databaseRef: DatabaseReference!
    @IBOutlet weak var TextView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        databaseRef = Database.database().reference().child("/log")
        databaseRef.observe(.childAdded, with: { snapshot in
        if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"], let time = obj["timestamp"] {
            let currentText = self.TextView.text
            self.TextView.isScrollEnabled = false
            self.TextView.text = (currentText ?? "") + "\n \(time) \(name) : \(message)"
            self.scrollToBottom()
            }
        })
    }
    
    @IBAction func WriteToDB(_ sender: Any) {
        self.view.endEditing(true)
        let MyTime:Int = Int(NSDate().timeIntervalSince1970*1000)
        let messageData = ["name": "user2(ios)", "message": "だれ？","timestamp":String(MyTime)]
        self.databaseRef.childByAutoId().setValue(messageData)
    }

    func scrollToBottom() {
        TextView.selectedRange = NSRange(location: TextView.text.count, length: 0)
        TextView.isScrollEnabled = true

        let scrollY = TextView.contentSize.height - TextView.bounds.height
        let scrollPoint = CGPoint(x: 0, y: scrollY > 0 ? scrollY : 0)
        TextView.setContentOffset(scrollPoint, animated: true)
    }    
}
```

# まとめ
これでAndroidとiOSのアプリが揃いました。
それぞれでボタンを押すと同時に反映されることが確認できると思います。


<img width="422" alt="Screen Shot 2020-02-24 at 12.06.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577898/653f53ed-a3e7-f4e5-0d17-67a337460481.png">


# このシリーズについて

 [その１:全体像とFirebaseの準備](https://qiita.com/uchi2020/items/22cec9cbd8b65298844f)

 [その２:Androidのクライアントアプリからデータの登録と表示まで](https://qiita.com/uchi2020/items/3857098ca5e9052f4b52)


 [その３:iOSのクライアントアプリからデータの登録と表示まで](https://qiita.com/uchi2020/items/6cc6f1bc2465f6d4b318)


 [その４:準備中]

 [その５:準備中]