iOS12 unarchiveObject(with:) was deprecated に対応する


はじめに

swiftで自身で定義したクラスをUserDefaultsに保存&ロードすることがよくありますが,iOS12では


  • archivedData(withRootObject:)

  • unarchiveObject(with:)

が非推奨になりました.書き換えに手間取ったので手順を記録しておきます.


確認環境

Xcode Version 10.1

Swift 4.2.1

iOS 12.0


変更前


変更前.swift

// 保存したいクラス

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の利用がオススメです.


リファレンス

Foundation Release Notes