【Swift】Firebase - メアド&パスワードで会員登録/ログイン

 

完成図

会員登録

 

f:id:nekokichi_yos2:20190211113425p:plain

 

ログイン

 

f:id:nekokichi_yos2:20190211113439p:plain

 

パスワードリセット

 

f:id:nekokichi_yos2:20190211113550p:plain

 

解説

 

会員登録

Auth.auth().createUser(withEmail: mailForm.text!, password: passForm.text!) { (user, error) in
   Auth.auth().currentUser?.sendEmailVerification(completion: { (error) in
   })
}
  • メアドとパスワードを入力すると、入力したメアドに認証メールが送られる
  • 認証メールにあるリンクを押せば、会員登録が完了
  • ※注意:パスワードは7文字以上じゃないと会員登録できない

 

f:id:nekokichi_yos2:20190211131002p:plain

f:id:nekokichi_yos2:20190211130949j:plain

 

ログイン

Auth.auth().signIn(withEmail: mailForm.text!, password: passForm.text!) { (user, error) in
}

 

ログアウト

if Auth.auth().currentUser != nil {
         //ログアウト成功
         do {
             try Auth.auth().signOut()
         //ログアウト失敗
} catch let signOutError as NSError { print (signOutError) } }

 

パスワードリセット

Auth.auth().sendPasswordReset(withEmail: resetEmail!, completion: { (error) in
      DispatchQueue.main.async {
          if error != nil {
              self.alert(title: "メールを送信しました。", message: "メールでパスワードの再設定を行ってください。", actiontitle: "OK")
          } else {
              self.alert(title: "エラー", message: "このメールアドレスは登録されてません。", actiontitle: "OK")
          }
      }
})
  • パスワードリセットの際、リセット用のメールが送られる
  • メールに貼ってあるリンクを押せば、リセット後のパスワードを入力
  • 入力すれば、パスワードが更新される

 

f:id:nekokichi_yos2:20190211131007j:plain

f:id:nekokichi_yos2:20190211130958p:plain

f:id:nekokichi_yos2:20190211130953p:plain

 

ストーリーボード

 

f:id:nekokichi_yos2:20190211113111p:plain

 

ソースコード

会員登録画面

import UIKit
import Firebase

class SignUp: UIViewController,UITextFieldDelegate {

    //メールアドレスとパスワード
    @IBOutlet weak var mailForm: UITextField!
    @IBOutlet weak var passForm: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //UITextFieldのデリゲート
        mailForm.delegate = self
        passForm.delegate = self
        
        //UITextFieldに枠線,角丸を施す
        mailForm.layer.borderWidth = 1
        mailForm.layer.cornerRadius = 10
        passForm.layer.borderWidth = 1
        passForm.layer.cornerRadius = 10
    }
    
    //会員登録
    @IBAction func signup(_ sender: Any) {
        //2つのフォームが入力されてる場合
        if mailForm.text != "" && passForm.text != "" {
            //入力したパスワードが7文字以上の場合
            if (passForm.text?.count)! > 6  {
                //会員登録開始
                Auth.auth().createUser(withEmail: mailForm.text!, password: passForm.text!) { (user, error) in
                    //ログイン成功
                    if error == nil {
                        //登録メアドに確認のメールを送る
                        Auth.auth().currentUser?.sendEmailVerification(completion: { (error) in
                            //エラー処理
                            if error != nil {
                                let storyboard = UIStoryboard(name: "Main", bundle:Bundle.main)
                                let rootViewController = storyboard.instantiateViewController(withIdentifier: "main")
                                UIApplication.shared.keyWindow?.rootViewController = rootViewController
                            } else {
                            }
                        })
                    //ログイン失敗
                    } else {
                        self.alert(title: "エラー", message: "ログイン失敗", actiontitle: "OK")
                    }
                }
            //入力したパスワードが6文字以下の場合
            } else {
                self.alert(title: "エラー", message: "7文字以上のパスワードを入力してください。", actiontitle: "OK")
            }
        //いずれかのフォームが未入力の場合
        } else {
            self.alert(title: "エラー", message: "入力されてない箇所があります。", actiontitle: "OK")
        }
    }

    //ログイン画面へ移行
    @IBAction func changeToLogin(_ sender: Any) {
        performSegue(withIdentifier: "changesignin", sender: nil)
    }
    
    //アラート
    func alert(title:String,message:String,actiontitle:String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: actiontitle, style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
   
    //Returnでキーボードを閉じる
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    //画面外タップでキーボードを閉じる
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        mailForm.resignFirstResponder()
        passForm.resignFirstResponder()
    }

}

