Twitter風のログイン機能を実装してみた

***ログイン前***
<img width="160" alt="スクリーンショット 2019-07-04 15.22.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/32ee176c-9aff-8b3b-d698-10286a151d56.png">　⇨　<img width="160" alt="スクリーンショット 2019-07-04 15.23.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/287ee7af-b83a-9647-8a17-d0f222dbbd97.png"><img width="160" alt="スクリーンショット 2019-07-04 15.23.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/a3656dd0-266a-237a-84f4-f3a9f8ccceb8.png"><img width="160" alt="スクリーンショット 2019-07-04 15.23.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/d57eaaed-0c02-a2fd-c941-7c5020b4360c.png">
　　　⇩
<img width="160" alt="スクリーンショット 2019-07-04 22.53.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/d66f4530-2689-6d44-79db-381eca5fae6d.png">

***ログイン後***
⇨<img width="160" alt="スクリーンショット 2019-07-04 22.56.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/169074dd-abef-fac5-17b7-d6dd848dde99.png">　⇨　<img width="160" alt="スクリーンショット 2019-07-04 23.20.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/07d370b4-68b9-80f1-edb1-dbcdacc502fb.png">

###事前準備
縦画面だけの対応にするため、GeneralのDeployment infoのLandscapeLandscapeLeftとLandscaperightのチェックを外す。
<img width="725" alt="スクリーンショット 2019-07-04 13.35.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/10791769-9adf-b237-e068-f8080588e7d3.png">

