サンプルアプリでCoreDataを使ったデータの永続化は試してみましたが、今回はUserDefaultの話です。
UserDefaultとは
デフォルトの情報、つまりアプリの設定情報やユーザーの設定など、一度セットして毎回使うような情報を保存したい時に使います。
(データベースが作られるわけじゃないので、どんどん増えていくデータなどの保存には向きません。)
UserDefaultsのデータはアプリのローカル保存領域に保存され、アプリ起動時に呼び出されるそうな。
保存方法はUserDefaultsで設定する方法と、@AppStrageで設定する方法があるみたいなので、今回は同じ挙動になるように両方のコードを試してみたいと思います。
ちなみに配列、辞書、Any型など一部のデータ型は@AppStrageでは扱えません。
開発環境 | バージョン |
---|---|
Xcode | 12.5.1 |
iOS | 14.0以降 |
macOS | BigSur 11.5.2 |
MVVM風にUserDefaultsを使ってみる
入力したデータがViewに即時反映できるように、MVVM風にUserDefaultを使ってみようと思います。
設定したい項目が多い時にはデータとViewを分けた方が使いやすいですよね。
入力後も何度でも自由に編集できて、かつアプリを再起動してもデータが消えません。
今回UserDefaultsに保存するのは、String型の身長、体重、年齢の入力データです。
Textfieldに入力されたものをUserDefaultsに保存し、アプリ再起動でもデータが消えないようにします。
ContentView.swift
//
// ContentView.swift
// UserdefaultsSample
//
// Created by Yaguchi Sato on 2021/12/09.
//
import SwiftUI
struct ContentView: View {
@StateObject private var profile = ProfileData()
var body: some View {
VStack(alignment: .center){
//UserDefaultsに入力保存
TextField("身長", text: $profile.bodyHeight)
TextField("体重", text: $profile.bodyWeight)
TextField("年齢", text: $profile.Age)
//UserDefaultsのデータを表示
Text("身長:\(profile.bodyHeight) cm")
Text("体重:\(profile.bodyWeight) kg")
Text("年齢:\(profile.Age) 歳")
}.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ProfileData.swift UserDefaultのデータモデル
UserDefaultは、値とキー(String型)をセットで保存して、キーで値を呼び出す仕組みです。
データモデルで、保存するデータの型とキー、初期値をセットします。
//
// ProfileData.swift
// UserdefaultsSample
//
// Created by Yaguchi Sato on 2021/12/09.
//
import SwiftUI
//クラスをObservableObjectとして定義
class ProfileData :ObservableObject{
//UserDefaults.standard.set(変数名, forkey: "Stringのキー")
@Published var bodyHeight: String {
didSet {UserDefaults.standard.set(bodyHeight, forKey: "bodyHeight")}}
@Published var bodyWeight: String {
didSet {UserDefaults.standard.set(bodyWeight, forKey: "bodyWeight")}}
@Published var Age: String {
didSet {UserDefaults.standard.set(Age, forKey: "Age")}}
//初期化する
init(){
bodyHeight = UserDefaults.standard.string(forKey: "bodyHeight") ?? ""
bodyWeight = UserDefaults.standard.string(forKey: "bodyWeight") ?? ""
Age = UserDefaults.standard.string(forKey: "Age") ?? ""
}
}
「init()」の「??」の後にデフォルトの値をセットすることができます。(サンプルは空欄)
UserDefaultには、data/bool/integer/float/double/URL/配列/辞書など、String以外のデータ型も保存できますよ。
というか今回のやつIntでもよかったなってちょっと思った。
Stringなら漢数字も入れられるしまぁこれはこれでいっか……
AppStrageでUserDefaultsに保存
次はiOS14から使える@AppStrageというプロパティラッパーを試してみます。
@Stateのようにデータを監視して変更を反映しつつ、UserDefaultsの値を扱えるということなんですが、どうかな。
アプリの挙動は全く同じで、Textfieldに入力された値をUserDefaultsに保存して表示します。
//
// ContentView.swift
// UserdefaultsSample
//
// Created by Yaguchi Sato on 2021/12/09.
//
import SwiftUI
struct ContentView: View {
//@AppStorage(wrappedValue:"初期値","キー") private var 変数名:データ型
@AppStorage(wrappedValue: "", "bodyHeight") private var bodyHeight: String
@AppStorage(wrappedValue: "", "bodyWeight") private var bodyWeight: String
@AppStorage(wrappedValue: "", "Age") private var Age: String
var body: some View {
VStack(alignment: .center){
TextField("身長", text: $bodyHeight)
TextField("体重", text: $bodyWeight)
TextField("年齢", text: $Age)
Text("身長:\(bodyHeight) cm")
Text("体重:\(bodyWeight) kg")
Text("年齢:\(Age) 歳")
}.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
データモデルファイルは無しで、スッキリしたコードで動作と見た目は全く同じです。
AppStrageは、ひとつのView内だけで使うなら、データ入力時に即時反映でViewが更新されるんですが、別ファイル間で値を渡そうとした場合、再起動しないとViewは更新されませんでした。
MVVMでもっと複雑なデータ構造のアプリを作るなら、ファイル間でデータの変更を感知できるようにコードを組んでUserDefaultsを使った方がいいかも。
単純に一画面で済むような時はAppStrageはとっても使いやすいと思います。
以上!アプリの設定などに使えるUserDefaultsの話でした。