ログイン画面/ログアウト

import UIKit
import Firebase

class SignIn: UIViewController,UITextFieldDelegate {

    @IBOutlet weak var mailForm: UITextField!
    @IBOutlet weak var passForm: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        mailForm.delegate = self
        passForm.delegate = self
        
        mailForm.layer.borderWidth = 1
        mailForm.layer.cornerRadius = 10
        passForm.layer.borderWidth = 1
        passForm.layer.cornerRadius = 10
    }
    
    @IBAction func remindPassword(_ sender: Any) {
        let remindPasswordAlert = UIAlertController(title: "パスワードをリセット", message: "メールアドレスを入力してください", preferredStyle: .alert)
        remindPasswordAlert.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: nil))
        remindPasswordAlert.addAction(UIAlertAction(title: "リセット", style: .default, handler: { (action) in
            let resetEmail = remindPasswordAlert.textFields?.first?.text
            Auth.auth().sendPasswordReset(withEmail: resetEmail!, completion: { (error) in
                DispatchQueue.main.async {
                    if error != nil {
                        self.alert(title: "メールを送信しました。", message: "メールでパスワードの再設定を行ってください。", actiontitle: "OK")
                    } else {
                        self.alert(title: "エラー", message: "このメールアドレスは登録されてません。", actiontitle: "OK")
                    }
                }
            })
        }))
        remindPasswordAlert.addTextField { (textField) in
            textField.placeholder = "test@gmail.com"
        }
        self.present(remindPasswordAlert, animated: true, completion: nil)
    }
    
    @IBAction func signin(_ sender: Any) {
        //ちゃんと入力されてるかの確認
        if mailForm.text != "" && passForm.text != "" {
            Auth.auth().signIn(withEmail: mailForm.text!, password: passForm.text!) { (user, error) in
                if error == nil {
                    //ホーム画面へ移行
                    let storyboard = UIStoryboard(name: "Main", bundle:Bundle.main)
                    let rootViewController = storyboard.instantiateViewController(withIdentifier: "main")
                    UIApplication.shared.keyWindow?.rootViewController = rootViewController
                } else {
                    self.alert(title: "エラー", message: "メールアドレスまたはパスワードが間違ってます。", actiontitle: "OK")
                }
            }
        } else {
            self.alert(title: "エラー", message: "入力されてない箇所があります。", actiontitle: "OK")
        }
    }
    
    @IBAction func changeTosignup(_ sender: Any) {
        performSegue(withIdentifier: "changeTosignup", sender: nil)
    }
    
    //アラート
    func alert(title:String,message:String,actiontitle:String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: actiontitle, style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        mailForm.resignFirstResponder()
        passForm.resignFirstResponder()
    }

}

 

ログアウト

import UIKit
import Firebase

class ViewController: UIViewController{
    
    //ログアウト
    @IBAction func signout(_ sender: Any) {
        //ログアウト開始
        if Auth.auth().currentUser != nil {
            //ログアウト成功
            do {
                try Auth.auth().signOut()
            //ログアウト失敗
            } catch let signOutError as NSError {
                print (signOutError)
            }
        } else {
            return
        }
        
        //ログイン画面に戻る
        let storyboard = UIStoryboard(name: "Main", bundle:Bundle.main)
        let rootViewController = storyboard.instantiateViewController(withIdentifier: "gosignin")
        UIApplication.shared.keyWindow?.rootViewController = rootViewController
    }
    
}

 

【Swift】スクロール画面に大量のUIButtonを等間隔で配置

 

完成図

 

f:id:nekokichi_yos2:20190211113825p:plain

 

解説

 

