LoginSignup
31
21

More than 5 years have passed since last update.

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

Posted at

はじめに

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

31
21
1

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
31
21