Help us understand the problem. What is going on with this article?

Firebaseを使ってチャットアプリを作ってみる会

ちょっと宣伝

https://note.com/gartaro/n/nae547c1a5f7a
自社サービスをアジャイル開発でやっていて、ブログやってます。
よかったらみていってください。

最初に

こちらのリポジトリからソースコードを落としてきてください。
https://github.com/mht-mikiya-okugawa/testFirebase1

完成品

スクリーンショット 2019-07-20 1.08.55.png
スクリーンショット 2019-07-20 1.09.30.png

やること

  • プロジェクトを作る
  • Firebaseに登録する
  • 設定ファイルのダウンロード
  • pod Install
  • 初期化コードの実装
  • アプリへのFirebaseの追加

実装する機能

  • チャット機能(読み書き)
  • ユーザー登録、メッセージ送信時のバリデーション機能の実装
  • チャットようにテーブルビューをひっくり返す
  • チャットルームから退出する時に表示するダイアログ

では早速作っていきましょう

実装

1.FIrebaseにアプリの登録

  • 1.FirebaseにiOSのバンドルIDを登録しましょう。
    スクリーンショット 2019-07-20 1.24.13.png

  • 2.設定ファイルのダウンロードと設定ファイルをプロジェクトに追加
    スクリーンショット 2019-07-20 1.24.26.png

  • 3.pod install

スクリーンショット 2019-07-20 1.24.31.png

  • 4.初期化コードをAppDelegateに追記

スクリーンショット 2019-07-20 1.24.38.png

  • 5.アプリを起動して、Firebaseをアプリに追加する スクリーンショット 2019-07-20 1.24.43.png

前準備はこれで完了です。

ここで先ほど落としてきたプロジェクトを開いてください。
masterブランチにチャット機能のない側だけのアプリがあります。

2.ユーザー登録画面の実装

UserRegistViewController.swift
    // チャット画面から戻ってきた時にろユーザー情報を解錠する
    override func viewWillAppear(_ animated: Bool) {
        self.nameTextField.text = ""
        UserDefaults.standard.removeObject(forKey: "name")
    }

    @objc func moveToChat() {
        // UserDefaultを使ってチャット画面にログイン名を登録する
        UserDefaults.standard.set(nameTextField.text, forKey: "name")
        let storyboard: UIStoryboard = UIStoryboard(name: "ChatRoom",bundle: nil)
        let viewController: UIViewController = storyboard.instantiateViewController(withIdentifier: "ChatRoom")
        show(viewController, sender: nil)
    }

3.チャット機能の実装

  • 読み取り機能
ChatRoomViewController.swift
    func initView() {
        // インスタンスの取得
        databaseRef = Database.database().reference()
        // データ読み取り機能
        databaseRef.observe(.childAdded, with: { snapshot in
            if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"] {
                self.messages.insert(obj, at: 0)
                self.tableView.reloadData()
            }
        })
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell
        let dictionary = messages[indexPath.row] as! [String: AnyObject]
        cell?.messageLabel?.text = dictionary["message"] as? String
        cell?.nameLabel?.text = dictionary["name"] as? String
        if cell?.nameLabel?.text == UserDefaults.standard.string(forKey: "name") {
            cell?.messageLabel.backgroundColor = UIColor(hex: "D7003E")
            cell?.messageLabel.textColor = UIColor(hex: "FFFFFF")
            cell?.iconImg.image = UIImage(named: "TUB_Red")
        } else {
            cell?.messageLabel.backgroundColor = UIColor(hex: "FFFFFF")
            cell?.messageLabel.textColor = UIColor(hex: "000000")
            cell?.iconImg.image = UIImage(named: "TUB_Blue")
        }
        return cell!
    }

  • 書き込み機能
ChatRoomViewController.swift
    @objc func sendData() {
        // データ書き込み機能
        if let message = messageTextField.text {
            let messageData = ["name": UserDefaults.standard.string(forKey: "name"), "message": message]
            databaseRef.childByAutoId().setValue(messageData)
            messageTextField.text = ""
        }
    }

4.チャットルームの退出

ChatRoomViewController.swift
//退出時のアラート
    func showNotAnswerDialog() {
        let alert: UIAlertController = UIAlertController(title: "退出", message: "現在のルームから退出しますか?", preferredStyle: .alert)
        // 決定ボタン
        let defaultAction: UIAlertAction = UIAlertAction(title: "退出", style: .default, handler: { (_: UIAlertAction!) -> Void in
            self.navigationController?.popViewController(animated: true)
        })
        // キャンセルボタン
        let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: .cancel, handler: { (_: UIAlertAction!) -> Void in
        })
        alert.addAction(cancelAction)
        alert.addAction(defaultAction)
        alert.preferredAction = defaultAction
        present(alert, animated: true, completion: nil)

    }

    @objc func exit(_ sender: UIBarButtonItem) {
        showNotAnswerDialog()
    }

5.テーブルビューの反転

  • TableViewの反転
