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)
今回は上記のメソッドを使用してデータを取り出しました。
他にも下のようにいろんなデータの取り出し方があります。
値を取り出す型が違ったらどうなるの?
仮にですが、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
を返します。)
返す値がオプショナル型になっているものもあります。なので、取り出しに失敗したら、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では以下のようなトラブルが起きません。ただ、このあたりはもう少し詰めて理解したい点です。
データ競合
:
複数のスレッドが同時に同じデータにアクセスし、互いの操作が競合することで、最終的なデータの状態が非決定的(不定)になる現象。
デッドロック
:
複数のスレッドがお互いのリソースの解放を待ち続けることで、どのスレッドも進行できなくなる状態。
リソースリーク
:
スレッド間でのリソース共有が適切に管理されていない場合、リソースが不適切に解放されないことでメモリリークなどが発生すること。
ドキュメント