【Swift5.0】UserDefaultsでパラメータを保存する~iOSアプリ開発入門~

# はじめに
こちらの記事は、下記の記事のUIを変更していく過程を記載しています。
[【Swift】Firebase Realtime Database を用いてチャットアプリを爆速コーディングしてみた。](https://qiita.com/tetsukick/items/638501babc692ec55858)

## この記事で分かること
- UserDefaultsを使用した値の保存方法

## スタート時のソースコード
```Swift:ViewController.swift
import UIKit
import FirebaseDatabase

class ViewController: UIViewController {
    
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var nameInputView: UITextField!
    @IBOutlet weak var messageInputView: UITextField!
    @IBOutlet weak var inputViewBottomMargin: NSLayoutConstraint!
    
    var databaseRef: DatabaseReference!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        readData()
        
        addKeyboardShowHideObserver()
        
    }
    
    private func readData() {
        databaseRef = Database.database().reference()
        
        databaseRef.observe(.childAdded, with: { snapshot in
            dump(snapshot)
            if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"] {
                let currentText = self.textView.text
                self.textView.text = (currentText ?? "") + "\n\(name) : \(message)"
            }
        })
    }
    
    private func addKeyboardShowHideObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
    }

    @IBAction func tappedSendButton(_ sender: Any) {
        view.endEditing(true)
        
        if let name = nameInputView.text, let message = messageInputView.text {
            let messageData = ["name": name, "message": message]
            databaseRef.childByAutoId().setValue(messageData)
            
            messageInputView.text = ""
        }
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        if let userInfo = notification.userInfo, let keyboardFrameInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            inputViewBottomMargin.constant = keyboardFrameInfo.cgRectValue.height
        }
        
    }
    
    @objc func keyboardWillHide(_ notification: NSNotification){
        inputViewBottomMargin.constant = 0
    }
    
}
```

# 1. ViewControllerにTextFieldのイベントを検知する権限を移譲する

`ViewController.swift`の末尾に以下を追加。

```Swift:ViewController.swift
extension ViewController: UITextFieldDelegate {
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        return true
    }
}
```

`ViewDidLoad()`内に以下を記載

```Swift:ViewController.swift
nameInputView.delegate = self
```

```Swift:ViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()
        
        readData()
        
        addKeyboardShowHideObserver()
        
        nameInputView.delegate = self
        
    }
```

これで、UITextFieldが持つイベントをViewControllerで拾うことができるようになりました。

ちなみに、`textFieldShouldReturn `メソッドはUITextFieldの入力でEnterが押された事を検知するイベントです。

# 2. UserDefaultsのインスタンスを生成

```Swift:ViewController.swift
let userDefaults = UserDefaults.standard
```

```Swift:ViewController.swift
class ViewController: UIViewController {
    
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var nameInputView: UITextField!
    @IBOutlet weak var messageInputView: UITextField!
    @IBOutlet weak var inputViewBottomMargin: NSLayoutConstraint!
    
    var databaseRef: DatabaseReference!
    
    let userDefaults = UserDefaults.standard

    override func viewDidLoad() {
        super.viewDidLoad()
        
        readData()
```

# 3. Enterが押されたタイミングでUserDefaultsに値を保存
```Swift:ViewController.swift
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        guard let inputText = textField.text else {
            return true
        }
        userDefaults.set(inputText, forKey: "name")
        userDefaults.synchronize()
        
        return true
    }
```

# 4. UserDefaultsから値を読み込む
```Swift:ViewController.swift
    fileprivate func readNameData() -> String {
        return userDefaults.object(forKey: "name") as? String ?? ""
    }
```

# 5. 読み込んだ値をUITextFieldに表示
```Swift:ViewController.swift
nameInputView.text = readNameData()
```

これで完了！
# 6. 完成形のソースがこちら

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

class ViewController: UIViewController {
    
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var nameInputView: UITextField!
    @IBOutlet weak var messageInputView: UITextField!
    @IBOutlet weak var inputViewBottomMargin: NSLayoutConstraint!
    
    var databaseRef: DatabaseReference!
    
    let userDefaults = UserDefaults.standard

    override func viewDidLoad() {
        super.viewDidLoad()
        
        readData()
        
        addKeyboardShowHideObserver()
        
        nameInputView.delegate = self
        nameInputView.text = readNameData()
        
    }
    
    private func readData() {
        databaseRef = Database.database().reference()
        
        databaseRef.observe(.childAdded, with: { snapshot in
            dump(snapshot)
            if let obj = snapshot.value as? [String : AnyObject], let name = obj["name"] as? String, let message = obj["message"] {
                let currentText = self.textView.text
                self.textView.text = (currentText ?? "") + "\n\(name) : \(message)"
            }
        })
    }
    
    private func addKeyboardShowHideObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
    }

    @IBAction func tappedSendButton(_ sender: Any) {
        view.endEditing(true)
        
        if let name = nameInputView.text, let message = messageInputView.text {
            let messageData = ["name": name, "message": message]
            databaseRef.childByAutoId().setValue(messageData)
            
            messageInputView.text = ""
        }
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        if let userInfo = notification.userInfo, let keyboardFrameInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            inputViewBottomMargin.constant = keyboardFrameInfo.cgRectValue.height
        }
        
    }
    
    @objc func keyboardWillHide(_ notification: NSNotification){
        inputViewBottomMargin.constant = 0
    }
    
    fileprivate func readNameData() -> String {
        return userDefaults.object(forKey: "name") as? String ?? ""
    }
    
}

