如何使用 Codable 存储 NSCoding 数据

1,851 阅读1分钟

一般来说,Objective-C 通过 NSCoding 归档数据,Swift 采用 Codable 方式。但是并不意味着它们无法协同工作,需要一些很少的工作就可以将 NSCoding 的数据在 Codable 内部进行归档。怎么理解?

比方说, UIColorUIImage 实现了 NSCoding 但是并没有实现 Codable

这里我们通过一个简单的 struct 举例:

struct Person {
    var name: String 
    var favoriteColor: UIColor 
}

如何将 Person 实现 Codable, 然后通过 JSONEncoder 进行归档?

我们将按这 4 个步骤:

  1. 创建 Persionextension,且遵守 Codable 协议
  2. 创建自定义的 CodingKey,用以描述哪些数据会被保存
  3. 实现 init(from:) 方法,将原始数据转化为 UIColor 类型
  4. 实现 encode(to:) 方法,将 UIColor 数据转为原始数据,这样 Codable 可以将其 base-64 编码

首先给 Persion 添加 extension:

extension Persion: Codable {

}

添加后,编译是失败的,因为 UIColor 没有实现 Codable。那我们继续第二步:添加自定义的 coding keys

enum CodingKeys: String, CodingKey {
    case name 
    case favoriteColor
}

我们需要通过声明这些 coding keys 进行手动地编码和解码。

实现解码操作:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    // 获取到原始数据
    let colorData = try container.decode(Data.self, forKey: .favoriteColor)
    // 进行解码
    favoriteColor = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(colorData) as? UIColor ?? UIColor.black
}

实现编码操作:

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(name, forKey: .name)
    // 转化为 原始数据
    let colorData = try NSKeyedArchiver.archivedData(withRootObject: favoriteColor, requiringSecureCoding: false)
    // 进行编码
    try container.encode(colorData, forKey: .favoriteColor)
}

上面的工作完成后,我们就可以愉快的玩耍了。

let taylor = Person(name: "Swift", favoriteColor: .blue)
let encoder = JSONEncoder()

do {
    let encoded = try encoder.encode(taylor)
    let str = String(decoding: encoded, as: UTF8.self)
    print(str)
} catch {
    print(error.localizedDescription)
}

原文阅读:How to store NSCoding data using Codable