はじめに
swiftで自身で定義したクラスをUserDefaultsに保存&ロードすることがよくありますが,iOS12では
- archivedData(withRootObject:)
- unarchiveObject(with:)
が非推奨になりました.書き換えに手間取ったので手順を記録しておきます.
確認環境
Xcode Version 10.1
Swift 4.2.1
iOS 12.0
変更前
// 保存したいクラス
class MyClass: NSObject, NSCoding{
var dataValue:String
init(value: String){
self.dataValue = value
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataValue, forKey: "dataKey")
}
required init?(coder aDecoder: NSCoder) {
self.dataValue = (aDecoder.decodeObject(forKey: "dataKey") as! String)
}
}
// 保存処理
func saveData(_ value : MyClass){
let archiveData = NSKeyedArchiver.archivedData(withRootObject: value)
UserDefaults.standard.set(archiveData, forKey: "key")
}
// ロード処理
func loadData() -> MyClass?{
if let loadedData = UserDefaults().data(forKey: "key") {
return NSKeyedUnarchiver.unarchiveObject(with: loadedData) as! MyClass
}
return nil
}
// データを作成してUserDefaultsに保存
let myClass = MyClass(value: "保存データ")
saveData(myClass)
// UserDefaultsからデータを取得
let loadClass = loadData()
print(loadClass!.dataValue) // "保存データ"
この状態だとiOS12では以下の警告が表示されます.
- 'archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead
- 'unarchiveObject(with:)' was deprecated in iOS 12.0: Use +unarchivedObjectOfClass:fromData:error: instead
変更後
上記のコードを警告が出ないように書き換えると以下のようになります.
※ 変更点はコメントに記載しています.
// 保存したいクラス
class MyClass: NSObject, NSSecureCoding{ // ⭐️NSCoding→NSSecureCodingに変更
static var supportsSecureCoding: Bool = true // ⭐️NSSecureCodingのprotocolに定義されているため追加
var dataValue:String
init(value: String){
self.dataValue = value
}
func encode(with aCoder: NSCoder) {
aCoder.encode(dataValue, forKey: "dataKey")
}
required init?(coder aDecoder: NSCoder) {
self.dataValue = (aDecoder.decodeObject(forKey: "dataKey") as! String)
}
}
// 保存処理
func saveData(_ value : MyClass){
// ⭐️警告で使えと言われたメソッドに変更 例外を投げるのでtry?を追加
guard let archiveData = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true) else {
fatalError("Archive failed")
}
UserDefaults.standard.set(archiveData, forKey: "key")
}
// ロード処理
func loadData() -> MyClass?{
if let loadedData = UserDefaults().data(forKey: "key") {
// ⭐️unarchiveTopLevelObjectWithDataに変更 例外を投げるのでtry?を追加
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(loadedData) as! MyClass
// ⭐️警告で使えと言われたメソッドの場合
// return try! NSKeyedUnarchiver.unarchivedObject(ofClass: MyClass.self, from:loadedData)
// ⭐️MyClass内でDateや配列を使っている場合はofClassではなく,ofClassesを利用する
// return try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [MyClass.self, NSArray.self], from: loadedData) as! MyClass
}
return nil
}
// データを作成してUserDefaultsに保存
let myClass = MyClass(value: "保存データ")
saveData(myClass)
// UserDefaultsからデータを取得
let loadClass = loadData()
print(loadClass!.dataValue) // "保存データ"
補足
unarchiveObjectの警告で使えと言われている
NSKeyedUnarchiver.unarchivedObject(ofClasses: [MyClass.self], from: loadedData)
を利用しても良いのですが,MyClassの中で日付や配列などを利用していると,利用クラスを列挙するため
NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, MyClass.self, NSDate.self, NSDictionary.self], from: loadedData)
のように長くなるので,NSxxx.selfを含むunarchiveTopLevelObjectWithDataの利用がオススメです.