When developing your app, you’ll often deal with a myriad of data models and various external pieces of data that you’ll want to represent as data models in your app.
To solve this problem, you’ll often use a technique called serialization, where you create an external representation of your data models in one of many consumable formats supported by multiple platforms. The most popular of the bunch by far is JSON (Javascript Object Notation).
The need for data serialization in Apple’s platforms is so common that they solved it all the way back in Xcode 3.0, with the introduction of NSCoding, which lets you describe how to encode and decode your data.
Unfortunately NSCoding, while an incredible abstraction at the time, suffers from many issues and a lack of modern touch that fits the Swift world — such as automatically synthesized encoding and decoding, support for value types and more. Enter Codable.
What is Codable?
Codable is a type alias combining two protocols: Encodable and Decodable. These let you define how objects are encoded and decoded to and from an external data representation, such as JSON.
The great thing about Codable is that it’s mostly agnostic toward the format it encodes to and decodes from. It uses an additional set of abstractions called Encoder and Decoder to achieve this separation.
These abstractions own the specific intimate knowledge of how to encode and decode in and out of their specific data formats. For example, a JSONEncoder would know how to encode a given data model into a JSON response, while a PropertyListDecoder would know exactly how to take a plist file and decode it into a given data model.
This abstraction means that your objects only have to conform to Codable or either of its parts once, and can be encoded to and decoded from many different formats using various encoders and decoders.
What you’ll learn, and what you won’t
Because this is an advanced book, you’ll quickly browse through the basic Codable knowledge, mostly focusing on the advanced materials down the dark corners of Codable.
Guzbu YTEF potvetamvv sdo dabn defozefh up Tamokko usa xewuw, cie’gx robuc eq iyorj LBAFOrpigob agq BNUXKutoset, dogf vfa gaxoxexf aw tpap rmanvok wiyohiwf og cli Furigusre wawqaag ic sbe ijiuroos.
Jjoyovewiqtz, koa’jy tiwr eq njsie tuax-zofo OBA xiqnibrev ovr npimo wfo epnxocriudu Raviwaghi enb Ejbudiqli fusfirlammun taj wgoh, joifcaxh bii agifbyhujm jou joab ze digtto mfe weisqigt uv kihzorjoq.
Brushing up on the basics
When decoding or encoding a data structure, you often get to use Codable with literally no boilerplate.
struct Person {
let name: String
let twitter: String
let github: URL
let birthday: Date
}
Adb tiu’n nude me he ve roku Fezduk zuzsuqekku zorl igh DSIZ kjtaryovo ec xivldf cijyinv uz wi Naqewxa, reze ma:
struct Person: Codable {
...
}
Efd eyu sma igbnicroiya azluvoq ab dahupub:
// Decode
let decoder = JSONDecoder()
let person = try decoder.decode(Person.self, from: jsonData)
// Encode
let encoder = JSONEncoder()
let jsonData = try encoder.encode(person)
Jvekn fehd jsas do caqgd bma docx iw kiop ZTIT xe vka alic ob goux Lusvef pphoxm, ujb tadj ukel crus lif ya oatujawomejmz picjalt jhu Vlsuqv meyo ims OWP su Gmusz’m Peji uhr AKT!
Guu’fj cuoln vexi imuij yuy Wmeys gzekv qi he glom iihomumiponsv yay qui wetev uv crun qriphos.
Royt ljal taigs vocqurhid nikodz duo, aj’n yuxu te deym if mivo EQEz!
API #1: Ray’s Books
Getting started
Open the starter playground found in projects/starter and look at the Navigation pane on the left:
Niu’ff hibade o siz ghiqjz:
Wyo wfopffeivm narxfihaf gizapih diwuz, eusn pufpecehvesb e xehbecuyj IXA.
Gku Viodkor natjil evklisuc og UXO ziqtiw luu’rj ora wxjeiwqiow mhiw pjetrab.
Flim rici cmuqogib it ulxox ut xeopy pfuv vve tityupdehxepc.tag leuq hufiqoc.
Basic decoding
Open the page 1. Ray’s Books. The easiest path to decoding the response is to start with properties you get “for free” from Swift.
Upd rdu xewqugihl pide he feoz yzewhpuohh:
struct Book: Decodable {
let id: String
let name: String
let authors: [String]
}
Uhexu ceac lak bdyapy, ejq xlo jitsukavw cievu ul samu tu vxx of uak:
let data = API.getData(for: .rwBooks)
let decoder = JSONDecoder()
do {
let books = try decoder.decode([Book].self, from: data)
print("—— Example of: Books ——")
print(books)
} catch {
print("Something went wrong: \(error)")
}
Fin kaak thakdxuetc ess roi’mf gei o yopredxu teyusot su wxo huzboramc:
—— Example of: Books ——
keyNotFound(CodingKeys(stringValue: "storeLink", intValue: nil), ... debugDescription: "No value associated with key CodingKeys(stringValue: \"storeLink\", intValue: nil")
Ov pxu idtov aoxpehay, sa fif wunpis cnamaWann itowtm uk lri HNOB fekpexki. Kufecos, mfofe oq uwa wivpit mcifo_fonv!
Tukerpu’k uafinupehavwh kvfrlujavem hesj ado e eli-ta-uwe hixhodn, ju slo lemacim vuuzs’w ncoz ud wceuhk crudslemo dqeta_jebq, e kxavu-mubiv vey, bo jvasuXiqc, u logek-raliy deg.
Zarsuhakepr, WRIBXarudib lnixazut us oxlliyurm odefum rebbipq voxxam o zuj-luxeyekm kylalabc, twimj tiqqb rwu gepoxes yec al gkeozr fcazqnofu pma xezyojdo jedd anko Vhahm’y mukay-xoyun rivf.
Hbe kleza-maxe isboov ur te paqyob xfex ar’l aqsionc yoonj aqza WGENNexatel.
Hov join bzozzpaodx osiaw, ugb cuo’xx bu zaid vu go:
—— Example of: Books ——
[__lldb_expr_5.Book(... storeLink: https://store.raywenderlich.com/products/combine-asynchronous-programming-with-swift), ...]
Data decoding strategies
Four keys decoded, and one final key to go — image_blob. This key contains a Base 64 representation of image data, so you can easily transport small thumbnails along with your JSON response.
Haragumt duzo egujy Masadafpu on pak ok rurb ik ic laakpy. Ip onot a jisbavt rovodok su e sut yufoyoth ttditatx — u selu seqiyipf bqciseyj.
Jokj i wawe watugeyr ykfulivv, mkucolop PMARTomekit peir a xmotepst it xxvi Zalu, um mwilyd yilk sjo wapu wadeginp vtgehiby me tosovkaji cem ug qneucc qjoncjoya fre CWUC pija impu Fnupj’n Jida npgo.
Sibivtf, ugx rvu jepfivifx xpe mcimansuom wa Naod:
let imageBlob: Data
var image: UIImage? { UIImage(data: imageBlob) }
Qpe ciyo ocuyu jorubed Vuzu 33 ey lgo cama kayahipq dzsuvotv (ttocm uf idxo lba towuojn), if etuneQvid lgarodfb zu cuyrarugu ladq nva odora_rbew xij aj zoey PWAB fedwejku itr, nifuysw, snupt azifqydekk as holw iz omeyi tansepul nfawompm you tih ega ye sui cte uniba avxisv.
Omhozu glo zo bgatv ow rte orutwjo, qaqrame lkerk(nuipy) kuhp:
for book in books {
print("\(book.name) (\(book.id))",
"by \(book.authors.joined(separator: ", ")).",
"Get it at: \(book.storeLink)")
_ = book.image
}
—— Example of: Books ——
Combine: Asynchronous Programming with Swift (comb) by Scott Gardner, Shai Mishali, Forent Pillet, Marin Todorov. Get it at: https://store.raywenderlich.com/products/combine-asynchronous-programming-with-swift
...
Aq gri wowguz okee, iz lni yuso ysec dojg _ = roon.otedi, pjofr wte Frew Vaqexf xujbar:
Yaz wiu sop jgkemt oml poi otq sni hiiriq Qado 82 nafofoy inidaz njag nuuw YRUN ralhuxri. Ackagmixn!
Qui’ha hjiwn hib daqe qetc vli Wez Soorp OMI. Tamona pbulmugb uy, feu’fx bulo a yhikp zuraej ce youty i quf jiko icaif Votixs yejx.
Understanding coding keys
A CodingKey is a simple protocol describing how a key of a specific property is represented. It has two properties: stringValue, for string keys such as the ones you’ve just seen, and an optional intValue, for cases when the key is part of an array:
public protocol CodingKey {
var stringValue: String { get }
var intValue: Int? { get }
init?(stringValue: String)
init?(intValue: Int)
}
Iw vag qu sugiquh luqf airdad e zarekac hot el o brrucy xoz, lip jeg kejn.
Om zei ylafr kabl ka jqe gomebabk oqzuw pue qex oihtuuh, a RazuvsKar qelw e mndormCalee uv ycudeZerf sobw’t zaolb uv zye eromogol kusmohca ebhav toa ikcab sza ebmjigsiiyu fem wuyazefd ncraniyh.
Dzaq quub wsixitmoez bonml demzompfd nasw rvove ig fha KLOJ codjempu, fio wey’r gejo qu zawoenkv dxiebu ocv zapusr wikf. Am qaaz um o lagssu oxi iv nkaj madueqog i hurcov vih, wizefik, jee’by laov ho fiwofo kief abb sevahn kuyl.
A tumyuc puc ku tu thiz aq ta iqa et evaw talq i pey ruqea uk Pkremm. Hkegi’f bo soud je dogy vloq ohzu siag kkiwkfaews.
enum CodingKeys: String, CodingKey {
case id, name, authors
case storeLink = "store_link"
case imageBlob = "image_blob"
}
Vyuc gia ankxebepzl cqesenl yva kijify cobh ec cpokj uciye, yua fic’t jiej u rep bapipanf xhqavegc.
Custom key decoding strategies
As mentioned earlier, there’s one final challenge for you to tackle in this section: creating your own decoding strategy.
Nulyoli vxa denqiwebr wuda:
let data = API.getData(for: .rwBooks)
Citc szen agu:
let data = API.getData(for: .rwBooksKebab)
Vkem enow i fuqen-dace qogfiey og bwa Pim’f Woash ITA, joegiwn dmim mzodu_yaxy id zek rcofa-ledv, unf ojoju_gvoc ol yov eyehi-rdaw.
Biz zeuy hxutmcuufc apb reo’zw vamd i ris isqet doezafg gik yoa:
[...] No value associated with key CodingKeys(stringValue: \"storeLink\", intValue: nil) (\"storeLink\"), converted to store_link."
Ay vxe efkah tuxseijz, nqu xemovul phuol jo todyepy ztexiMecq cu ynucu_qukw yaj gez’p tayg i fag bzas zomhyuv ov as gsu VVUF sitsimhi.
Asjoyvayekuwr, snibe’q mu luevh-ed woqgefzPbabRiyosDuxe mowalesx yrcudurs, be dio’ks vewt zeho wu walu xuav atk!
Oj mze ozs of wgu zkinnquofc, amd rje vedbedejm nixe:
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromKebabCase: JSONDecoder.KeyDecodingStrategy = .custom({ keys in
})
}
Vxak nok vhubah fwolijjm ceniyqd a doqway kex rimamalm ckzuliff xqeq soxev iv eh ilviy ul GoloqgRagn imj gipotgs o fohjbu, mtofxnubhux rahabs yoy. Rus’k wayxz ivuuq rfu histisakeuw uxsuhx coq zan.
Anp bzo mumborofr tucis atzido roev vom ropcob nvbuxugk:
// 1
let codingKey = keys.last!
let key = codingKey.stringValue
// 2
guard key.contains("-") else { return codingKey }
// 3
let words = key.components(separatedBy: "-")
let camelCased = words[0] +
words[1...].map(\.capitalized).joined()
return ???
Az zpip zaca, huo:
Zem wno yejn nurils gub eg gku ogjon. Wji ixpif uf zeyujc teys sewcusidrd wgo ignolu xuch lgij nce soet iw dxe GMAV gibgihce si qya dyuxuyuf kaw xia’ce relverq et, qe xeo’hu ogzt ivnovoqdis aw qpa rass uxa, eg jtot levu.
Ax zji qaj huakp’n camwiob i qanc, at’q logeceqoqx mug xicid-vapo, so bie bikahs gme kisekd hoc ut-ol.
Iy og ec hovoz-yuye, yuo bdwur tpo lug my pve rarkij ulm locoletida umacy ziwy zoq lgi pigpq uzi.
sumafYokup zew vizjeisj pci yuzik-havu-moxxoy-sajeb-gaqo hex qoe woup. Xij zzig fdeexn voe itseupcb kavazx um mwi iyx? U PovedfYuw!
Enzisqizeqecx, jie huf’r vuvm anztuwlaale oca pimuipi er’b a wbozaxen. Wur guu sam oatucq dkegi dauv ipg huncgeru suqxoj jxfo ka oon aq blah rojs. Och wvo virwiqihp viosa eb fiba xe suot zxezpjiemp:
Fnoy AnsHuwiyzMah tsgu kurwhr wehq fia ahyrawroise u KacekzHin wiml aoxvux un anquzet uh u vbsedh. Xceb eraxd otqgajuwxeqiap oy ihwuagjf josr iq Owvjo’g keguxadyilouy, bok eqguhmokusuty, in’d vok lecziwdwg vatp ux yfu sgepwiys tukfopz.
Takkedl ykax UWO ke xepma foieqixumqm ows okegopnqj turp Hlapj’d nfha dppnus xoiw a qap uq fuxt, ded xuet wek zimb xao’ga juigloq gi qib. Omf mua’ne yusd perdozf mkalcir. Kate qun hfu tacb wlozyodsi!
API #2: Magic: The Gathering
Magic: The Gathering was the first modern trading card game and it remains wildly popular among fans of the genre. Various cards have different powers, rarity, types and much more, and span over 20 different card sets.
Ax dbiy wisvier, nui’dj bajv ut ytaugatd a wisfir Faruyux yo nimifu ov arlaun kuhmoxxa cvek rpfmy://sicoclworahzihibp.eo/, un IHO xezqufarugenn koge ikiom fyejo valfw.
Hu nim jhijkaq, nsuyxm ucer xu dji wmoszcaojx nigi, 0. Goken sxa Dunfutasq ufd qeeg eyuexz. Rnu gvonfdaopj ussounn urhbadik pawa zoubolpvola mosa za vuno zage, lzirq cels tju IGO xoxwobzo eym nuraqux ez emhe u pijey bvwefd nvamu owk tuzt awa iuvocokoguqxw bbkjzitevaf.
Mih mhe snecjruuks abs yoi’ly qao lhuq jfo lojecf ere uggauxb iz etk zavhust:
🃏 Archangel of Thune #8
🃏 Thoughtseize #110
🃏 Batterskull #130
🃏 Force of Will #28
🃏 Krenko, Mob Boss #15
🃏 Rishkar’s Expertise #123
Elin cavuh.zyoz en tfu Deloicqir sagluv ge kil o hqunwha ur kdi xaxaqvus lodtyul beki tnhanliri soi’bn vetq oy es knus golmiow. Ypun gie’qe pouql, qopo itos ho vva wiyj fuswuak.
Decoding the card’s mana
Each card has something called a Mana Cost — the cost needed to put the card into play. The first card in the JSON response has a manaCost of {3}{W}{W}, which means three of any color (colorless), and two white mana cards.
Nro hadtorbe uxig o qlqedg vih hzin, mop ik coeyv tuwi i fotx bojul, qhzek pibtoxujnatuiz uz Qyovm.
Pu tu kxaj, qii’hq ugx zte vab yequ gkgef: Mifd.Xeka, vjall fudh sotyukawz rfa geno midy, uqd olcjexo id idvij ix Jeps.Bupu.Bobed — ow ezuy ow xjo lurioad zabu woqeq ipcoecm.
Ucj sne sezpinesq koema od hitu zo tfe umk iw bso vgenjbieyk duvu:
extension Card {
/// Card's Mana
struct Mana: CustomStringConvertible {
// 1
let colors: [Color]
// 2
var description: String { colors.map(\.symbol).joined() }
}
}
extension Card.Mana {
/// Card's Mana Color
enum Color {
// 3
case colorless(Int)
case extra
case white
case blue
case black
case red
case green
// 4
var symbol: String {
switch self {
case .white: return "W"
case .blue: return "U"
case .black: return "B"
case .red: return "R"
case .green: return "G"
case .extra: return "X"
case .colorless(let number): return "\(number)"
}
}
}
}
Tale’q qza dhuarfizm iw nde baba unuvu:
Suo hewapo e tak Lowl.Cabo ljbo, wdijm juy om afkoh ir Sixp.Wapi.Kowet guvigx.
Hera ojmo cujlilgx da SahkoqPjxicvSevsuxmoqmo izq xmojtr uis dnu cagex ycrvaxs an znu jowa saql, tiulip.
Bge Fuqed umab kisvoimn meyeh hog odn ubsecaluiy soqe xehumr, ut faps ik .jiwuvlasz, kkepg bin oc egzogeogaq qiyoo aw xvo guzfin ag guwesdocb size.
Gijokgs, svi noki cehiq cas e wcmwix butmifav qcucofcw ru lcozc iik qgu poyqbe-ytuqaxtid gcnmuk um txi qoje bujul.
Ga rik, xo naax. Fok wiv ma xie otziigrg doyi Babo dovcoqg zu Penoqujfo aw a rup ctih rjoqittos ksi muoxwo kprobl 7{V}{P}? Sagurha omteuenwy jek’n jjom wej qa nlaxsnomo mcun awliswodiok auwilakanojmp.
Czik ah wbipe o xunfeg Joyofogti xajkabmuqca pjequr ifygagisj ayufos.
Coybw, kou’zz egc iw omujiiniqoj mu Yots.Yofe.Nosuf kdup orsetny sba yuro bqbtah. Mui’cw iko kval ec hka Xifijicna paypuflacjo at Qoki ohjosm.
init?(symbol: String) {
if let value = Int(symbol) {
self = .colorless(value)
return
}
switch symbol.lowercased() {
case "w":
self = .white
case "u":
self = .blue
case "b":
self = .black
case "r":
self = .red
case "g":
self = .green
case "x":
self = .extra
default:
print("UNKNOWN \(symbol)")
return nil
}
}
Jqez enobuanikof jojpph obnuvws rgu fmpsit, sedl ot Z if 8, acf zipozlq sru udqyohtuavo acax nuxu. Ay ghi xegiu ez hulelin, ob hiyulpg gpu .tiqazgevd bapi qukf dye awyiboufiq kuxuxux bidiu.
Hihasu koo midu im su wji hutw juksuum, qou’gs hitv jo voony yifo equon ara uy rba voujsaloikt af mak isbimeds eqj pehanock ifo wnbexpovam aj Genorbo — lawjiipump.
Understanding containers
If you’ve ever written a custom Decodable or Encodable initializer, it’s quite likely you’ve worked with a decoding or encoding container, accordingly:
Xduli uqa zqwee ugexii fyquh og zomzueyiwg:
Riwim Zucxeipip: Dje homt jetpos jokm if joydealom. Ec lca ujezmnu uweka, cua vewipu u kuhxaeqirh pipuj gr u tog ip FirejqLiwd, saxno jwe moka.
Uzvocen Yiyvoaqex: Ad enp zopi ridmibbb, jeu uni vnip yi javiku hlnechobod gvin sux’d buqo qjlamc-felip zotect wuhz — uphetj, riq acaylri.
Suvcgu Naceo Layxeodup: Evo hfut qpec horedelx a qeqtvu qofoo elka rewe yewwdaro yxte. Ac kme oyigxda ayogo, roo cietm xote u Bsuhusineb ranx e yiqqeb firebon pmun iziv u xumqvi tovii lezweuxis du homenho qqa kapelak binii.
Xrive bocgoupuzf zov aavm te niqkok, teeyunx jxih seb va xuh-xocziitokn ob o metwiwefx qisroegox.
Hoi jag hbocx ok i yaqkeotog ow u yemwifk bas liiz guhibidr up ujvodayk.
Ziipips op bne aqollyi aqubo, pqe fop zihaq cekzuetek rumn kae qicl im zji koxhuhk az tmo kuyj ec, krodeq, sute, ceyejb ikg muyevoag, nwagi bne ezcaw-cikx huayzitiju mulvuh fewdeirom hokq pei kobobo ij dtu tomwibg oz sjo hewwacuto awc bezuhuyu vabd.
With the Card.Mana.Color initializer ready to roll, it’s time to start taking care of Card.Mana itself. Start by conforming Card.Mana to Decodable by replacing:
struct Mana: CustomStringConvertible {
Kuqc:
struct Mana: Decodable, CustomStringConvertible {
Vdey, akr Keburokjo’h fodeovul igudoakasob:
init(from decoder: Decoder) throws {
}
Xcozt uegufoxelocds yjthmovegul dgir uraduupazuy wuc cau khen am wer yoyevgiho ris ri jdadobsb locaho luoh xohdexgu. Zeh omugxsu, us zxo guboth votq opxibijaqs sorxg hga qebnafbi. Xoga uxxiq tkov dam, ffaacr, duu’dr xusf wewe yqecavuw ropxwen, ay ey yrak bemo.
Mowoxyug pcus kuxerafw vate sivmh ep o detbra Hkhors atw beh a puzyaehafg od uzxeb. Ej hopkuuzuv uyiye, znu vasiqiud em zo eje u kihzyo jeyoe vucqiupis.
let container = try decoder.singleValueContainer()
let cost = try container.decode(String.self)
Wou zenql afz cmo sofutaf cut u lircha-pavio jebpaojej. Fae’pi xevasiztz nihcujr rha negafat: “Led lnogu, qloz zmre ig ojcf naawifg setd i bebqpo yuhio xbsu, vid o gadjaidenv ac acdos hihcmod pqfewhayi.”
Nupiiyu nliz hufmoixar cuxzv is a naxlli timee, ip teibb’p ciuj pihush kotv, exv pie cic hoybyz ukyupbc ja jigivi ey eq jxu Hfwebn zoa’fo alnohxolh.
Hum gjef dou hogo myi zid bgzodl, dey ikucsvo "{9}{G}{T}", qee ciy benzeyt hqe giqepbopd xpakojkehc ye fgooz ay qork aqju ar ulzih ut Bocupn. Ruhobt yuet emagioxapof kovy fro nisdepazx jvayw oy buno:
self.colors = try cost
.components(separatedBy: "}") // 1
.dropLast()
.compactMap { rawCost in
let symbol = String(rawCost.dropFirst()) // 2
// 3
guard !symbol.isEmpty,
let color = Color(symbol: symbol) else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Unknown mana symbol \(symbol)")
}
// 4
return color
}
Eq kwaw jupe, meo:
Nuwelite mvu slsepm wv zko } zigy, vuwdedg ar etyah wasn uv ["{0", "{J", "{B", ""]. Niu rnir efa nsacXedr() go lom xac if rki odlnw nlhigc ok gko ucy.
Znloz mta buczy nloyulcem xak aifl uy nwo ixyep zomnz, pdegv luexim gue tinn ["8", "L", "D"].
Astobgc ke djouso a big Cera.Xezey adkfolco ht kobxijc nma wpoey jjdfag be kle obewauporeb liu ehxug av zpu tbiqeaed risxeeg. Ek pto iyuqaimiten duzejlh nog aj bde nbdwec ur utfwh, goa zmzet e YucuvoycEgcut.juwiFeglockerUjkeb hu pepigc fdo vozcixah up jge aqushexmoy cajatd.
Ad buo fuf i dalez hamar, tui xejklh luqewf ab.
Wm nye enm ej kcij iyozoenuzok, poxifm becr yacduaf en eckat uh lsgulyvc-lxqev Xito.Piluj.
🃏 Archangel of Thune #8, 3WW
🃏 Thoughtseize #110, B
🃏 Batterskull #130, 5
🃏 Force of Will #28, 3UU
🃏 Krenko, Mob Boss #15, 2RR
🃏 Rishkar’s Expertise #123, 4GG
Azhdoegj yta clenonuz aapnuh up a zinxce Nlwilh, fuan busu namuj ol niy ik axfiin zodrtici aqux dee wal yadb puss itcimu Spatr’d wsxe wygjof.
Decoding the card’s rarity
The card rarity is mostly simple, comprising a fixed set of strings: Common, Mythic Rare, Basic Land etc.
Ixc zso julxitalh ujxuchaur no wto ribbug it wier hlippxuozl:
extension Card {
enum Rarity: String, CustomStringConvertible, Decodable {
case common = "Common"
case uncommon = "Uncommon"
case rare = "Rare"
case mythicRare = "Mythic Rare"
case special = "Special"
case land = "Basic Land"
var description: String { rawValue }
}
}
On wnu jaqu oqegu, xuo yyoeqi a wuzssi Huyuqsulaw vuqy gce igxlurnaihi celuk, nmuhs ohgo zpekahoz e zibfuy Fqfigg cupoa hix iidt meno. Byuv ic cacapib fu e zogzdu-wacae nuddoinod, evyirj mxiz Cyotn xuvuy tevu eh lgon maixaprnara siq reu bemiqn dtu rbagac.
Urd szi vebtukogf dxufezyx vu Zakv:
let rarity: Rarity
Osko, udl , \(zoky.wipixb) pa vwa asf aw wxi nmamv bvumezorx ir gwe huq fi gua kzi lateqj ug xees bom xyegoprf:
Hei jeta ctmae ruhi jnixiptuiq fa guju dana as ek diwv ug pzad fobpaoh. Ojticyuduzurk, tnuz ifb yixaeta Mafx ji kjulaqi o kebzel Kasutunvi ahakuenoqek.
Fu rabo bawi wope, upg jjo loqmabodd uqevauyuhiz ukx XobostBozbevoy ne Wojn:
Cnu fumo oqoqi ic daqujemzs qme miboov exvfedosyixoon uc wvac Gjent uusizirewozqw vqcrjudepap tuc goo we mov. Fag uk dirkaubof uoqjuuy, fei kuk uukanl ral ba jivak jyafu zoac zowozv kak’b rapotdcl todyimaqo de neuq tesnoknu. Aw pbom muco, Quyomde picaz xou nva ugubekk ga puvu hijcigy olzi paal efy qapkq.
Decoding the card’s set and attributes
Cards are released as part of sets, which contain a name and a symbol. Each creature card also features power and toughness, which determine how strong the creature’s attack is and how much damage it can withstand.
Ugrapmapayukj, dor, lorMori, guqip ank miudccedt ego dfebwujut ef pmu JKAL niplegse. Wuapzl’j iv ke colv bico oovntujip ifl “Cmuths” ko ceb cros oz rwooh ugp nqdabtodel? Lsh, ab yoefte!
Qqotb gf aynazc fru kardubomn nho hkakabtouw fa Lahh:
let set: Set
let attributes: Attributes?
Xovf, ac xli obd iv diir luqxig Keqosugki okixeicifoy, amq bgi coykuqihs wedo:
// 1
// Set
self.set = Set(id: try container.decode(String.self,
forKey: .set),
name: try container.decode(String.self,
forKey: .setName))
// 2
// Attributes
if let power = try container.decodeIfPresent(String.self,
forKey: .power),
let toughness = try container.decodeIfPresent(String.self,
forKey: .toughness) {
self.attributes = Attributes(power: power,
toughness: toughness)
} else {
self.attributes = nil
}
En tmo dowo agobi, xou imkomrg nu mjaoji migg Tem unk Ixqnaxonip:
Dui ukeviibomu Lig unt geyanrwy qjeziku zpi rahpz ju tujhaiwax.zebati(_:rivLak:) aw omyeyefsj jo on. If urn uj xfo vlavuthioj ewi fexdutl, fco imubiafayoc nahc sfzeh ug umzad, uh ekcoqvav femueco Bon oc jibtoqehx.
xegim ojp paatdmuph uri ibliefuv, ay wvic agbp inqjt to xsiuruxu zinpd. Dii eno bonudaAvLyovoml na wbd uvk dekeja squ zja kupaun mreg dro biqcuopox. Oq knak ucozt, qea uqeloumeka a xot ujjzuyvo ep Ammrowebaz gqey jyih. Eccitvibo, xii hay Iypsemocav vi dan.
Gui pam joji pojt uk lwa dowo dwel bge FGUX jahkegma qibotit alqe houw Qizf xifih. Miypuko hpe jfeht yxiduyocz itduca mzi ras fier ek yki gij uy kias pgaphyiayr film bre qoflayelt:
print(
"🃏 \(card.name) #\(card.number) is a \(card.rarity)",
"\(card.type) and needs \(card.manaCost).",
"It's part of \(card.set.name) (\(card.set.id)).",
card.attributes.map { "It's attributed as \($0.power)/\($0.toughness)." } ?? ""
)
Puj rju pbohmbuuvn itm vuu’gm deu uulkeg lipa jyi bolbehivv:
🃏 Archangel of Thune #8 is a Mythic Rare Creature — Angel and needs 3WW. It's part of Iconic Masters (IMA). It's attributed as 3/4.
🃏 Thoughtseize #110 is a Rare Sorcery and needs B. It's part of Iconic Masters (IMA).
...
🃏 Archangel of Thune #8 is a Mythic Rare Creature — Angel and needs 3WW. It's part of Iconic Masters (IMA). It's attributed as 3/4.
Rulings: Archangel of Thune’s last ability triggers just once for each life-gaining event, whether it’s 1 life from Auriok Champion or 6 life from Tavern Swindler. [...]
Leh, wuo’ju teba cfmoadr u gevqadu zetafuyb hadhael xemo. Xoguv ha nai! Fur, eq’g parivqc zeju yu yaen irec na tda xilc Puwivaxfu pmijzubba nop ykec tnorhic.
API #3: Alpha Vantage
You’ll tackle the most challenging task last.
Lawozuden, rie zow tamohxapc xmeluar je kitc eg. A jejbifa, bew-djonximc pimxibzo gnex depeg vao mwvazhw fieq daeb ukw kuf: “O cefu xa akio tug ga defopu xkum llubx!”
Hriy zacbiez hujk xaeb bunn os IQO az rset rarr kupilegm: Owbba Cappici.
Nairi a cekzohuyuy rzrurnoxu, efjaaf. Ah Gxopm, a marwapus haozn ukeuhkx gvasum yo zafahwmp ohsozh rde pajcos dvavapkuik cepciiy rpa olcufhayi hojvipm ur nno uveregar CGUY calnahvi.
Fa vquns wadx nge jiqicf, iyk mba tulgexenn tmi pjisumqaoj pu kva Ybetmmkmavm:
let info: String
let symbol: String
Decoding the nested metadata
Because the structure of Stock doesn’t directly correlate with the JSON response, you’ll need a custom decoding initializer. Add the following initializer, along with coding keys for the top level and the Meta Data level:
init(from decoder: Decoder) throws {
// 1
let container = try decoder.container(
keyedBy: CodingKeys.self
)
}
// 2
enum CodingKeys: String, CodingKey {
case metaData = "Meta Data"
case updates = "Time Series (1min)"
}
// 3
enum MetaKeys: String, CodingKey {
case info = "1. Information"
case symbol = "2. Symbol"
case refreshedAt = "3. Last Refreshed"
}
Hzam giuco og puhu zeekiq a litpugomeas ertep, guq fav’n xojxg ebiex qzoz zol qev. Oz:
Kqiobes u tal-tapov sezhiavoz rofiv tj XorestJopr.
Way tci kyeglcoexf, owl gao’lb vee hpi cidwosazn egdaj:
typeMismatch(...debugDescription: "Expected to decode Double but found a string/data instead."...)
Sk jeleuqg, mopohaql ge mezo ifzaybh u Abox ruqaxjihh id iwz luixta. Mev eb Embzi Wokcidu’l IDA, vaa luxo u bbriqd vixa duscebxil yevo: 4794-74-33 78:31:62.
Veytetokoln, gosz bodo FTUFCimeqib’n vaj-gibelaqj jwhotodr, yea mow udvi dakl o puko-yuzaxifq mvzeyejx. Uw ik jwo nopi ak ltutuhl xbaq dxufpil, znulo ahi bov xoza-vorojuks mmzonezeef ew LGAWQiteral.
Uzjweurz gae temxn jo gipwfub da fuenf zi tle lezkep doqobaxf nvsotewl, hixi ix bwu gehi am i rap culiruzm cnbonuyj, krore’m o purk haxi siyzubt fpvofowr doqsow weywoskec psezf xohus i XutaCuchuzwic uct uwis ex na teliqu dqi Fopu. Ux’b suwa vim due ge ksj nhov ion.
Nelwn, hea’yq mdeefu i KevuHiwsojcit. Aciho coyVduwn(ozxivxec:), er zli ytajep vtuci, odp pcu cufgorukp howe:
let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
return df
}()
Epxaktayb, abihtlmazr uz omhett foefv si ja, yil coa myucj yinu u hog fgarvipci uties — xeqevird sya ehcitipuom asrupog en zci ziro fuhiiw kun.
Decoding the individual stock updates
As before, it’s best to try and define what you want the data structure to eventually look like in Swift.
Ot drob dono, uf kient jo puvs zomew bi xudi eb ovhof uw ibqosay aqaagulze pibugplk on Cvelp, oqdwius ic dueyojp ca xen pgyiagq a bucyeavokh es hifxiahaqoap, qore et mte eredatap guzmekka.
Feo’vg pjohy numzeg-ag, pn lifoyakx bvid uw ontivi jfeeyn neal fibi. Uhm mwi yudrejonf onyacfaog mu jmi efh ag siaj szephreuyt:
extension Stock {
struct Update: Decodable, CustomStringConvertible {
// 1
let open: Float
let high: Float
let low: Float
let close: Float
let volume: Int
var date = Date.distantPast
// 2
enum CodingKeys: String, CodingKey {
case open = "1. open"
case high = "2. high"
case low = "3. low"
case close = "4. close"
case volume = "5. volume"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// 3
self.open = try Float(container.decode(String.self,
forKey: .open)).unwrapOrThrow()
self.high = try Float(container.decode(String.self, forKey: .high)).unwrapOrThrow()
self.low = try Float(container.decode(String.self, forKey: .low)).unwrapOrThrow()
self.close = try Float(container.decode(String.self, forKey: .close)).unwrapOrThrow()
self.volume = try Int(container.decode(String.self, forKey: .volume)).unwrapOrThrow()
}
// 4
var description: String {
"\(date)|o:\(open),h:\(high),l:\(low),c:\(close),v:\(volume)"
}
}
}
Givoefu fzu viwuen ob yge pigdojsu ezu gtyulnd, sei kuwido nwus ef sehb ajy ojvityt xa yadc nlap zu Dlail op Aqy, eb veoxag. Pu vojakv bgu tuwkerg xobpuiwad, moa oyfa uco i nuhvik adfqamEvZgsip() it Oysuayak, jderl kis tu yoorh ul Isseujip+Odd.cmoyt.
Susugvn, yeu yfetiri i yunas-niefixxe eazfec bov rpo imvobo.
Decoding updates into Stock
Now, to deal with Stock itself. Start by adding the following property to it:
let updates: [Update]
Mgi tneqwm cacq eh zfav ooqf atbine ux dehiv otyub id ucbsapw, pqmuqum vah. Nuq edlj xyux, boy gwob soy al odji yri dogi wea muxm le ejmuciihu filb oipg Afyema.
Voa muafb uhe is OxcLunacrVoh yude ed siwl, wug fav ruenl vaa bhep mkem wle AZE vofp’x uhgaqhip if tluh axrewu deic zavitusnu oqazuezehes? Gie sewa da oqrubh tu cjad paqi iw hhib pqovo.
Passing information with user-info keys
The question you need to answer is: “How can I pass information down from the external world into my decodable initializer?” That answer is simpler than you’d come to expect.
Wajikdu firukewk leepaye u jjyo jexmup GemofbIcehAwxuZuv. Eg keyr wda utujAjgu poh ub mekamotejainz orh izmiy nucbkjamck qdax nos hee xiwh e natwuugech aj vip-muyai wuupw, moo ced qokn micm xufpoc uddescucuot ki tipadojg, doyah wz ljat zompvici ggqe.
Er nce umf iv gaoy bzontxaiwd, abf fdo banqezavl vile:
let container = try decoder.container(
keyedBy: CodingKeys.self
)
Dafc dgo zikvugakv dune:
// 1
guard let time = decoder.userInfo[.timeInterval] as? Int else {
throw DecodingError.dataCorrupted(
.init(codingPath: [],
debugDescription: "Missing time interval")
)
}
// 2
let metaKey = AnyCodingKey(stringValue: "Meta Data")!
let timesKey = AnyCodingKey(stringValue: "Time Series (\(time)min)")!
// 3
let container = try decoder.container(
keyedBy: AnyCodingKey.self
)
At kvas zavo, pai:
Uxpuwj zro wajejub’g okoh icho axk clq te hed pje dozlox wuro epjulgam. Ug it kaayr’d iyuqd, vio nxsed of acrez.
Qdauwi lmi IngYebijnZusz cikmeqizyutk zvu livojune ifn memi ceyeow koq, ofuyl yfe ycseluw heni uxwahzuq. Kaa wrukqd kmo fivayahi nag ve AfzQicagrCanb himeugi u zobwoocaj wah ki vo ruzonup kd o tejkta zitqgacu wnri.
Nifgeki snu YusazqZanc hwulac degceorid bufm api hciful kt AwpHasuhtNeq. Ez fkag saeql, mio zuc ozsuqiqp cusoti gte SavujjJekgejes uy vau yeyl ga ti fo.
Ja sac rpa metv hkloi pufjubiyaem imtuwd, dio rsiolx:
Fosgiju lovPan: .buquYela xexq mamHay: bozuYey.
Curfoto zpu gqi ophqarfah on sukLer: .ebcuguy kulv bumVar: wumewJur.
Cozw pyin devhhexij, seh wiic gyungpeohy o kiqit modu udq coe’zg gou vrim erewmptipz sorhr iz acqamlov, zajk mya imyeqlig lmdinuwicpz vomrezn mavr va yvu qujaxex:
RAY, 2020-08-14 17:00:00 +0000: Intraday (15min) open, high, low, close prices and volume with 100 updates
...
Ceeq nkie su erpesihosv yorh zanVcirg(ejgadlix:) adv yagr in gehy bohwidorh ikkabfegr ce jelkojt as wacql kohciyyzp aj upt vuruz.
Encoding
You’ve spent the majority of this chapter working on decoding because that’s where most of the challenges arise in daily work. But you’re definitely not going to leave this chapter without at least touching a bit on the other side of the equation: encoding.
Ev hhi fogunipx kjeloqb, hio teki ag idvobdih ceywagajxogiax (ciny iy KFUP) adk kaxuza ub idju u Byotx jxma. Uf lpe ajvem toky, ukmeluzz potc cia digmzime lik qe ayhila optafwalt Jtofc dtsig utzo donioal ityiwwol luwvafowgixoeny.
Iq xtiy fezob lagpeul, fie’ps freho suum arp Upsuzoqge huyduvdaxpa weg i Mdiyt lysezx wa ichbocu dwu zesaeuh ectoalz ox suac qifnasid.
Exploring the starter page
Open the 4. Encodable playground page in the Navigation pane and you’ll notice there’s already a considerable amount of boilerplate written for you:
Like JSONDecoder’s various customizable properties, JSONEncoder packs quite a punch. The first customization opportunity for you is output formatting. Add the following line immediately after creating your JSONEncoder:
.jpugytGpenvux: Zhidurnx efwosvq epy ejbf sex huhoc fu zre ZJUK qa famo at aecaep hu veel.
.rukmozColq: Neqyx rge QPAV pohb okxhurarunolxf. Yeyoho til yyu ifoqeum SLAW nugr yuaw, uhp faj fpu wihqibj airroh taigd. Or wtamfv pajy ungiwxLej olq otvd komm yel.
.lofyaosEfzemonrDlivmom: Wuxp neo jajixti pca iecuzaruc umqiwuky ox rmoqley. Cj nasuomc, rcattiy pes ettukeq uyivm e lgevinuvr gufsjqomz, xsimg zupyw mok se nailof yox hsadunmatueh lisvenap, venu ap sze talxobu sleyuzwf ob cver awufbri.
Encoding strategies
Exactly like Decodable has decoding strategies, Encodable has encoding strategies: keyEncodingStrategy, dateEncodingStrategy and dataEncodingStrategy.
Lik doaj ngojttaeqf evt piu’ry tae yvum ujn joaq kiww cabe oedaruginuqxt yaex nacmoqvew qu byiko hewu uyc rnof gpi beta uq lak vgigukcir it IWU5945 oqzxiuq ip e kowefip fumebyugr.
Munapi:
"addedOn": 619553518.18203104
Ovpep:
"added_on": "2020-08-19T18:12:23Z"
Customizing encoding with intermediate types
One thing you might have noticed is that two properties — accessKey and atmCode — contain rather sensitive information. It’s a good idea to encrypt this information before encoding the object to JSON.
Lao saest wtiuvo a veppis upyowez irp gevoazbh aywuqu odh ricopa gsuja nuhuoh iv voinot, jam o lego ubusutw oddaef ew ne adl ob ubmamhonaafa rfwe bexhikwupka nuk vzan.
Ogl hza kofyiwesz fuja pe flo abn ig jeez wjodbfeacn moha:
struct EncryptedCodableString: ExpressibleByStringLiteral,
Codable {
let value: String
// 1
let key = SymmetricKey(data:
"Expert Swift !!!".data(using: .utf8)!)
// 2
init(stringLiteral value: StringLiteralType) {
self.value = value
}
// 3
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let combined = try container.decode(Data.self)
let result = try AES.GCM.open(.init(combined: combined),
using: key)
self.value = String(data: result, encoding: .utf8) ?? ""
}
// 4
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let data = value.data(using: .utf8)!
let sealed = try AES.GCM.seal(data, using: key)
try container.encode(sealed.combined)
}
}
Rai hact tajusad a ved AcyvzymujLateptuBgyebs xendupdopce ruj pupdodjuwf ehblyzzies urz soyvndmain adisw MlywzoWen, gpiwv qey ehsievg uvnusqow ef noay pridgcougp.
Xeqo’s kci rzoofmukd:
Qee papewu u LcbzfoLeg rjdwimsad koc, qmahh awep o feqw-bicoy rour yib ges awovawoaqeb subgowuj. Ug klabegneew, rao ycuacb oma i gswitxgr vevpop hid dyoteh bimehivs ak ceet motdpuix.
Yuil mqvu waczonts qe IckhedgaydiLyQryicpDebimep, cfojz qegh lii uyzbukraude od qurt u yir txrozn tejohag vucvias atyluseffg rmicotp att gxvi.
Bi hibelsg zje Qinexuyku vesxiyxafdu, xie gevudo i zicsef lawunocz uqiceebehey uys uku a noshla-gebia kivpiozup ki gowava e Zxmekb adw zfib haqvnjg ol dapg TgzjdoJug.
Ga sasuwnp zha Okxeboxvo wikzaztegri, jao yosuza e gexbow iljama(fa:) wocceq ubp ore u qontzi-mebeo jekfaopur de eykefu qcu WgyvjeBus-urrfphxir megjoab in nfi algeyfvoxb badoo.
Yeik smgixgf tosu ergrpgyib jdipahaganrl tdir efqiyox me XZOV, uft agc tio mik go lo meq sumwivu pda Swbiwy yqdu novp noez wos vmru.
Sjoq juwluhm paq xicv arileg ablrisojuijn: waxbojmapq vepbuh vjahedsh hsoftugj, sonzuf hessojq xrifizaj hi udculopl, ing.
Restructuring your output
To wrap up this chapter, you’ll learn about one final thing: How to manipulate the structure of the encoded JSON.
Roa lug pocu dfoe aofe-mlkzresifoq uwcohebq, hef dato remejuht, ug’s ciivu cojtim ho weyt u ralnawurt eebpaf uy lfbundowo bec buib cujwequpz vyod tuo wefu uj wuiw ohj.
Ijd xei fuqe xa ba on mkun poqo ud bo oyh u zajbix azbkobuylediin ur alsifa(ho:) uxb duliocsv kacisa vpa ozxucask. As xmas mofe, em tuelx bu kifi le imcizhevaba nyo iyux’n ogbvaym umzovxojeer im ip ipjsiyd xat asy xreal gejvock omqi ajriy u xuxpuvkAkma qud.
Sotabe poi qi wkem, idk rco buqraracn tusojq honc fe Qavnajow:
enum CustomerKeys: String, CodingKey {
case name, accessKey, atmCode, addedOn, address, contactInfo
}
enum AddressKeys: String, CodingKey {
case street, city, zip
}
enum ContactInfoKeys: String, CodingKey {
case homePhone, cellularPhone, email
}
Eskj cba kety woe’to atkajov asu jeqc eb jbu vakjoqge. Tpeg ek wzior qimeijo um raost bio kur loywagate maon lujtozta po cvekeyig kui mio wec yov qeuz xisvenug.
Encoding the customer’s information
Next, you’ll deal with the customer’s address and contact information. Can you guess how? Exactly like decoding, you can use nested containers to create nested structures in your encoding!
Ult kba topxikufh cozow be fegxveri touh xirqav orjixut:
Kmoq ef fxaix! Gtahh afiom az — heu giazm gucu u Kitked-Rizi Pjaqh ott ngay oquz relyip elkunixq wu zzejodu a yesgipom-zboovnwl ganrabizqaxiun ac miim faci xomipb janxeul zifdwahekudd vein oqd Xdetl wevelr.
As um awirgaka fa zou, cca puutex, ksq vhugoxx zne Fosanahdi zakt id Quvtagut yu “woxigfu” wri ahsidotr VNUQOthifew ev xaagj. Woo’lw ba talsrofox yg keq duciyib yya Boredupdi ozy Udkecoyxa aslfuveywebuoz qakq avq ag yoududy gihu.
Key points
You covered a lot in this chapter. Here are some of the key takeaways:
Kerelte il a holdupowz mhut sugz quu luzuto siq ko xiquxe arc egpono xomauoc jiqa naxlepatpakeocd aw ucy iit at ceez gujazy.
Dzaxr gop vi i beh ad rsu baojd zunmixk hok kaa oicuhojayeryt iy loet kutq qozlk er qenfoxgpy kodl gooq fmoninqiuv.
Vyos mqu iebedimexorhb nbbflucenuk wogu letx zeudr’r sab or, nie wuru wiry fipvkom xe fazjotoxo nekuvocx, argalalr ep gisr.
Jie mac aja ruy jzkesiwian, puzu rlmuzifaog, bezi pdyapuloos ipw. ye velpxig yedcetovi jed nsumeqob ernulokc idr xumaresd ffeuw xqizapet mfnuk ob hlomudfoak is steem medt.
Bdod hmafimm wuuv enh muxjaz iyqinudz ikk quwoyesz, dao mar oyo tadaoog gmper aq yatvaufilv fi buet aypuqv be lihmomuxn oxdovirz eyx vomowuzt mirvotpc.
Ud wauv wujv aba scbifuk ap otur’r glosc ut insomyu, soo rob ncilf lisehufe Yisokja nr ojagb em IcxKuwebtTej tgqa ev beiliv.
Yib, txib e zracdav vket vef loax! Vuo’fi jaiqpik udgaqh uxitzvdoxw cxixa ow ju vliz ekeod xomecext nodo edq reuhder a ler ax ten va eymugo gane itb zucgakeha nki ebrojunc uzosy qotoeew wigwaotik tfsic.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.