2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

カスタムクラスのリストを、UserDefaultsに保存する。

Posted at

はじめに

以下のようなカスタムクラスのリストをUserdefaultsに保存しようとすると、ビルドは通りますがエラーが出てしまいます。

import Foundation

class Task: Codable {
    var body: String
    var isCompleted: Bool
    
    init(body: String, isCompleted: Bool) {
        self.body = body
        self.isCompleted = isCompleted
    }

}
// インスタンス化して保存
let task = Task(body: "こんにちは", isCompleted: false)
UserDefaults.standard.setValue(task, forKey: "hoge")

// エラー文
// A default object must be a property list—that is, an instance of (or for collections, 
// a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
// If you want to stfore any other type of object, you should typically archive it to create an instance of NSData.

これはなぜかというと、UserDefaultsに保存できるのは、エラー分の通りNSData, NSString, NSNumber, NSDate, NSArray, NSDictionaryとなっているからで、カスタムクラスはこれに準拠していないからです。

解決策として、カスタムクラス型をNSData型に変換して保存し、読み出すときはNSData型が読み出されるので、それを再度カスタムクラス型に変換する、ということを考えます。実装したコードは次のとおりです。

実装

まず、先程のカスタムクラスにCodableに準拠させます。名前の通り「NSData型に変換できるよ!」みたいなイメージです。

class Task: Codable {

そして、次のファイルを作成します。

JsonEncoder.swift
class JsonEncoder {
    
    class func saveItemsToUserDefaults<T: Codable>(list: [T], key: String) {
        let data = list.map { try! JSONEncoder().encode($0) }
        UserDefaults.standard.set(data as [Any], forKey: key)
        UserDefaults.standard.synchronize()
    }
    
    class func readItemsFromUserUserDefault<T: Codable>(key: String) -> [T] {
        guard let items = UserDefaults.standard.array(forKey: key) as? [Data] else { return [T]() }
        let decodedItems = items.map { try! JSONDecoder().decode(T.self, from: $0) }
        return decodedItems
    }
    
}

そして次のように呼び出すことで、カスタムクラスのリストをUserdefaultsに保存することが出来ます。

// 保存処理
JsonEncoder.saveItemsToUserDefaults(list: taskList, key: "hoge")

// 読み出し処理
let taskList: [Task] = JsonEncoder.readItemsFromUserUserDefault(key: "hoge")

解説

saveItemsToUserDefaultsはカスタムクラスのリストを受け取り、JSONに変換して保存していて、eadItemsFromUserUserDefaultはその逆の処理をしているのがわかると思います。


またT: CodableというふうにTが型みたいになっているのは、これはジェネリクスという概念で、引数の型を複数指定することを可能にします。今回では、「CodableならなんでもOKだよ!」というふうなイメージです。ジェネリクスで共通化をしたので、どのようなカスタムクラスでも保存ができるようになっています。
さらに引数の型にはCodableに準拠したカスタムクラスはもちろん、CodableはEncodableプロトコルとDecodableプロトコルを合わせて作られたので、これに準拠した方も引数に取ることが出来ます。


また読み出し処理において、定義する変数の型を指定してあげないと、ジェネリクス側が型推論できなくなってしまい、コンパイルエラーが起きるので注意してください。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?