ChatRoomViewController.swift
    func initView() {
        // TableViewを反転する処理
        tableView.transform = __CGAffineTransformMake(1, 0, 0, -1, 0, 0)
    }

  • cellの反転
ChatRoomViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//省略
        cell!.transform = __CGAffineTransformMake(1, 0, 0, -1, 0, 0)
        return cell!
    }

6.バリデーションの実装

UserRegistViewController.swift
    func initView() {
//省略
        changeLoginEnabled()
        validTxtField(textField: nameTextField)
        sendButton.setTitleColor(UIColor.white, for: .normal)
        sendButton.setTitleColor(UIColor.gray, for: .disabled)
    }


    private func changeLoginEnabled() {
        if nameTextField.text!.count > 0{
            // ボタンの活性状態
            sendButton.isEnabled = true
            sendButton.backgroundColor = UIColor(hex: "D7003E")
        } else {
            // ボタンの非活性状態
            sendButton.isEnabled = false
            sendButton.backgroundColor = UIColor(hex: "801a2e")

        }
    }

    func validTxtField(textField: UITextField) {
        // textの変更を検知する
        textField.rx.text.subscribe(onNext: { _ in
            print(textField.text!.count)
            // チェック関数の呼び出し
            self.changeLoginEnabled()
        }).disposed(by: disposeBag)
    }

ChatRoomViewController.swift
    func initView() {
//省略
        // バリデーション
        validTxtField(textField: messageTextField)
            sendButton.setTitleColor(UIColor.white, for: .normal)
            sendButton.setTitleColor(UIColor.gray, for: .disabled)

    }
    override func viewWillAppear(_ animated: Bool) {
        tableView.separatorStyle = .none
        changeLoginEnabled()
    }


    // 文字数チェック
    private func changeLoginEnabled() {
        if messageTextField.text!.count > 0{
            // ボタンの活性状態
            sendButton.isEnabled = true
            sendButton.backgroundColor = UIColor(hex: "D7003E")
        } else {
            sendButton.isEnabled = false
            sendButton.backgroundColor = UIColor(hex: "801a2e")
        }

    }

    func validTxtField(textField: UITextField) {
        // textの変更を検知する
        textField.rx.text.subscribe(onNext: { _ in
            print(textField.text!.count)

            // チェック関数の呼び出し
            self.changeLoginEnabled()
        }).disposed(by: disposeBag)
    }


@objc func sendData() {
        // データ書き込み機能
        if let message = messageTextField.text {
            let messageData = ["name": UserDefaults.standard.string(forKey: "name"), "message": message]
            databaseRef.childByAutoId().setValue(messageData)
            messageTextField.text = ""
        }
        changeLoginEnabled()

    }


完成品

UserRegistViewController.swift
//
//  UserRegistViewController.swift
//  testFirebase1
//
//  Created by がーたろ on 2019/07/16.
//  Copyright © 2019 がーたろ. All rights reserved.
//

import UIKit
import RxCocoa
import RxSwift

class UserRegistViewController: UIViewController {

    @IBOutlet var nameTextField: UITextField!
    @IBOutlet var sendButton: UIButton!
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        initView()
    }

    func initView() {
        // 見た目
        self.nameTextField.layer.cornerRadius = 5
        self.sendButton.layer.cornerRadius = 25
        self.title = "TUB"
        sendButton.setTitleColor(UIColor.white, for: .normal)
        sendButton.setTitleColor(UIColor.gray, for: .disabled)
        self.navigationController?.navigationBar.titleTextAttributes = [
            // 文字の色
            .foregroundColor: UIColor.white,
            NSAttributedString.Key.font: UIFont(name: "Futura", size: 30)!
        ]
        self.navigationController?.navigationBar.tintColor = .white
        sendButton.addTarget(self, action: #selector(self.moveToChat), for: .touchUpInside)

        changeLoginEnabled()
        validTxtField(textField: nameTextField)
    }

    // チャット画面から戻ってきた時にろユーザー情報を解錠する
    override func viewWillAppear(_ animated: Bool) {
        self.nameTextField.text = ""
        UserDefaults.standard.removeObject(forKey: "name")
    }

    @objc func moveToChat() {
        // UserDefaultを使ってチャット画面にログイン名を登録する
        UserDefaults.standard.set(nameTextField.text, forKey: "name")
        let storyboard: UIStoryboard = UIStoryboard(name: "ChatRoom",bundle: nil)
        let viewController: UIViewController = storyboard.instantiateViewController(withIdentifier: "ChatRoom")
        show(viewController, sender: nil)
    }

    private func changeLoginEnabled() {
        if nameTextField.text!.count > 0{
            // ボタンの活性状態
            sendButton.isEnabled = true
            sendButton.backgroundColor = UIColor(hex: "D7003E")
        } else {
            // ボタンの非活性状態
            sendButton.isEnabled = false
            sendButton.backgroundColor = UIColor(hex: "801a2e")

        }
    }

    func validTxtField(textField: UITextField) {
        // textの変更を検知する
        textField.rx.text.subscribe(onNext: { _ in
            print(textField.text!.count)
            // チェック関数の呼び出し
            self.changeLoginEnabled()
        }).disposed(by: disposeBag)
    }
}

