UserDefaultsについて復習

# UserDefaultとは？
（公式より）ユーザーのデフォルト データベースへのインターフェイス。アプリの起動後もキーと値のペアを永続的に保存できる機能をもつ。
端末上にデータを保存させるための機能を提供します。ただし、端末の保存になるため、アプリを削除してしまうとデータも同時に消えてしまいます。

## 環境
Swift 5.10
Xcode 15.3

## iOS13からの仕様変更
4194304 bytes　以上のデータを保存させることはできません。


## シンプルな使い方
```swift
func save(value: String) {
    UserDefaults.standard.set(value, forKey: "key")//端末に保存する
}

save(value: "sako") 

func load(){
    let value = UserDefaults.standard.string(forKey: "key")//端末のデータを取得する
    guard let value else { return print("nil") }
    print("##", value)//"sako"
}
load() 
```
### .set(< value: Any? >, forKey: < String >)
上記のメソッドを使ってデータを端末に保存します。

### .string(forKey: String)
今回は上記のメソッドを使用してデータを取り出しました。
他にも下のようにいろんなデータの取り出し方があります。

<img width="400" alt="2023-11-13 14.32.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3232025/fa25dbd0-a877-c476-8171-15e124cf0d6d.png">

### 値を取り出す型が違ったらどうなるの？
仮にですが、swiftではメソッド名と仮引数の名前が同じでも、仮引数の型が違えば、そのメソッドも作ることができます（アドホック多相）。下例
```swift
func save(value: String) {
    UserDefaults.standard.set(value, forKey: "key")
}

func save(value: Bool) {
    UserDefaults.standard.set(value, forKey: "key")
}

save(value: "sako")

func load(){
    let value = UserDefaults.standard.bool(forKey: "key")
    print("##", value)
}
load()
```
この場合、load()を使用した結果は`false`を返します。`key`を見て値を取り出そうとしますが、取り出そうとした値がBool型ではなくString型が入ってきてしまっているため、UserDefaultsのデフォルトの振る舞いとして`false`を返してしまいます。(.integerでは`0`を返します。）
<img width="400" alt="2023-11-13 14.32.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3232025/dfb01604-4d13-6d91-c0fc-3e39af872517.png">
返す値がオプショナル型になっているものもあります。なので、取り出しに失敗したら、`nil`を返します。

## 独自の型で保存させたい時は？

独自の型を使って保存させたい時は、一旦、JSON形式に置き換える作業が発生するため`JSONEncoder`でエンコードさせ、取り出したい時には`JSONDecoder`を使ってデコードして値を取り出してあげます。

``` swift
enum DataConversionError: Error{
    case encodeError
    case decodeError
    case dataNotFoundError
}

struct Person: Codable{
    let name: String
    let age: Int
}

func save(value: Person) throws {
    do {
        let jsonData = try JSONEncoder().encode(value)
        UserDefaults.standard.set(jsonData, forKey: "key")
    } catch {
        throw DataConversionError.encodeError
    }
}

let sako = Person(name: "sako", age: 31)

do {
    try save(value: sako)
} catch DataConversionError.encodeError {
    print("Error: Failed to encode the person data.")
} catch {
    print("An unexpected error occurred.")
}

var whoPerson: Person?

func load() throws -> Person{
    let loadData = UserDefaults.standard.data(forKey: "key")
    guard let loadData else { throw DataConversionError.dataNotFoundError }
    do {
        let person = try JSONDecoder().decode(Person.self, from: loadData)
        return person
    } catch { throw DataConversionError.decodeError }
}

do {
    whoPerson = try load()
    print("Loaded Person: \(whoPerson!.name), \(whoPerson!.age)")
} catch DataConversionError.dataNotFoundError {
    print("Error: No data available to load.")
} catch DataConversionError.decodeError {
    print("Error: Failed to decode the data.")
} catch {
    print("An unexpected error occurred.")
}

```


## スレッドセーフ
UserDefaultでは以下のようなトラブルが起きません。ただ、このあたりはもう少し詰めて理解したい点です。
`データ競合`：
複数のスレッドが同時に同じデータにアクセスし、互いの操作が競合することで、最終的なデータの状態が非決定的（不定）になる現象。

`デッドロック`：
複数のスレッドがお互いのリソースの解放を待ち続けることで、どのスレッドも進行できなくなる状態。

`リソースリーク`：
スレッド間でのリソース共有が適切に管理されていない場合、リソースが不適切に解放されないことでメモリリークなどが発生すること。

## ドキュメント

https://developer.apple.com/documentation/foundation/userdefaults