WWDC
Swift
swift4

Swift4.0 で追加される Codable

More than 1 year has passed since last update.

※ まだ正式リリースされていない情報なので変更の可能性があります🙇

Swift3まではstructenumをデータとして保存や読み込みをする際に必要となるオブジェクトのシリアライズとデシリアライズのために一発NSCodingプロトコルに準拠したNSObjectを噛ませなければいけませんでした😭

struct Person {

let name: String
let age: Int
}

extension Person {

class Helper: NSObject, NSCoding {

let person: Person?

init(person: Person) {
self.person = person
super.init()
}

required init?(coder aDecoder: NSCoder) {
guard
let name = aDecoder.decodeObject(forKey: "name") as? String,
let age = aDecoder.decodeObject(forKey: "age") as? Int else {
return nil
}

person = Person(name: name, age: age)
super.init()
}

func encode(with aCoder: NSCoder) {
guard let person = person else { return }

aCoder.encode(person.name, forKey: "name")
aCoder.encode(person.age, forKey: "age")
}
}
}

let person = Person(name: "Foo", age: 10)
let helper = Person.Helper(person: person)
let data = NSKeyedArchiver.archivedData(withRootObject: helper)

ただ、PersonをDataにしたいだけなのにこの手間。Swift4ではCodableというプロトコルがFoundationに追加されて解決しました☺️

https://github.com/apple/swift-evolution/blob/master/proposals/0166-swift-archival-serialization.md


struct Person: Codable {

enum Child: String, Codable {
case son, daughter
}

let name: String
let age: Int
let children: [Child]
}

let person = Person(name: "Foo", age: 10, children: [.son, .daughter])
let data = try? JSONEncoder().encode(person)

これだけで済むのです👏エンコードを例にしましたが、定義元を見るとわかるのですがデコードも可能です。しかもSwift4で追加されたプロトコルの&が使われている👏

https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md

/// A type that can convert itself into and out of an external representation.

public typealias Codable = Decodable & Encodable

これならAPIなどで取得したデータをパースする時の処理に外部ライブラリを使わなくても済みますね。

let person = try? JSONDecoder().decode(Person.self, from: data)

このEncoderは現在JSONEncoderPropertyListEncoderのみですが、今後増えて行くと思います。

しかもこの検証をPlaygroundで行なっていたところ、バイナリデータのサイズに大きな違いがあることも分かりました🎉前者がデカいのはNSObjectを継承しているためでしょう。

let data = NSKeyedArchiver.archivedData(withRootObject: helper) // 299 bytes

let data = try? JSONEncoder().encode(person) // 53 bytes

おしまい👏