ChatRoomViewController.swift
//
//  ChatRoomViewController.swift
//  testFirebase1
//
//  Created by がーたろ on 2019/07/09.
//  Copyright © 2019 がーたろ. All rights reserved.
//

import UIKit
import Firebase
import FirebaseDatabase
import RxCocoa
import RxSwift

class ChatRoomViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet var sendButton: UIButton!
    @IBOutlet var messageTextField: UITextField!
    @IBOutlet var tableView: UITableView!
    var databaseRef:DatabaseReference!
    private let disposeBag = DisposeBag()
    // RealtimeDatabaseに送るメッセージデータ
    var messages = [Any]()
    override func viewDidLoad() {
        super.viewDidLoad()
        initView()
    }

    func initView() {
        sendButton.addTarget(self, action: #selector(self.sendData), for: .touchUpInside)
        sendButton.layer.cornerRadius = 25
        self.messageTextField.layer.cornerRadius = 5
        self.title = "TUB"
        sendButton.setTitleColor(UIColor.white, for: .normal)
        sendButton.setTitleColor(UIColor.gray, for: .disabled)
        self.navigationController?.navigationBar.titleTextAttributes = [
            // 文字の色
            .foregroundColor: UIColor.white,
            NSAttributedString.Key.font: UIFont(name: "Futura", size: 30)!
        ]
        self.navigationController?.navigationBar.tintColor = .white
        self.navigationItem.hidesBackButton = true
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "退出", style: .plain, target: self, action: #selector(exit(_:)))
        tableView.delegate = self
        tableView.dataSource = self
        tableView.indicatorStyle = UIScrollView.IndicatorStyle.black
        tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
        // インスタンスの取得
        databaseRef = Database.database().reference()
        // データ読み取り機能
        databaseRef.observe(.childAdded, with: { snapshot in
            if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"] {
                self.messages.insert(obj, at: 0)
                self.tableView.reloadData()
            }
        })
        // TableViewを反転する処理
        tableView.transform = __CGAffineTransformMake(1, 0, 0, -1, 0, 0)
        // バリデーション
        validTxtField(textField: messageTextField)
    }

    override func viewWillAppear(_ animated: Bool) {
        tableView.separatorStyle = .none
        changeLoginEnabled()
    }



    @objc func sendData() {
        // データ書き込み機能
        if let message = messageTextField.text {
            let messageData = ["name": UserDefaults.standard.string(forKey: "name"), "message": message]
            databaseRef.childByAutoId().setValue(messageData)
            messageTextField.text = ""
        }
        changeLoginEnabled()

    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell
        let dictionary = messages[indexPath.row] as! [String: AnyObject]
        cell?.messageLabel?.text = dictionary["message"] as? String
        cell?.nameLabel?.text = dictionary["name"] as? String
        if cell?.nameLabel?.text == UserDefaults.standard.string(forKey: "name") {
            cell?.messageLabel.backgroundColor = UIColor(hex: "D7003E")
            cell?.messageLabel.textColor = UIColor(hex: "FFFFFF")
            cell?.iconImg.image = UIImage(named: "TUB_Red")
        } else {
            cell?.messageLabel.backgroundColor = UIColor(hex: "FFFFFF")
            cell?.messageLabel.textColor = UIColor(hex: "000000")
            cell?.iconImg.image = UIImage(named: "TUB_Blue")
        }



        cell!.transform = __CGAffineTransformMake(1, 0, 0, -1, 0, 0)
        return cell!
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 76
    }

    @objc func exit(_ sender: UIBarButtonItem) {
        showNotAnswerDialog()
    }
    //退出時のアラート
    func showNotAnswerDialog() {
        let alert: UIAlertController = UIAlertController(title: "退出", message: "現在のルームから退出しますか?", preferredStyle: .alert)
        // 決定ボタン
        let defaultAction: UIAlertAction = UIAlertAction(title: "退出", style: .default, handler: { (_: UIAlertAction!) -> Void in
            self.navigationController?.popViewController(animated: true)
        })
        // キャンセルボタン
        let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: .cancel, handler: { (_: UIAlertAction!) -> Void in
        })
        alert.addAction(cancelAction)
        alert.addAction(defaultAction)
        alert.preferredAction = defaultAction
        present(alert, animated: true, completion: nil)

    }
    // 文字数チェック
    private func changeLoginEnabled() {
        if messageTextField.text!.count > 0{
            // ボタンの活性状態
            sendButton.isEnabled = true
            sendButton.backgroundColor = UIColor(hex: "D7003E")
        } else {
            sendButton.isEnabled = false
            sendButton.backgroundColor = UIColor(hex: "801a2e")
        } 
    }

    func validTxtField(textField: UITextField) {
        // textの変更を検知する
        textField.rx.text.subscribe(onNext: { _ in
            print(textField.text!.count)

            // チェック関数の呼び出し
            self.changeLoginEnabled()
        }).disposed(by: disposeBag)
    }
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした