iOS
Firebase
swift4

Firebase(「Google丸投げ」でiOSアプリ開発が恐ろしくラクになる!Firebaseの使い方)チュートリアルで詰まったところのメモ

はじめに

「Google丸投げ」でiOSアプリ開発が恐ろしくラクになる!Firebaseの使い方

こちらを試していた時に、そのままコピペだと動かないので下記の環境に対応するように書き直しました
クラス名が異なるものもありました。

環境

Xcode9.2
Swift4.0.3
Firebase 4.13.0

このチュートリアル出来ること

Firebaseを使ってログイン・ログアウト機能を作ることが出来る
データベースを使って追加と削除が出来る

チュートリアルどおりだと動かず詰まった箇所

こちらチュートリアル

    switch errCode {
                        case .errorCodeUserNotFound:
                            self.showAlert("User account not found. Try registering")
                        case .errorCodeWrongPassword:
                            self.showAlert("Incorrect username/password combination")
                        default:
                            self.showAlert("Error: \(error.localizedDescription)")
                        }

こちらが修正後

if let error = error {
                    if let errCode = AuthErrorCode(rawValue: error._code) {
                        //ここ異なる 参考(https://code.i-harness.com/ja/q/23b70bf)
                        switch errCode {
                        case .invalidEmail:
                            self.showAlert("User account not found. Try registering")
                        case .wrongPassword:
                            self.showAlert("Incorrect username/password combination")
                        default:
                            self.showAlert("Error:\(error.localizedDescription)")
                        }
                    }
                    return
                }

コード全部

LoginViewController.swift

import UIKit
import FirebaseAuth

class LoginViewController: UIViewController {

    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var passwordField: UITextField!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        //FIRAuthは→Authにする
        if let _ = Auth.auth().currentUser {
            self.signIn()
        }
    }

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

    @IBAction func didTapSignIn(_ sender: UIButton) {
        let email = emailField.text
        let password = passwordField.text
        Auth.auth().signIn(withEmail: email!, password: password!, completion: { (user, error) in
            guard let _ = user else {
                if let error = error {
                    if let errCode = AuthErrorCode(rawValue: error._code) {
                        //ここ異なる 参考(https://code.i-harness.com/ja/q/23b70bf)
                        switch errCode {
                        case .invalidEmail:
                            self.showAlert("User account not found. Try registering")
                        case .wrongPassword:
                            self.showAlert("Incorrect username/password combination")
                        default:
                            self.showAlert("Error:\(error.localizedDescription)")
                        }
                    }
                    return
                }
                //このreturnがいる
                return assertionFailure("user and error are nil")
            }
            self.signIn()
        })
    }

    @IBAction func didRequestPasswordReset(_ sender: UIButton) {
        let prompt = UIAlertController(title: "To Do App", message: "Email:", preferredStyle: .alert)
        let okAciont = UIAlertAction(title: "OK", style: .default) { _ in
            let userInput = prompt.textFields![0].text
            if (userInput!.isEmpty) {
                return
            }
            Auth.auth().sendPasswordReset(withEmail: userInput!, completion: { (error) in
                if let error = error {
                    if let errCode = AuthErrorCode(rawValue: error._code) {
                        switch errCode {
                        case .userNotFound:
                            DispatchQueue.main.async{
                                self.showAlert("User account not found. Try registering")
                            }
                        default:
                            DispatchQueue.main.async {
                                self.showAlert("Error: \(error.localizedDescription)")
                            }
                        }
                    }
                    return
                } else {
                    DispatchQueue.main.async {
                        self.showAlert("You'll receive an email shortly to reset your password.")
                    }
                }

            })
        }
        prompt.addTextField(configurationHandler: nil)
        prompt.addAction(okAciont)
        self.present(prompt, animated: true, completion: nil)

    }

    func showAlert(_ message: String) {
        let alertController = UIAlertController(title: "To Do App", message: message, preferredStyle: UIAlertControllerStyle.alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: nil))
        self.present(alertController, animated: true, completion: nil)
    }

    func signIn() {
        self.performSegue(withIdentifier: "SignInFromLogin", sender: nil)
    }

}

Item.swift

import Foundation
import FirebaseDatabase

