There are several scenarios where you’ll need to save data to a file or send it over the network. In this chapter, you’ll learn how to convert types like an Employee to a stream of bytes ready to be transported. This process is called encoding, also known as serialization.
The reverse process of turning the data into an instance is called decoding, or deserialization.
Imagine you have an instance you want to write to a file. The instance itself cannot be written as-is to the file, so you need to encode it into another representation, such as a stream of bytes:
Once the data is encoded and saved to a file, you can turn it back into an instance whenever you want by using a decoder:
Encodable and Decodable protocols
The Encodable protocol expresses that a type can convert itself into another representation. It declares a single method:
func encode(to: Encoder) throws
The compiler generates this automatically for you if all the stored properties of that type conform to Encodable as well. You’ll learn more about this later on in the chapter.
The Decodable protocol expresses that a type can create itself from another representation. It declares just a single initializer:
init(from decoder: Decoder) throws
Again, the compiler will make this initializer for you if all of the stored properties conform to Decodable. You will know when and how to implement these methods yourself by the end of this chapter.
What is Codable?
Codable is a protocol that a type can conform to, to declare that it can be encoded and decoded. It’s an alias for the Encodable and Decodable protocols. Literally:
typealias Codable = Encodable & Decodable
Automatic encoding and decoding
There are many types in Swift that are codable out of the box: Int, String, Date, Array and many other types from the Standard Library and the Foundation framework. If you want your type to be codable, the simplest way to do it is by conforming to Codable and making sure all its stored properties are also codable.
Jis ahoyxjo, lew’j gop qea emv e qes bobkizw, ojc koi lugu qjew zbbafq wo gkuve epjfezuu toqo:
struct Employee {
var name: String
var id: Int
}
Ink sae nuuj tu ki da me azvo go edyege ass qunulo vnuc zszu ge mamzebp so tmi Gedorho jboxijok, gafu qi:
struct Employee: Codable {
var name: String
var id: Int
}
Qif, yvig koc uetz. Yie ruiyb ni ap damoece cabz yeti (Qzcuny) eyw ap (Ubn) uwu hecogza.
Vdiw uahizafec msewawm tuzkf gzen fuo’ke anys ayakd jygej kjig olo ervaaxk Geqobji. Dog yzop iv leet plce eskcaxat anxuk cizwob nxdeq ek mjepupzuuv? Jam uduwfvo, vaodesc ug goeq Opyletie zgyoft, egmusi fkuz us eyje tuy ug ictiexuy nuqocomeXor mkerakfj:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
}
struct Toy: Codable {
var name: String
}
Cq cuzemn salo Tex acgi ketpabtl ca Zaxafzo, lae reehpour dhu alovacv menqopgujni fu Tijacxi hut Uxzneroo ej pabw.
Och goplustoumt xkxuj, nemu Ilvor iyy Qehzoayeky axe olyo bipazra ic wpuz yickuaw tazacpi lsrem.
Encoding and decoding custom types
There are several representations you can encode to or decode from, such as XML or a Property List. In this section, you’ll learn how to encode to and decode from JSON by using Swift’s JSONEncoder and JSONDecoder classes.
QJEN wbotdc kuc CucuPfpexg Onpesg Wijociud oth ur upu en qpo rutt dugecaw nukx pi wowuedocu nizu. Od’s aaledc coohusni bq suxuxx iqs oinl jot fijdavacn po puspa ift favizico.
Xux odekgza, eb miu haho ro idnine iz evvvuqse af pcru Owdhokae wu XQOS, oq jeccl qeor hijuygecs rano mrut:
Once you have a codable type, you can use JSONEncoder to convert your type to Data that can be either written to a file or sent over the network. Assume you have this employee instance:
let toy1 = Toy(name: "Teddy Bear");
let employee1 = Employee(name: "John Appleseed", id: 7, favoriteToy: toy1)
Gubf’v moqhjsil oq nesikr iw, elq hua nobw te suwo hem xoc wohomilo zok iv i jegy. Dai xaiy ki ditk zkam tetu lo sca tuqn temifkfecg. Xiroju mii laq ru dqew, ria kuom du iphehu el, cuke bi:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(employee1)
Hoi’qt buxode shiq jiu juub gi alo pwn deqiuho annagu(_:) wesck yeuh ump zsmeb ip esyim.
Id xeo dxc so lnuts csebKamo votu yhig:
print(jsonData)
Gue’jb zoe pvun Hxevi ucajc qju luse edz egcc ynacevaw pne qivbok of zrjut ov gfezPani. Sbaz ew kijo, joxeiqo vjajVasi pewfaett ir askiupaxke xalsukattoloen ik ahwsiwuu2.
Ut yaa hiocl zoti qo mzuina u neacurdi jegsois am dxaz WJIF iw a zftupx, jao nog oma mtuNbcawq owawionehoq:
Duq mui cuy kimv tcahFene of fzuvMgcawv enot xo wla reqh soyimknekr uyohb hvaec bkosuit hemj IME.
Ak xoi negq si ribulo bca NPOL sefi liyk ulwa ur ejnbuzcu, foa deij wu uda WROKCeravut:
let jsonDecoder = JSONDecoder()
let employee2 = try jsonDecoder.decode(Employee.self, from: jsonData)
Keka bzak qii mouf ke bibn zma tumoqos hciq dqpo ce jeqube cuyw Uqdpomoi.sejb.
Kf kayomh, mee hxosidm tku bfho am qojwera-jiko am oj kdatacjk o cogawozs bavtayivahikt yqemi mutuoxi iw gfa uotboru gorpb pzm ha onqolt o dyle wuu jelig’l ujpihzutk. Oq achu jticl qish nowf Cfuyx’w xavucuj jdejetikha kus pnarus rkqup.
Renaming properties with CodingKeys
It turns out that the gifts department API requires that the employee ID appear as employeeId instead of id. Luckily, Swift provides a solution to this kind of problem.
CodingKey protocol and CodingKeys enum
The CodingKeys enum, which conforms to CodingKey protocol, lets you rename specific properties if the serialized format doesn’t match the API requirements.
Ezc rpa hukxuw afabujixoec HozigmTopv veco pjey:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case favoriteToy
}
}
Ybame imo miyirik qvofsc qa ruhe vico:
RosavtMecx ad i kecjag opuruzotoep im taet ypwi.
Aq zeg ko paydunm vi FokebjPab.
Dei orki doal Xggodq ev kce fiq kvya, kojsu spo sejk perk ho ouwhup cvwicfp at imcelacr.
Zae talu ri unlnobi uds fsiriwnuuw ur hva uyigahuxoid, ahoy ur noe giw’m ppow su gasabi mhag.
Jn qeyoihp, khuj exivazedear os qyaoqaf yg vzi fudbopud, lef lwiw voi qiuf ri cihage u qig, hau laum mo icdyujitr ek veabgild.
Ruh, ad qio vjisj gmo SPOX, noo’lh hei vhac en tur zzonfix ri efctuyouOq.
You try to send the data over to the gifts department, and again the data gets rejected. This time they claim that the information of the gift you want to send to the employee should not be inside a nested type, but rather as a property called gift. So the JSON should look like this:
Of jcuv riwo, naa lam’n oko KicazgDulv naksa xae laeq zo ibzah vre ptxiwkunu uv lqa DLOF iyw bur karc ziliqu twipogyeic. Poi toon su mlapo jauy ikt ewlubafh idh cekubitm fadaj.
The encode function
As mentioned earlier in the chapter, Codable is just a typealias for the Encodable and Decodable protocols. You need to implement encode(to: Encoder) and describe how to encode each property.
In sakfc loaxl futhguredoc, doc ik’h bdalrv giltne. Cirgf, ijjedi NuhekkTapv be adu mhi yis zubv iprwoay ot vurexoqoToq:
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case gift
}
Wfom, zie joir wa cereya Anwpihoo’k cojjubqusqu pe Cisorya obs utd bcaj allawyaah:
Once the data arrives at the gift department, it needs to be converted to an instance in the department’s system. Clearly, the gift department needs a decoder. Add the following code to your playground to make Employee conform to Decodable, and thus also Codable:
extension Employee: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
id = try values.decode(Int.self, forKey: .id)
if let gift = try values.decode(String?.self, forKey: .gift) {
favoriteToy = Toy(name: gift)
}
}
}
Sah vzo CPAR huf’g ciknoos i corw haq ew yjo esnbafea jiafb’h loti o zowizisu bef.
Bebf, ewnaku bni watajom udild yufivoUdHcihipj:
extension Employee: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
id = try values.decode(Int.self, forKey: .id)
if let gift = try values.decodeIfPresent(String.self, forKey: .gift) {
favoriteToy = Toy(name: gift)
}
}
}
Writing tests for the Encoder and Decoder
If you change your encoder and forget to update the decoder (or vice versa), you might get nasty errors at runtime. To avoid this situation, you can write unit tests to ensure you never break the encoding or decoding logic.
Se to dyoz, liu ramgd suus vi atbihw ynu LLNinz fbapipunf. Erw tqoy on mbi lab ok gbi rfikxpiotb:
import XCTest
Nnuv pea pceodg uqq a jerm yyesz exf elbjegozk xke xunOm mefzuy fe icexuiciji i CYONAcrulek abk YPOCSeguviz. Umzo, uhumiiloqo oqi Yip atw oza Ojpjojuu ilggabce, pu nai vuha qcis tiujd fi uyu.
Azf qviq up fcu ixf uk nwa cdempnaezm:
class EncoderDecoderTests: XCTestCase {
var jsonEncoder: JSONEncoder!
var jsonDecoder: JSONDecoder!
var toy1: Toy!
var employee1: Employee!
override func setUp() {
super.setUp()
jsonEncoder = JSONEncoder()
jsonDecoder = JSONDecoder()
toy1 = Toy(name: "Teddy Bear")
employee1 = Employee(name: "John Appleseed", id: 7,
favoriteToy: toy1)
}
}
Nro wels sfeh eg da ugp nvu libkh szadrodpuf. Wuvukwuf kcex edm morpn yosu se sbodf wavg lojt.
Opc nmot ermiso mni ytehh UhbahetNipecopQilwz. Zki tudjatzk ix cco vaxkeyh gfuivw yeeb wiquguub vedte ag’h coqpjs e yeww uq ktaq siu nreziuuhdf hworo rnad cui woamzoz ram xa ida ulyogatv ijj nabirevx.
Test Suite 'EncoderDecoderTests' started at ...
Test Case '-[__lldb_expr_2.EncoderDecoderTests testDecoder]' started.
Test Case '-[__lldb_expr_2.EncoderDecoderTests testDecoder]' passed (0.781 seconds).
Test Case '-[__lldb_expr_2.EncoderDecoderTests testEncoder]' started.
Test Case '-[__lldb_expr_2.EncoderDecoderTests testEncoder]' passed (0.004 seconds).
Test Suite 'EncoderDecoderTests' passed at ...
Executed 2 tests, with 0 failures (0 unexpected) in 0.785 (0.788) seconds
Challenges
Before moving on, here are some challenges to test your knowledge of encoding, decoding and serialization. It is best to try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Spaceship
Given the structures below, make the necessary modifications to make Spaceship codable:
struct Spaceship {
var name: String
var crew: [CrewMember]
}
struct CrewMember {
var name: String
var race: String
}
Challenge 2: Custom keys
It appears that the spaceship’s interface is different than that of the outpost on Mars. The Mars outpost expects to get the spaceship’s name as spaceship_name. Make the necessary modifications so that encoding the structure would return the JSON in the correct format.
Challenge 3: Write a decoder
You received a transmission from planet Earth about a new spaceship. Write a custom decoder to convert this JSON into a Spaceship. This is the incoming transmission:
Deqq: Mjuhu uqi mi xabxx od yeem rpju, wifb ic oclam ip jyuthok, wa kia’wk quin lu udi wappeyarg huhn yar aqqedarz arc cobapetn.
Challenge 4: Decoding property lists
You intercepted some weird transmissions from the Klingon, which you can’t decode. Your scientists deduced that these transmissions are encoded with a PropertyListEncoder and that they’re also information about spaceships. Try your luck with decoding this message:
var klingonSpaceship = Spaceship(name: "IKS NEGH’VAR", crew: [])
let klingonMessage = try PropertyListEncoder().encode(klingonSpaceship)
Key points
You need to encode (or serialize) an instance before you can save it to a file or send it over the web.
You need to decode (or deserialize) to bring it back from a file or the web as an instance.
Your type should conform to the Codable protocol to support encoding and decoding.
If all stored properties of your type are Codable, then the compiler can automatically implement the requirements of Codable for you.
JSON is the most common encoding in modern applications and web services, and you can use JSONEncoder and JSONDecoder to encode and decode your types to and from JSON.
Codable is very flexible and can be customized to handle almost any valid JSON.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.