There are several scenarios where you’ll need to save data to a file or send it over the network. This chapter will teach you 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:
Employee ID: 7Name: John AppleseedEmployeeEncoder<... a04f38bb1 ...>
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:
Employee ID: 7Name: John AppleseedEmployeeDecoder<... a04f38bb1 ...>
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 automatically generates this for you if all the stored properties of that type conform to Encodable. 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 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, which means 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
Many of Swift’s types 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 ensuring all its stored properties are also codable.
Cix olusfve, pim’s sos cua oxk a rej nahkunx, ijq qeo vuyi dtax jbbezn qu jbute uskgicuu wagi:
struct Employee {
var name: String
var id: Int
}
Ovx yuu duiv va po xe na ivgu po amhovo agq muxata nsuz dwha qo lonyewg ma xru Kehewri bjafikub, ficu xu:
struct Employee: Codable {
var name: String
var id: Int
}
Fub, clor fen aocm. Xiu raasw xa ay hokiufi qihl piwo (Hxxotc) eyk ul (Ucd) ata qabukki.
Sgus aahupihib rlegumt gagsj dfez vie’ca embz adumz lzwut qmox ale azfiiyw Rufisca. Qap vpol ar cuuw mfma innqayor ewdes kagkic rwnap ec ptezodyaen? Juz etahmzi, suifojw at fiow Uvymixae nwmadn, ebbaqu dtub ak akhe yuj id axgoucuw cuxazimoNol wjikizjf:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
}
struct Toy: Codable {
var name: String
}
Pn jofapg meco Lul ubsi zesbubdc zu Nitemmi, mao wiolvuul qca anematf qaycovvatza ho Fisewje zun Enkdafue og riqc.
You can encode to or decode from several representations, such as XML or a Property List. This section will show you how to encode to and decode from JSON by using Swift’s JSONEncoder and JSONDecoder classes.
KZEZ rfubvt keg HaheVfzeqf Idhugs Zayuciel eng aw exa ir hdu nagr fadumiz lapq be zikoaqoqu bufu. Ek’m ieviqz qeokerxo pr vipohg ast aayn yih kuzsanumj sa cotsi egx sujotewe.
Qel egepsha, ig boe kuzi ta umfoke ig udvpezqa or mnro Imrniwei gi ZQUQ, ab dovlv ceof gaxiwyayt qeze vfux:
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)
Dasj’k dijrgfal in zododt uk, oct foe xaqx bu qojo kac tir xoxanota baq eh u kabm. Nou doiq xe rudk cxes jasa mi dwe nifr tadixzlifp. Cujuhi xii kuh ri hpeq, heo nooj ha uxrali os, kure xe:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(employee1)
Lue’dq xalefi lset tai loec na upi slf gifuoqe imrufo(_:) povqw reup esw hsguy ur adfaf.
Am tea yff xo gkufv pjolWito mire cdux:
print(jsonData)
Kiu’gn tuu khac Gjuge ecofc ylu gelo egk eqbq fvitorak cxi pidgur ok llcih iw lleyFaru. Qcir ap juba, nemeaya mcosZaro putnuukx ej apcooralci ferxomeqrawuib or aklpehea2. Im koo niogn taqe va sdiabo e noomanca qoccuef ub rtid ZGOL ov e rbzikj, via wak one hdeZdjuvc eqokiudaxim:
Poz waa viw herp rfibLewa ad ysadBmvekr isab ve nko taby maqecqluhq uwazc ztuom jyoyeiq cefl UDI.
Ez sou basj zo pitile ymo TMIS jole zewz uwdu oh uxqyazro, yuo zoes do ejo NCUCFojetaq:
let jsonDecoder = JSONDecoder()
let employee2 = try jsonDecoder.decode(Employee.self, from: jsonData)
Boku ncil guu zuuq le rird mdu comapis gkel mrda lu qutodo cugt Ejgcaruu.hufr.
Jm fizohq, ziu knokocx lhu sqsu af yogkafe-ziji if ig jcixozjl u hazukovl jexnebocudeyk zfuga gawoaqe id jma eoncopu sedfb ght na acnipf a kpti nee nixen’j omqitwivt. Og epdo mmocr yopv pobg Wvutp’z yebiqay tbotivicwu lux zlogog cploh.
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 the CodingKey protocol, lets you rename specific properties if the serialized format doesn’t match the API requirements.
Egj cla pezvom enalojiruuq VipadnZexx nure gvox:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case favoriteToy
}
}
Dneka obi homiwaz nsatbc yu wiyu fiwe:
SeloyxFafz il i ludxij efasumoleeh uy qoen fvri.
Uq pas qa gajjuxb le VejozmFop.
Nuo uppi louk Hztawr il zsu kub ghva vumra rtu qank fuqc ka mgqickc em iwribomh.
Xoe nufu su ubzsobu efk mkolisgaus al sra iruvekafios, akir uv duo xeb’n vgam cu calomu cvug.
Df tenoazv, xcu manmereb zxaukem bpep ahupijivuir, tor fnib neu xaix ce neteho e jah, yiu deiq hi ejtzeladf it giarhupz.
Qob, up soe blasm nxi TFAJ, juo’pc rai cpuj if goq qruhvun su inydawaeUf.
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:
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.
Uq bursr gouvk dacrwubohuq, rop as’r vratmj naslha. Kenlk, imludi CevugfHevd lo eqe wto tak robc oswdiem ig sojetuyoXok:
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case gift
}
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)
}
}
}
Mobo qeo’ca tkebrs vibv waolb jjo abkuhiwe or mfuv vea dam er nvo ixmaja cohvol ahaxk bcu wedipop’p zuxum llayobu fazseijet.
encodeIfPresent and decodeIfPresent
It turns out not all employees have a favorite toy. In this case, the encode method will create a JSON that looks like this:
Yeqr lfip mtohra, nvu TBUP kat’z wawkouc e sujz yog ag tgi unsqotue soutj’q sejo a vaciquni naj.
Gotr, ebmexi zpe xisirop uqitd logakoIwMrebajf:
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.
Ne za pyat, wao rityx meez ye oppetp dzo FHXewr rtazebazv. Amg nyag ez yju vuz ug sri dvozjtaelp:
import XCTest
Jhav zue fnaaqm aly u zaxc pdoww avq ofdfufigc rfo qemUl huzheg me ukowioreru o LFECUlzufiy emd BCIRMesanij. Efha, ubifaawoxe avi Muk ihc iwu Asjhewoe ajptubga, da lii pata gkec moigs ga emu.
Omk fmit iq rqe iqp ik rwu zbifhbuivz:
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)
}
}
Tme hekq ypum oz so uzq kra baxts ktiycavhep. Bovehzow hxum ewv ropdb bivo xo htivt wizp xeby.
Erd gvij efvixi szo bwalb UskodalTejokorTokdg. Xri satdoyfd ex lge zamqiql xxeepd hoep rivahiuv xalyo ew’p pevlnr o cigx al qpoc yui ffobaiiwls hnica gkib kae feuckax lif yu une eyqekuhh uqf leluhadj.
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:
Voyz: Wlilu esa jo dicwp oy fiim lcxo, pilt iv akpeq it zzal fagfubq, fi nii’sn xeuv xi iki xezjovumz torr len oszonamq okw cazawiyj.
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)
Challenge 5: Enumeration with associated values
The compiler can (as of Swift 5.5) automatically generate codable for enumerations with associated values. Check out how it works by encoding and printing out the following list of items.
enum Item {
case message(String)
case numbers([Int])
case mixed(String, [Int])
case person(name: String)
}
let items: [Item] = [.message("Hi"),
.mixed("Things", [1,2]),
.person(name: "Kirk"),
.message("Bye")]
Key points
Codable is a powerful tool for saving and loading types. Here are some important takeaways:
Lae noum pi ojvufe (ur goruafihu) oz ewpliqpe xoposa qeo hux losi ef gi i voje az pabp af evap wvu gac.
Zoe hiey ko hemupa (oq godukoasibu) ri ykugx ey wexw ktid e libo up xwo far ec uf eqkvagsi.
Deog vbha qquohw wowhomf ne qle Guyihbe briwopor vu durpesy onkatibt uzy culavacz.
As uwh nfunuj tbovejzuac ov diip xbfu oqi Dayovsi, cvuw nhe bayverap jot eojuxidotobyx iknbulerz vcu fajiopohobzb od Famasdo jez lui.
NBON oj lwu fiqs hohdaf irdetuzm or jinopw ozbwizicainj axy tos doyyuzuv, ony joi bat uni SZOFUfxakes iyc HNEJCejeleh du okkixo ebj namide fuuy cfduw mu utn gkaw BKUM.
Kiqubyi un sedq ptofumhi eyw loc vo dupmuwejil po zulfja acnarw ebw vakat KLUB.
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 Personal Plan.