UserDefaultsを使えば何でも保存できると思いがちですが、実は保存できるデータには制限があります。
Appleの公式ドキュメント UserDefaultsをみると以下のような記述があります。
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
, orNSDictionary
. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
つまり、プロパティリストとして保存できるデータ型(NSData, NSString, NSNumber, NSData, NSArray, NSDictionary) のインスタンスでなければならないわけです。
普通の数値型や文字列型などを保存する場合は問題ないですが、UI部品などを含めたカスタムクラスやそのプロパティなどを保存する場合には制限がかかるわけです。
ではどうすればUserDefaultsに保存できるのかというと、NSObjectを継承しNSCodingに準拠したクラスを作り、そのプロパティに保存したいクラスを入れた後、NSData型のインスタンスとしてアーカイブを作成してからUserDefaultsに保存する、という流れになります。
UserDefaultsからの取り出しはその逆で、NSData型として取り出した後、任意のクラスとしてデコードする流れになるわけです。
以下のサンプルは動作を簡単に確かめるため、Playgroundでも動かせるようになっていますが、実際使うときには状況に応じた修正が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import UIKit class SomeClass: NSObject, NSCoding { // 保存したい変数 let color: UIColor init(color: UIColor) { self.color = color } func encode(with aCoder: NSCoder) { aCoder.encode(self.color, forKey: "colorkey") } required init?(coder aDecoder: NSCoder) { self.color = aDecoder.decodeObject(forKey: "colorkey") as! UIColor } } let saveColor = SomeClass(color: UIColor.red) print("save color: ", saveColor.color) // 保存 let data = try! NSKeyedArchiver.archivedData(withRootObject: saveColor, requiringSecureCoding: false) UserDefaults.standard.set(data, forKey: "saveColor") UserDefaults.standard.synchronize() // 取得 if let data = UserDefaults.standard.object(forKey: "saveColor") as? Data { if let restoredColor = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? SomeClass { print("loaded color :", restoredColor.color) } } |
上記の例では UIColor を保存し取り出す例です。アーカイブ関連の処理はtry!で処理していますが、実際はエラーをキャッチするか、処理に失敗してもnilになるようtry?で処理するのがいいと思います。
同じようなサンプルはネット上にもたくさんありますが、アーカイブ関連の処理がiOS12以降では異なってくるので、それに合わせた修正を行っています。