class Item {
    var ref: DatabaseReference?
    var title: String?

    init(snapshot: DataSnapshot) {
        ref = snapshot.ref

        let date = snapshot.value as! Dictionary<String, String>
        title = date["title"]! as String
    }
}

SignUpViewController.swift

import UIKit
import FirebaseAuth

class SignUpViewController: UIViewController {

    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var passwordField: UITextField!

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

    @IBAction func didTapSignUp(_ sender: UIButton) {
        let email = emailField.text
        let password = passwordField.text
        Auth.auth().createUser(withEmail: email!, password: password!, completion: {(user, error) in
            if let error = error {
                if let errCode = AuthErrorCode(rawValue: error._code) {
                    switch errCode {
                    case .invalidEmail:
                        self.showAlert("Enter a valid email.")
                    case .emailAlreadyInUse:
                        self.showAlert("Email already in use.")
                    default:
                        self.showAlert("Error: \(error.localizedDescription)")
                    }

                }
                return

            }
            self.signIn()
        })


    }

    @IBAction func didTapBackToLogin(_ sender: UIButton) {
        self.dismiss(animated: true, completion: {})
    }

    func showAlert(_ message: String) {
        let alertController = UIAlertController(title: "To Do App", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
        self.present(alertController, animated: true, completion: nil)
    }

    func signIn() {
        performSegue(withIdentifier: "SignInFromSignUp", sender: nil)
    }

}

ItemsTableViewController.swift

import UIKit
import Firebase

class ItemsTableViewController: UITableViewController {

    var user: User!
    var items = [Item]()
    var ref: DatabaseReference!
    private var databaseHandle: DatabaseHandle!

    override func viewDidLoad() {
        super.viewDidLoad()
        //ユーザーのログイン時の情報をuserにセット
        self.user = Auth.auth().currentUser
        //Firebaseデータベースのルート
        ref = Database.database().reference()
        startObservingDatabase()
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return items.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        let item = items[indexPath.row]
        cell.textLabel?.text = item.title
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            let item = items[indexPath.row]
            item.ref?.removeValue()
        }
    }


    @IBAction func didTapSignOut(_ sender: UIBarButtonItem) {
        do {
            //FIRAuth→Auth
            try Auth.auth().signOut()
            performSegue(withIdentifier: "SignOut", sender: nil)
        } catch let error {
            assertionFailure("Error signing out: \(error)")
        }
    }

    @IBAction func didTapAddItem(_ sender: UIBarButtonItem) {
        let prompt = UIAlertController(title: "To Do App", message: "To Do Item", preferredStyle:.alert)
        let okAction = UIAlertAction(title: "OK", style: .default){ (action) in
            let userInput = prompt.textFields![0].text
            if (userInput!.isEmpty) {
                return
            }
            self.ref.child("users").child(self.user.uid).child("items").childByAutoId().child("title").setValue(userInput)
        }
        prompt.addTextField(configurationHandler: nil)
        prompt.addAction(okAction)
        present(prompt, animated: true, completion: nil)
    }

    //データベースに加えられた変更も検知するリスナーをセット
    //初期値取得のために一度呼び出されデータに変更がある度に呼ばれる
    func startObservingDatabase() {
        //.value
        //FIRDataEventTypeValue:そのパスにあるコンテンツ全体の変更の検知と読み取りをする
        //FIRDataEventTypeValueイベントは参照するデータベースのデータが変更されると、子要素の変更も含めて毎回実行される
        databaseHandle = ref.child("users/\(self.user.uid)/items").observe(.value, with: { (snapshot) in
            var newItems = [Item]()
            for ItemSnapShot in snapshot.children {
                let item = Item(snapshot: ItemSnapShot as! DataSnapshot)
                newItems.append(item)
            }
            self.items = newItems
            self.tableView.reloadData()
        })
    }

    deinit {
        ref.child("users/\(self.user.uid)/items").removeObserver(withHandle: databaseHandle)
    }

}

参考文献

「Google丸投げ」でiOSアプリ開発が恐ろしくラクになる!Firebaseの使い方
Firebase認証エラーの読み込み

最後に

なにか間違いなどありましたらご指摘お願いします