まとめ |
---|
・構造体に含める変数名はキャメルケースにする ・構造体の変数には独自クラスを含めない ・"構造体の型" は、"JSONEncoder()" を使って "data型” に変換する ・"data型” は、"JSON Decoder()" を使って "構造体の型" に変換する |
構造体(struct)をUserDefaultsで読み書きするとき、書き込みはできるのに読み出すと "nil" になるという事象がありました。エラーも発生せず、原因が特定できませんでした。今回は結果論(多分バグでは、、)ですが、対処方法を整理します。
値を永続化したい構造体(変数)を定義する
ここが本記事で一番重要です。AppStoreから取得する購入情報を格納するための構造体です。ぱっと見では特におかしいところは無いんです。でも、これでは読み出しができません。
struct PurchasesInfo {
var productID: String
var isUpadate: Bool
var expirationDate: Date?
var revocationDate: Date?
init(productID: String, isUpadate: Bool, expirationDate: Date?, revocationDate: Date?) {
self.productID = productID
self.isUpadate = isUpadate
self.expirationDate = expirationDate
self.revocationDate = revocationDate
}
}
こちらが正常に動作する構造体の定義です。どこが違うかわかりますか?
struct PurchasesInfo {
var productId: String
var isUpadate: Bool
var expirationDate: Date?
var revocationDate: Date?
init(productId: String, isUpadate: Bool, expirationDate: Date?, revocationDate: Date?) {
self.productId = productId
self.isUpadate = isUpadate
self.expirationDate = expirationDate
self.revocationDate = revocationDate
}
}
"ID"の部分が違います。
正:var productId: String
誤:var productID: String
ちなみに、変数名を変えてみた結果は次のとおり。
誤:var ID: String
誤:var Id: String
正:var productAe: String
正:var productAeC: String
誤:var productAeCC: String
そろそろお気づきだと思いますが、"ID"という文字列がキャメルケースになっていないからです。じゃあ、書き込みもエラーにしてくれればいいのに、、
構造体をdata型にエンコードしUserdefaultsに保存する
UserDefaultsは独自で定義した型の読み書きには対応していませんが、独自で定義した型を含まない構造体であれば、data型に変換することで扱うことができます(UserDefaults.standard.setで定義されていない型(Date型など)を扱う場合も同様です)。
Data型へのエンコードと、userdefaultへの書き込み処理をまとめると次のようになります。
func userdefaultsStandardSet(forKey: String, purchasesInfos: [PurchasesInfo]) -> Bool {
print("get-- userdefaultsStandardSet")
// `JSONEncoder` で `Data` 型へエンコードし、UserDefaultsに追加する
let jsonEncoder = JSONEncoder()
jsonEncoder.keyEncodingStrategy = .convertToSnakeCase
guard let data = try? jsonEncoder.encode(purchasesInfos) else {
return false
}
UserDefaults.standard.set(data, forKey: forKey)
return true
}
UserDefaultsから読み出したdata型を構造体に変換する
次は、読み出し処理です。書き込み時と逆の処理になるため、data型で読み出された値を、構造体に変換します。
func userdefaultsStandardPurchasesInfo(forKey: String) -> [PurchasesInfo]? {
print("get-- userdefaultsStandardPurchasesInfo")
// `JSONDecoder` で `Data` 型を自作した構造体へデコードする
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
guard let data = UserDefaults.standard.data(forKey: forKey),
let date = try? jsonDecoder.decode([PurchasesInfo].self, from: data) else {
print("return nil")
return nil
}
print("return date.count:\(date.count)")
return date
}
変数定義にUserDafaultsへの読み書きを定義する
変数定義にUserdDefaultsへの読み書きをクロージャーで定義します。こうすることで、変数の値が変化するたびにUserDefaultsに書き込まれます。
static var purchasedInfos: [PurchasesInfo] {
get {
print("get-- purchasedInfos")
if let result = userdefaultsStandardPurchasesInfo(forKey: "purchasedInfos") {
return result
} else {
print("return nil")
return []
}
}
set {
print("set-- purchasedInfos:\(newValue)")
let result = userdefaultsStandardSet(forKey: "purchasedInfos", purchasesInfos: newValue)
print("set result:\(result)")
}
}
まとめ
まとめ |
---|
・構造体に含める変数名はキャメルケースにする ・構造体の変数には独自クラスを含めない ・"構造体の型" は、"JSONEncoder()" を使って "data型” に変換する ・"data型” は、"JSON Decoder()" を使って "構造体の型" に変換する |