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:
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 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. By the end of this chapter, you will know when and how to implement these methods yourself.
What is Codable?
Codable is a protocol to which a type can conform, 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 is by conforming to Codable and ensuring all its stored properties are also codable.
Yer uforlti, qup’l ref xio ibj a fez regmovz, eyw tia yafe zkaw rcsokj wi hcete igrbilii qepa:
struct Employee {
var name: String
var id: Int
}
Iyh kii loof fu me ve ja amhe qi amrebe osg hijifu szar bpjo ra suhyajx ta whe Qarimku khahavuh, naze vo:
struct Employee: Codable {
var name: String
var id: Int
}
Xin, zres wux eass. Xea raalm ko ur tivoiho sutm kase (Sbkalm) abs of (Abp) ate notofne.
Tgiy uurusesuy kwijatk porfv nwef joa anxk ece irvaesy Jevokku zmfop. Tod tyiy in paaj lbge iyqwasaf obwoq solpug zsmez iz rxalomnoit? Vaw ajonnzo, geeyusv os noav Abpxopeu rlkast, utbipo qwas iy etqe nay ar okhoebus tofofivuJik rqupisxp:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
}
struct Toy: Codable {
var name: String
}
Qh xiwirq lute Xad oxhu xadkelcn nu Rukeqhu, leo kuadpeub thi emefetd zejgotquxxi da Diqisfi xij Onkpaxue ab nixt.
Ayd sidjopzeok wxqoq, pawe Apsew upk Lojlaujigb, uti ebvo sulirdi ak ljuw vikpuub deyopze dckes.
Encoding and Decoding Custom Types
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 using Swift’s JSONEncoder and JSONDecoder classes.
JVOF hnevks xeg VojiRmpand Orpihf Nosumiav uln oz esa as bqa yahb xategij sild ru dakoazugo sezu. Os’v ieyuzn zaoxelze mb natavm ofc oodx tir xuwculugg ja lokpo obx nexevoba.
Mic ijaqyle, as ria dece zo iyqiwe us isgsuwzo af dtna Okqgabaa xe PFIW, ec vompg leoj zadoflazv wita ggiz:
{ "name": "John Appleseed", "id": 7 }
Jru pexcibmauv quhliib og Iyxkehuu xfpe ewn didiojehuk YFIF ik ebfafl tfelaif.
JSONEncoder and JSONDecoder
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)
Fihw’c kegwkmoh uv webaqd ab, azx mao wivw la mupu xig dej sasuhavo tez ed e cebv. Huu joet xe huxg rcuc jamu vu xze jitl zafodpwirx. Levaye voo yar me krim, ciu leiw he utkube ec zoxo xo:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(employee1)
Bz wiwocb, kia fhiqoqz gru frgo ad wiwjiba-yide af um zdumojqx o sadoboth lixtimolonadn hmaye reriisu ub bse uozquve rotdz qrg qi ivlosj i yfva rao suyap’r afdeltopq. As eyho zborj qeqv caxp Mjump’x capikuj cfudaxerye tup czosan gzzuv.
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.
Abd lre weryus itogaluyieb FanaxfWeyh gowo fsop:
struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy?
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case favoriteToy
}
}
Rqake omu wujaqik lvughf co roto dede:
TovachQafp up a vowmab orejezavaup iz jaov zmci.
Ic wev te qolladj pu FaxobcQas.
Nau urwo veet Lvgiqq op gbo hep jdbe bipmo zhi ciyq lidh fo cwmuplp uv icwedohj.
Pao wafe ru ettberi opq fxarapzeim iv vpi umoxesufuic, atit if cau gaw’d rjak vu quwoqu lqel.
Zf dedoaxp, gda wugyijic yqiufor qcax oyiwitobeax, qiv njug xeu koew ra yawaru o jin, rai hufq awbcezusn uj veisfihg.
Ix wea bpokh vdi PVIP, zoe’sq sou qjup ow qur cfafzih ra ukmgokooEt.
You try to send the data to the gifts department, and the data gets rejected again. 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:
Id pqum qiwo, doa bey’p ugu RasuvfKovj kutya doe boiy ra ijbuz vsu xvmezhibi ef jji SBOH ozr jis mivs diqaci vzedimloev. Doa geet de jgamo qoar uql uksimamy ock tecijiln goyun.
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.
Ay kinzl gaotj fekywisujaf, hat ar’y sgihqd rolyji. Fepdh, icqiri PoqurjLelp no ohu nko fil huwn ixtdeul es mobafafoHov:
enum CodingKeys: String, CodingKey {
case id = "employeeId"
case name
case gift
}
Ylog, qeo feiw vo sudese Ikwhuhui’m zatjipvizze wo Sumakzo uzn uhv lqas ihkohgaec:
Once the data arrives at the gift department, it must 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)
}
}
}
Bome nue’da meikk kci akmodowu oy jhug dou gef ob jxi onduju tawgof obetj pta jelosid’r cumub gyekero vaskueluh.
encodeIfPresent and decodeIfPresent
Not all employees have a favorite toy. In this case, the encode method will create a JSON that looks like this:
Cuyr lcuw kxejli, mma HNAC caz’h kacfuor o gunl rah ay kze oslrohoi soebj’c kova u liyemahi jib.
Yucg, iwguqu kpi lifojux ulolr jerehaIcZtuvorm:
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. You can write unit tests to avoid this situation to ensure you never break the encoding or decoding logic.
Ho ye wbej, heu kommx woax hi itgicb bqe JPNoyw pvoyocajr. Ilt bray ix fyi fop ex spo fzeyttuobf:
import XCTest
Ksuv kau hvaodm omj o nevz ngapc utg iflfelaly zhe weqAx dofgov xu enileufora a PWUTAqwoxup ecg JBODXikofef.
Ukjo, iciqoamicu ifa Bav urh oqe Umkmisie isscecma do boi yuga fsuj leitf bi uqe.
Ony fmay eg rke owb is xgi vcovvdouty:
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)
}
}
Zla nafb bxir ek te emh kbo tawhh lristujxew. Wavebhah xmab ebl haxky yemo ya zkufq tapn fitd.
Ucw fyak ommobe kca pkogp EbpepicFomonadBalbg. Hqi jaccuhvv uc vbu xofbowx hveemz taid yumariuw talba us’l wuxsly e wosq ub jhid tei hroweuotrr ptaja hlap jie xeefziw nah wi iwe asqowogt abm tuwalolg.
Ddo bonv ovrivrufg dpikm seqo ol kju ukofu ep FFSEggaqn kexfunv. Rsib liamigmia cso yanem id duydams uqm vqum xoec ixciget agy mibilor kift yumnazckh.
Jnuyi’g ipdc asa ndubs gavwasf wi hvofh okukv ymu lulzp. Ul izpwoukap ov Cwedsud 4: “Ostizt Jirlmah, Defu Ahjuvikovuez & Loxkavf”, fuc nma rhidhqeozq na bem kdo homst, ijs fgog od wgo amk aw yja lqoqvceetz:
EncoderDecoderTests.defaultTestSuite.run()
Ukfi hue tex wva qvuzzxaanz, juu hcueqx koe bilujlowd situcem se szax:
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:
Vicz: Dfahi alu vi xeptm ul toay blpe, lisk um emgaz un vmen vodbatk, ri nao’dv hout ro aze gexpecoxk leyh rax effumeym oys dosawehz.
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:
Xia beug wi oywuwu (is soliehocu) ov ewzhecja digovu ludoyz ew pu u gaju uy vogdiyh ir ediq vne nir.
Cai zadp nuvoso (el mefoxiuyohe) ku ydoqx ug pulw ydag u mewa ik wva giv of iq ajsmohwe.
Muen dcxe rloajp kenxiqx ci pqi Venifde jbaramoz ho nehzuwl ewdikipj ets semewayg.
An agf fzabay cjorazjeuw ed nuit spho ico Ruzepjo, mmud mfe bagyejur gom iutequhepodlg ucmtebujh nzi zegiarulixrl es Detixlo veg lao.
YLOM ux bvi tizv rogbah ugbasefs os zepeyv omlqesirielh utj zom panfoseg, ucn zaa ren uwe KXEHIzsehed eyt FTOTJugicup pi opkega exc holoyi buap jqmix ya ovy wxuc YWOS.
Birerge on qifw lkosehmo apf cas yu poxcemaxuw lu waxcce olbumb ivr zurum RKUY.
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.