今回はNCMBを使うので、[こちらのページ](https://mbaas.nifcloud.com/doc/current/introduction/quickstart_ios.html)のCocoaPodsを利用する方法を用いてNCMBを導入する。

また、Storyboardを作成していきます。(赤:Outlet接続 黒:Action接続orclass)

***SignIn.storyboard(自分で作る)***
<img width="564" alt="スクリーンショット 2019-07-04 15.29.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/596b5ed0-0fbe-490e-3b70-7a4201eaf5b5.png">

今回はパスワードを忘れたときと、利用規約、プライバシーポリシーの処理は省かせていただきます。

***Main.storyboard***
<img width="383" alt="スクリーンショット 2019-07-04 20.38.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/696809b5-f6c6-428d-71c7-a63c25369674.png">



これから本題に入っていくのですが、
❶ユーザー登録
❷ログイン
❸ログアウト
➍ユーザー退会
の順番でやっていこうと思います。

##ユーザー登録
NCMBのページを開いて、アプリを作成します。
<img width="1024" alt="スクリーンショット 2019-07-04 16.12.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/a8f2cd69-b551-f0b0-83ff-e8b1a627a480.png">
アプリケーションキーとクライアントキーはのちに使うのでコピーしておいて下さい。
<img width="680" alt="スクリーンショット 2019-07-04 16.13.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/5da3e7b9-04dc-c47d-dde9-16246dcaa50b.png">
AppDelegate.swiftに以下の記述を書いてください。
これでNCMBと接続が行えます。

```
import UIKit
import NCMB

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        //連携完了
        NCMB.setApplicationKey("あなたのアプリケーションキー", clientKey: "あなたのクライアントキー")
        return true
    }

    ~~~省略~~~

```
また、ログインしているか否かで最初の画面表示(Storyboard)が違うのでそのためのコードを書かなければなりません。

```
AppDelegate.swift

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
       ~~~省略~~~
        let ud = UserDefaults.standard
        let isLogin = ud.bool(forKey: "isLogin")
        
        if isLogin == true {
            // ログイン中だったら
            //画面の大きさ
            self.window = UIWindow(frame: UIScreen.main.bounds)
            let storyboard = UIStoryboard(name: "Main"
                , bundle: Bundle.main)
            //起動時の画面を設定
            let rootViewController = storyboard.instantiateViewController(withIdentifier: "MainController")
            self.window?.rootViewController = rootViewController
            self.window?.backgroundColor = UIColor.white
            //その画面を表示
            self.window?.makeKeyAndVisible()
        } else {
            // ログインしていなかったら
            self.window = UIWindow(frame: UIScreen.main.bounds)
            let storyboard = UIStoryboard(name: "SignIn", bundle: Bundle.main)
            let rootViewController = storyboard.instantiateViewController(withIdentifier: "SignInController")
            self.window?.rootViewController = rootViewController
            self.window?.backgroundColor = UIColor.white
            self.window?.makeKeyAndVisible()
        }
        return true
    }

```
これで起動時の画面が決定しました。

<img width="334" alt="スクリーンショット 2019-07-04 21.29.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/8922097a-6a54-696b-2a30-95e85657b10a.png">
このような丸みを帯びたボタンにするために以下のコードを書いていきます。

```
import UIKit

class TopViewController: UIViewController {
    @IBOutlet weak var signUp: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //角丸サイズ
        signUp.layer.cornerRadius = 10.0
    }  
}
```
次にアカウント作成ページを作っていきましょう。
<img width="336" alt="スクリーンショット 2019-07-04 21.36.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/67345dea-30f6-2b9b-2bc1-465a948e47a0.png">

```
import UIKit
import NCMB

class SignUpViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet var userIdTextField: UITextField!
    @IBOutlet var emailTextField: UITextField!
    @IBOutlet var passwordTextField: UITextField!
    @IBOutlet var confirmTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        userIdTextField.delegate = self
        emailTextField.delegate = self
        passwordTextField.delegate = self
        confirmTextField.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    @IBAction func signUp() {
        let user = NCMBUser()
        
        if (userIdTextField.text?.characters.count)! < 4 {
            print("文字数が足りません")
            return
        }
        
        user.userName = userIdTextField.text!
        user.mailAddress = emailTextField.text!
        
        if passwordTextField.text == confirmTextField.text {
            user.password = passwordTextField.text!
        } else {
            print("パスワードの不一致")
        }
        
        user.signUpInBackground { (error) in
            if error != nil {
                // エラーがあった場合
                print(error)
            } else {
                // 登録成功
                let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
                let rootViewController = storyboard.instantiateViewController(withIdentifier: "MainController")<img width="338" alt="スクリーンショット 2019-07-04 22.35.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/90f22961-fbfc-55fe-9181-67a7c1a8d19b.png">
<img width="338" alt="スクリーンショット 2019-07-04 22.35.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/52410859-4046-e5f8-7f2c-1f18f8d501fe.png">

                UIApplication.shared.keyWindow?.rootViewController = rootViewController

                 // ログイン状態の保持
                 let ud = UserDefaults.standard
                 ud.set(true, forKey: "isLogin")
                 ud.synchronize()
                        
                    }
                })
            }
        }
        
    }
```
以上で簡易ではありますが、ユーザー登録の処理は終わりました。

##ログイン
<img width="340" alt="スクリーンショット 2019-07-04 22.10.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/e4c23fb4-d412-6312-feee-12ea14f8801a.png">

```
import UIKit
import NCMB

class SignInViewController: UIViewController, UITextFieldDelegate {
    
    @IBOutlet var userIdTextField: UITextField!
    @IBOutlet var passwordTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        userIdTextField.delegate = self
        passwordTextField.delegate = self
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
    
    @IBAction func signIn() {
        
        if (userIdTextField.text?.characters.count)! > 0 && (passwordTextField.text?.characters.count)! > 0 {
            
            NCMBUser.logInWithUsername(inBackground: userIdTextField.text!, password: passwordTextField.text!) { (user, error) in
                if error != nil {
                    print(error)
                } else {
                    // ログイン成功
                    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
                    let rootViewController = storyboard.instantiateViewController(withIdentifier: "MainController")
                    UIApplication.shared.keyWindow?.rootViewController = rootViewController
                        
                    // ログイン状態の保持
                    let ud = UserDefaults.standard
                    ud.set(true, forKey: "isLogin")
                    ud.synchronize()
                    }
                }
            }
        }
    }
    
```

##ログアウト
<img width="338" alt="スクリーンショット 2019-07-04 22.35.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/581c9b2f-9d3f-5dc5-af3d-7342fc1ed252.png"><img width="335" alt="スクリーンショット 2019-07-04 22.46.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/b016d4ba-5716-0e6b-83b2-f30dbf1d47d7.png">

```
import UIKit
import NCMB

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    
    @IBAction func logout(_ sender: Any) {
        let alertController = UIAlertController(title: "メニュー", message: "メニューを選択して下さい。", preferredStyle: .actionSheet)
        
        let signOutAction = UIAlertAction(title: "ログアウト", style: .default) { (action) in
            NCMBUser.logOutInBackground({ (error) in
                if error != nil {
                    print(error)
                } else {
                    // ログアウト成功
                    let storyboard = UIStoryboard(name: "SignIn", bundle: Bundle.main)
                    let rootViewController = storyboard.instantiateViewController(withIdentifier: "SignInController")
                    UIApplication.shared.keyWindow?.rootViewController = rootViewController
                    
                    // ログイン状態の保持
                    let ud = UserDefaults.standard
                    ud.set(false, forKey: "isLogin")
                    ud.synchronize()
                }
            })
        }
        let cancelAction = UIAlertAction(title: "キャンセル", style: .cancel) { (action) in
            alertController.dismiss(animated: true, completion: nil)
        }
        
        alertController.addAction(signOutAction)
        alertController.addAction(cancelAction)
        
        self.present(alertController, animated: true, completion: nil)
    }
}
```
ここで少し小技。
<img width="297" alt="スクリーンショット 2019-07-04 22.33.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/cdcb626f-62c4-60a8-e213-3358e4020759.png">
このようにパスワードを打つとき隠したい場合、
<img width="267" alt="スクリーンショット 2019-07-04 23.01.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/399a36fa-fd9f-fca9-347c-1420650cf094.png">
このようにSecure Text Entryにチェックを入れる。

##ユーザー退会
ユーザーの退会は重い処理なので、メールアドレスを入力させたりするのですが今回は簡単にやってみたいと思います。
<img width="160" alt="スクリーンショット 2019-07-04 22.56.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/f85376d4-f35f-5173-be40-ec185d8642f5.png">　⇨　<img width="160" alt="スクリーンショット 2019-07-04 23.20.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325764/9dc93e11-1518-887f-644a-6e51bc547bbd.png">　
ViewControllerに

```

@IBAction func logout(_ sender: Any) {
       ~~~省略~~~
        
        let deleteAction = UIAlertAction(title: "退会", style: .default) { (action) in
            let user = NCMBUser.current()
            
            user?.deleteInBackground({ (error) in
                if error != nil{
                    print(error)
                }else{
                    // ログアウト成功
                    let storyboard = UIStoryboard(name: "SignIn", bundle: Bundle.main)
                    let rootViewController = storyboard.instantiateViewController(withIdentifier: "SignInController")
                    UIApplication.shared.keyWindow?.rootViewController = rootViewController
                    
                    // ログイン状態の保持
                    let ud = UserDefaults.standard
                    ud.set(false, forKey: "isLogin")
                    ud.synchronize()
                }
            })
            
        }
        
      ~~~省略~~~
        alertController.addAction(deleteAction)
        
        self.present(alertController, animated: true, completion: nil)
    }
    
```
これで簡易ではありますがログインからユーザー退会までの機能がすべて終わりました。
ぜひみなさん開発するときの参考にしてみてください。

サンプルは[こちら](https://github.com/matushinn/SignIn)です。

[ユーザーページを作ってみたい方はこちら](https://qiita.com/SHOBLOG/items/17c1ca570d6682b7597d)