「手順」

  1. ScrollViewを設置
  2. UIViewを生成
  3. UIButtonを生成
  4. UIButton.frameを設定
  5. UIButton.tagを設定
  6. UIButtonに文字や処理を設定
  7. UIViewに設置
  8. 3~7をループ
  9. ScrollViewにUIViewを設置
  10. ScrollView.contenSizeとUIView.bounds.sizeを同じにする

 

「ポイント」

  • for文のループ内にある変数iにかける数値がUIButton同士の幅となる
  • 横ならx座標を増加させ、縦ならy座標を増加させればいい

 

ストーリーボード

f:id:nekokichi_yos2:20190211113903p:plain

 

ソースコード

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var scrollView1: UIScrollView!
    @IBOutlet weak var scrollView2: UIScrollView!
    
    var vc1 = UIView()
    var vc2 = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        verticalScroll()
        horizontalScroll()
    }
    
    //アラート
    @objc func alert() {
        let alert = UIAlertController(title: "成功", message: "タップ", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
    
    //横スクロール
    func horizontalScroll() {
        //vcのframe
        vc1.frame = CGRect(x: 0, y: 0, width: 1000, height: 55)
        
        //上部のスクロールビューに多数のボタンを配置
        for i in 0...6 {
            let button = UIButton()
            //サイズ
            
            button.frame = CGRect(x: (i*100), y: 30, width: 80, height: 55)
            //タグ
            button.tag = i
            //buttonに文字を挿入
            setTitleForButton(tag: button.tag, button: button)
            //button.titleの色
            button.setTitleColor(.white, for: .normal)
            button.layer.borderWidth = 1
            //buttonに処理を追加
//            button.addTarget(self, action: #selector(alert), for: .touchUpInside)
            //vcに載せる
            vc1.addSubview(button)
        }
        
        //スクロールビューにvcを配置
        scrollView1.addSubview(vc1)
        scrollView1.contentSize = vc1.bounds.size
    }
    
    //縦スクロール
    func verticalScroll() {
        //vcのframe
        vc2.frame = CGRect(x: 0, y: 0, width: 240, height: 1000)
        
        //上部のスクロールビューに多数のボタンを配置
        for i in 0...6 {
            let button = UIButton()
            //サイズ
            button.frame = CGRect(x: 120, y: (i*100), width: 80, height: 55)
            //タグ
            button.tag = i
            //buttonに文字を挿入
            setTitleForButton(tag: button.tag, button: button)
            //button.titleの色
            button.setTitleColor(.white, for: .normal)
            button.layer.borderWidth = 1
            //buttonに処理を追加
//            button.addTarget(self, action: #selector(alert), for: .touchUpInside)
            //vcに載せる
            vc2.addSubview(button)
        }
        
        //スクロールビューにvcを配置
        scrollView2.addSubview(vc2)
        scrollView2.contentSize = vc2.bounds.size
    }

    //スクロールビューのボタンに文字を入れる
    func setTitleForButton(tag:Int, button:UIButton){
        switch tag {
        case 0:
            button.setTitle("最新", for: .normal)
        case 1:
            button.setTitle("人気", for: .normal)
        case 2:
            button.setTitle("フォロー", for: .normal)
        case 3:
            button.setTitle("文学", for: .normal)
        case 4:
            button.setTitle("社会", for: .normal)
        case 5:
            button.setTitle("科学", for: .normal)
        case 6:
            button.setTitle("ビジネス", for: .normal)
        default:
            break
        }
    }

}

 

【Swift】Firebaseから取得するデータを調べてみた

 

 

保存してあるデータ

 

f:id:nekokichi_yos2:20190204113353p:plain

 

解説

 

Firebaseからデータを取得する時、

  • どのような型(クラス)で取得されるのか?
  • どんなメソッドがあるのか?

という疑問が湧かないだろうか?

 

udemyで初めてFirebaseを知り、自分で試してみて、挫折しかけた。

 

クエリはもちろん、海藻や階層のURLの扱い方すらちんぷんかんぷんだった。

 

中でも、取得するデータの扱いには一苦労。

 

なんとなーく使ってるあなたや僕のためにも、今回はFirebaseから取得するデータを調べてみた。

 

デバック結果

 

「snap」

f:id:nekokichi_yos2:20190204114525p:plain

f:id:nekokichi_yos2:20190204114541p:plain

f:id:nekokichi_yos2:20190204114600p:plain

f:id:nekokichi_yos2:20190204114611p:plain

 

 

「snap.children」

 9

 

「snap.exists」
true

 

「snap.hasChildren」
true


「snapdata」

f:id:nekokichi_yos2:20190204114636p:plain

 


「item」
[["State": "ボロボロ", "UserID": "userID", "Date": "2019/02/01 12:17:54", "Title": "gfdsf", "DeliveryBurden": "着払い(受取人)", "Good": "0", "DeliveryDay": "5~6日", "UserName": "userName", "ItemID": "BfdQbs7ry", "DeliveryWay": "普通郵便", "Status": "display", "Category": "スポーツ"], ["?": "?"], ["?": "?"], ["?": "?"], ["?": "?"]]

 

ソースコード

 

func downloadImageData() {
        //FIrebaseのデータベースの参照URL
        let ref = Database.database().reference(fromURL: "https://bookshare-b78b4.firebaseio.com/")
        ref.child("Item").observe(.value) { (snap) in
            //階層下のデータを全部表示
            print(snap)
            //階層下のデータのメモリ情報?
            print(snap.children)
            //階層下のデータ数
            print(snap.childrenCount)
            //階層は存在する?
            print(snap.exists())
            //階層下にデータはある?
            print(snap.hasChildren())
            for item in snap.children {
                let snapdata = item as! DataSnapshot
                //1つのデータ
                print(snapdata)
                let item = snapdata.value as! [[String:String]]
                //snapdataを辞書型に変換
                print(item)
            }
        }
    }

 

【Swift】Firebaseに画像を保存/画像を読み込む

 

完成図

 

「操作画面」

f:id:nekokichi_yos2:20190204110834p:plain

 

「保存されているデータ」

f:id:nekokichi_yos2:20190204110838p:plain

 

解説

 

Firebaseから画像を取得するには、2つの方法がある。

  • Storageの参照で取得
  • FiirebaseUIで取得

(参考ページ)

 

firebase.google.com

 

前者は一般的な方法だがクロージャやエラーハンドリングのせいでコードが長くなりやすい。

 

今回は、たった3行でFirebaseから画像をダウンロードする方法を紹介する。
(ついでに画像を保存する方法も載せておく。)

 

(参考ページ)

firebase.google.com

 

「アップロード時のファイル名について」

 

画像やファイルをアップロードするとき、

  • Storage.storage.reference(forURL: ストレージのURL).child(ファイル名(階層名))

で保存する。

 

もし好きなファイル名にしたいなら、

child(命名したい文字列)

 

ランダムに命名したいなら、

childByAutoId()

 

ソースコード

 

import UIKit
import Firebase
import FirebaseUI

class FirebaseStorage: UIViewController {
    
    @IBOutlet weak var imageView: UIImageView!
    
    @IBAction func uploadImage(_ sender: Any) {
        uploadImage()
    }
    
    @IBAction func loadImage(_ sender: Any) {
        loadImage()
    }
    
    //Storageに画像を保存する
    func uploadImage() {
        //Storageの参照("Item"という名前で保存)
        let storageref = Storage.storage().reference(forURL: "gs://sample-e206e.appspot.com").child("Item")
        //画像
        let image = UIImage(named: "猫.png")
        //imageをNSDataに変換
        let data = image!.jpegData(compressionQuality: 1.0)! as NSData
        //Storageに保存
        storageref.putData(data as Data, metadata: nil) { (data, error) in
            if error != nil {
                return
            }
        }
        self.dismiss(animated: true, completion: nil)
    }
    
    //取得したURLを基にStorageから画像を取得する
    func loadImage() {
        //StorageのURLを参照
        let storageref = Storage.storage().reference(forURL: "gs://sample-e206e.appspot.com").child("Item")
        //画像をセット
        imageView.sd_setImage(with: storageref)
    }

}