😬

[Swift] iOS 12 deprecated NSKeyedArchiverのメソッドを推奨されたものに置き変えた

に公開

TL, DR

  • コード内のNSKeyedArchiver, NSKeyedUnarchiverのメソッドでiOS 12 deprecatedのものを、推奨されたものに置き換えた。
    • NSKeyedArchiver
      • archiveData(withRootObject:) -> archiveData(withRootObject:, requireSecureCodign:)
    • NSKeyedUnarchiver
      • unarchiveObject(with:) -> unarchiveObject(ofClass:, from:)
      • unarchiveTopLevelObjectWithData(_) -> unarchiveObject(ofClass:, from:)
  • 以下のオブジェクトのarchive/unarchiveの方法を記載(Deprecatedな方法と推奨された方法)
    • Bool
    • Date
    • Strcut
    • Class

NSKeyedArchiver, NSKeyedUnarchiverとは

NSKeyedArchiver

  • NSCoderのサブクラスで、オブジェクトを、ファイルに保存するのに適した形式で保存できるように、エンコードするメソッドをもつクラス。

NSKeyedUnarchiver

  • NSKeyedArchiverでエンコードしたオブジェクトをデコードするメソッドをもつクラス。

Deprecatedな方法と推奨された方法

Deprecatedな方法

以下のメソッドはiOS 12でdeprecatedした。

  • NSKeyedArchiver.archiveData(withRootObject: Any)-> Data
  • NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_)-> Bool
  • NSKeyedUnarchiver.unarchiveObject(withRootObject: Data)-> Any?


推奨された方法

  • NSKeyedArchiver.archiveData(withRootObject:requiringSecureCoding:)
    • archiveData(withRootObject:)の置き換え
  • NSKeyedUnarchiver.unarchiveObject(ofClass: from:)
    • NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(_)-> Any?の置き換え
    • NSKeyedUnarchiver.unarchiveObject(withRootObject: Data)-> Any?の置き換え

具体的な変更方法

Bool

Deprecatedな方法

func encodeBool_old(bool: Bool) -> Data {
  return NSKeyedArchiver.archivedData(withRootObject: bool)
}

func decodeBool_old(data: Data) -> Bool? {
  return NSKeyedUnarchiver.unarchiveObject(with: data) as? Bool
}

推奨された方法

func encodeBool_new(bool: Bool) -> Data? {
  // 推奨のメソッドに変更
  // try? を追加
  return try? NSKeyedArchiver.archivedData(withRootObject: bool, requiringSecureCoding: false)
}

func decodeBool_new(data: Data) -> Bool? {
  // 推奨のメソッドに変更
  return try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [], from: data) as? Bool
}

Date

Deprecatedな方法

func encodeDate_old(date: Date) -> Data {
  return NSKeyedArchiver.archivedData(withRootObject: date)
}

func decodeDate_old(data: Data) -> Date? {
  return NSKeyedUnarchiver.unarchiveObject(with: data) as? Date
}

推奨された方法

func encodeDate_new(date: Date) -> Data? {
  return try? NSKeyedArchiver.archivedData(withRootObject: date, requiringSecureCoding: false)
}

func decodeDate_new(data: Data) -> Date? {
  return try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSDate.self ,from: data) as? Date
}

Class

(reference: NSKeyedArchiverとNSKeyedUnarchiverを使ったクラスの保存)

Deprecatedな方法

class MyClass: NSObject, NSCoding { 
  var num: Int
  var bool: Bool
  var date: Date

  // ~~~ 省略 ~~~
  func encode(with coder: NSCoder)
  required init?(coder: NSCoder)
}

func encodeClass_old(c: MyClass) -> Data {
  return NSKeyedArchiver.archivedData(withRootObject: c)
}

func decodeClass_old(data: Data) -> MyClass? {
  return NSKeyedUnarchiver.unarchiveObject(with: data) as? MyClass
}

推奨された方法

class MyClass: NSObject, NSSecureCoding { //<- NSSecureCodingに変更
  static var supportsSecureCoding: Bool = true //<- NSSecureCodingの要件
  
  var num: Int
  var bool: Bool
  var date: Date
  
  // ~~~ 省略 ~~~
  func encode(with coder: NSCoder)
  required init?(coder: NSCoder)
}

func encodeClass_new(c: MyClass) -> Data? {
  // 推奨のメソッドに変更
  // try? を追加
  return try? NSKeyedArchiver.archivedData(withRootObject: c, requiringSecureCoding: true)
}

func decodeClass_new(data: Data) -> MyClass? {
  // 推奨のメソッドに変更
  // try? を追加
  // `ofClasses`には、`MyClass`と`NSDate`を指定(NSDateの指定は、MyClass内でDate型を使用しているため必要)
  return try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [MyClass.self, NSDate.self], from: data) as? MyClass
}

Struct

(reference: UserDefaults で Struct を扱う)

Deprecatedな方法

struct MyStruct: Encodable, Decodable { // <- Json Encode/DecodeしてArchive/Unarchiveするため
  var num: Int = 0
  var bool: Bool = true
  var date: Date = .now
}

func encodeStruct_old(s: MyStruct) -> Data {
  guard let encodedData = try? JSONEncoder().encode(s) else {
    fatalError()
  }
  return NSKeyedArchiver.archivedData(withRootObject: encodedData)
}

func decodeStruct_old(data: Data) -> MyStruct? {
  guard let data = NSKeyedUnarchiver.unarchiveObject(with: data) as? Data else {
    return nil
  }
  return try? JSONDecoder().decode(MyStruct.self, from: data)
}

推奨された方法

struct MyStruct: Encodable, Decodable {
  var num: Int = 0
  var bool: Bool = true
  var date: Date = .now
}

func encodeStruct_new(s: MyStruct) -> Data? {
  guard let encodedData = try? JSONEncoder().encode(s) else {
    fatalError()
  }
  return try? NSKeyedArchiver.archivedData(withRootObject: encodedData, requiringSecureCoding: false)
}

func decodeStruct_new(data: Data) -> MyStruct? {
  guard let data = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: data) as? Data else {
    return nil
  }
  return try? JSONDecoder().decode(MyStruct.self, from: data)
}

References

Discussion