extension ViewController: UITextFieldDelegate {
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        guard let inputText = textField.text else {
            return true
        }
        userDefaults.set(inputText, forKey: "name")
        userDefaults.synchronize()
        
        return true
    }
}
```

#7. リファクタリング
- Keyの定数化
- 保存処理をメソッド化
- 保存処理をボタン押下時にも追加

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

class ViewController: UIViewController {
    
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var nameInputView: UITextField!
    @IBOutlet weak var messageInputView: UITextField!
    @IBOutlet weak var inputViewBottomMargin: NSLayoutConstraint!
    
    var databaseRef: DatabaseReference!
    
    let userDefaults = UserDefaults.standard
    fileprivate let nameKey = "name"
    fileprivate let messageKey = "message"

    override func viewDidLoad() {
        super.viewDidLoad()
        
        readData()
        
        addKeyboardShowHideObserver()
        
        nameInputView.delegate = self
        nameInputView.text = readNameData()
        
    }
    
    private func readData() {
        databaseRef = Database.database().reference()
        
        databaseRef.observe(.childAdded, with: { snapshot in
            dump(snapshot)
            if let obj = snapshot.value as? [String : AnyObject], let name = obj[self.nameKey] as? String, let message = obj[self.messageKey] {
                let currentText = self.textView.text
                self.textView.text = (currentText ?? "") + "\n\(name) : \(message)"
            }
        })
    }
    
    private func addKeyboardShowHideObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIResponder.keyboardDidHideNotification, object: nil)
    }

    @IBAction func tappedSendButton(_ sender: Any) {
        view.endEditing(true)
        
        if let name = nameInputView.text, let message = messageInputView.text {
            save(name: name)
            
            let messageData = [nameKey: name, messageKey: message]
            databaseRef.childByAutoId().setValue(messageData)
            
            messageInputView.text = ""
        }
    }
    
    @objc func keyboardWillShow(_ notification: NSNotification){
        if let userInfo = notification.userInfo, let keyboardFrameInfo = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            inputViewBottomMargin.constant = keyboardFrameInfo.cgRectValue.height
        }
        
    }
    
    @objc func keyboardWillHide(_ notification: NSNotification){
        inputViewBottomMargin.constant = 0
    }
    
    fileprivate func readNameData() -> String {
        return userDefaults.object(forKey: nameKey) as? String ?? ""
    }
    
    fileprivate func save(name: String) {
        userDefaults.set(name, forKey: nameKey)
        userDefaults.synchronize()
    }
    
}

extension ViewController: UITextFieldDelegate {
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        guard let inputText = textField.text else {
            return true
        }
        save(name: inputText)
        
        return true
    }
}

```

# GitHubはこちら
https://github.com/Tetsukick/Firebase-chat-iOS