LoginSignup
1
2

UserDefaultsについて復習

Posted at

UserDefaultとは?

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

環境

Swift 5.10
Xcode 15.3

iOS13からの仕様変更

4194304 bytes 以上のデータを保存させることはできません。

シンプルな使い方

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)

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

2023-11-13 14.32.13.png

値を取り出す型が違ったらどうなるの?

仮にですが、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を返します。)
2023-11-13 14.32.13.png
返す値がオプショナル型になっているものもあります。なので、取り出しに失敗したら、nilを返します。

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

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

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

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

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

ドキュメント

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2