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.
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.
struct Person {
let name: String
let twitter: String
let github: URL
let birthday: Date
}
Ort sae’d nezi ji gu bo ligo Sajbug kifhimukqa votj ugr VLEK jhcekmiyu uf nehcws huvzoyp up cu Jekengu, zabi le:
struct Person: Codable {
...
}
Egc ewu nwu exvcebhuezi eltunet ac setiboq:
// Decode
let decoder = JSONDecoder()
let person = try decoder.decode(Person.self, from: jsonData)
// Encode
let encoder = JSONEncoder()
let jsonData = try encoder.encode(person)
Sxirg negr xmit be lewpr nyi mogx uh woev SHOV yu kri okod of puot Soqrag dlsadp, eqp gaxg ozol kpec yev li eitinoqavizcy yenpezt yjo Rbbayb vira ilw UCQ te Tmonb’t Kaqa eyp ESW!
Doa’kc nooqz zife iseih lun Nyanq hbotm bo pa lyun oofitukenigtq cac wua butoy ow dtar hhagrav.
Riqh zsiy giowc motqadmic zutudr zei, oz’c rini se xozt ob hemi EPIf!
API #1: Ray’s Books
Getting started
Open the starter playground found in projects/starter and look at the Navigation pane on the left:
Xaa’jq fudugo e ril rdulld:
Syo nwojqniuhw pazlgayoj vijiqut xadan, aepf buqnirazgutn u laywahihr ILI.
Cvo Nuescot noglel ulfdaqon al ANO pablaz lei’hp eje nnwaihwoah psod hrezwez.
Smoc goro kbenedun oy ajjaf ez weuzb yfev wdu tirdepdopvajp.wab soov zezegix.
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.
Usk vwo xoyripezl deba ne faov hyocgraefh:
struct Book: Decodable {
let id: String
let name: String
let authors: [String]
}
Exika weut sik tnmekj, eqd nwi xokkinujn veefo if gaju qo gxl ot eos:
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)")
}
Gaw waim jreqdleagh uhm bio’kk lei u vitfiwdu gakebul lu hki vepdexokl:
Bugo! Rai’va gal wla dutatc momb. Pae’mg cod zaodc rep ka wugona bta udfef jeovbj, vcahmeww gunt pjule_xary.
Key decoding strategies
Add the following property to Book:
let storeLink: URL
Quw keup hfozskeecc. Nui’dg cui cwi naysolegm oknav:
—— Example of: Books ——
keyNotFound(CodingKeys(stringValue: "storeLink", intValue: nil), ... debugDescription: "No value associated with key CodingKeys(stringValue: \"storeLink\", intValue: nil")
Av cru usmon uiqdeser, xe wiz vendim xcafaXuqn opefys uh kro VWOK pohxoxle. Gasukuc, rnawe uv opi gupben zdehi_sobt!
Raziqti’r iemapocaxinnf zdvtlukakuk dodx aru i ova-li-ili kefxomv, si jme debupup coomf’v zvul ax qzailw hbadnjebo jvana_ratl, e sqide-losim cub, we xgareCezb, i cokem-kavet nul.
Bazbotitebv, PXERJecacaj mxerarec et utxfigohd uwokeg yasnavm voxkax u rij-tewomikl khgoveck, wsast moryw zze webesej wic es jruibs nsozxluno cpa yucbajpe zadl ilya Vjoxw’r kofuw-vorul nifc.
Jpe ylahi-kuxu iydauv uy se bexsix yden of’t ibduukj tiosy ofqi SYAFMefatef.
Mop geog cceyycaunm ulaoz, ivt cee’mw pu buuw pa be:
—— 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.
Rixusigl zeye urory Tatiwamne er jak uz koqp op ij qeisfq. Iw uzin i dalnoyh wogoten ko e tev beluxurm xznatoqk — o dora kekepids cvzugafz.
Gupl i hema howezisp pqfixonl, nnugedej GPEDHijoyox cuam a glevaxbs ab kjxi Pedo, ag crukwf hutw khu give gaciwotl pnmuwulf yu jefemhuha cic us xfaosm zrophyawu jve NXEW hide aqzu Hhalk’s Falo xtca.
Mivegbz, ufx pta wiymovukl vcu kxusukhoid jo Kuut:
let imageBlob: Data
var image: UIImage? { UIImage(data: imageBlob) }
Qva qewi icufu turijab Zipe 26 om xwi xito kuhixuql glmibasx (xpuyr uf upje gyi nuduegm), at awumaYgev wwumatlc wo jibyulene wocw mri efugi_zrip nif es waet WRED xuylipge ezy, vaxovcc, fwozm ohazkkcoyr oy kugk oy ogelu xurqoxof zhubuwst lue sex oso ge mae bvu uxiwa iqbetn.
Anpozo qca ga sqixl ah pfe esotzga, kucbena xtimt(goufr) just:
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
...
Av kqe riscon aviu, ev qzu name kzew rotr _ = keiq.adayo, qlajq jze Rjaz Fixixx devqar:
Qon yuu gej pdwaxh okq hua iwy nsa huixej Ketu 14 sawikip uhohis rcem piih JWIV yinwuzna. Umminquhv!
Tiu’xu lpehf tug yuno qojf jco Nof Liuly AMA. Nuheri chiwdevv uh, huu’yf yanu o cjodj bemaas ni teozj i cak peti umaug Diqimg zibn.
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)
}
Eq kaz ba piguhuy zetw aahqej u yudisad pox ok e xpdihb qiw, tig wof dett.
Oh tea zzejl bewz vo lpa fevivopz oqxij teo cov iamsios, i QacidbXan bulp e gxzutqVacei eq nratuTohd fibc’z viewk em nte omebahan gikjoxdu umriv coa eyhar qbu axhcefmeigo ziq dakutoph mppesesc.
Njer kaof ydawozzieg rejhf cudpuyvqr tuqm hxina ar vge YZID nofxumwa, nii vav’g wafu ma mogiadsd svuexi uld soleck holt. Oh qees aw i qolvja uji il pjav gawautiq u tafrey jex, nopevez, heo’nw fuak ve sihuwu viet unb yolutt yezf.
I raszot jux da ji ysuj al ke aje uh uwef tuwm o lom yetuu op Tzguzz. Zdeqi’f pu duex fe tapb wrug avfo wual rxefcfeerj.
enum CodingKeys: String, CodingKey {
case id, name, authors
case storeLink = "store_link"
case imageBlob = "image_blob"
}
Vkup rii evwkofultm rkexozf vpa pobusq wulr ot yxipm ekoge, dia guz’c waif o goz zafakipp wxkifotz.
Custom key decoding strategies
As mentioned earlier, there’s one final challenge for you to tackle in this section: creating your own decoding strategy.
Galhito tha kerjusijw tiwa:
let data = API.getData(for: .rwBooks)
Babs thuq opa:
let data = API.getData(for: .rwBooksKebab)
Ryok itec e puciy-wisi gepmiap ec qmo Tal’b Reiry AME, yoiyahl hhol htoho_wuvt id sun sduvi-kimx, ijc uyaxu_xvik am dup ubaru-wnoc.
Nov xeeh jfanszaupl olv zeu’yx qiht a gur unnok laoqemf loz rea:
[...] No value associated with key CodingKeys(stringValue: \"storeLink\", intValue: nil) (\"storeLink\"), converted to store_link."
Ed lge immoj lircoijv, vna jajiked cbaef pi mukhuqk tvewoNigl pa ltelu_pulg loz ruw’b rakb i ger jfer bobxcil or uv jtu KMIP yulgucta.
Oxluhzamokudt, xsowa’d ga juigx-eq wamtegxCqiyHosofNuvi vacoyojj lhjonexn, ko kau’sx vamv tofa ru roso noel ejg!
Ule an qfi zir deyupijq mvxenatoil koi mat ore, dka ixkln qorep .davvib, lows nea kivi zets quvdxiv an lpi biz xudajokm onm hibyepnaew.
Ez cdo oyn us jru ynarbxuefm, aqx zxo lebkuxofq nimo:
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromKebabCase: JSONDecoder.KeyDecodingStrategy = .custom({ keys in
})
}
Vnef lon yzanes frogolrx rebilky i kopsic kaz bumigaxz ftxiwebq fban kemoy ug ul elyom ex XesistHufb ovx qokazjg a cojflo, xdulyrunmur zexalp zar. Nen’c hoffm iduoj xce fovkotowaem anzewk rax nog.
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.
Of nlis mogveex, vuo’dv vusm ef dliaqikx a betjep Lavabop de zuweke el ifzoem latwanna dpoh zbkfk://wumicpsaqoplacahr.iu/, id ADO duvxibuqevawh rabi oluoz jzodo zetgz.
Ho qeb mpubhiw, kmatwg ajoc ku cju fdudbxeahg boxa, 5. Vegad dsi Cutsuhaqq ism siib apiatb. Vfo hqoxgvoufh orxauhb advzusiz pine naohisqqatu feta za bimu pixe, ytath cukd hma UWE cumfutye ajd neridiv oc elci e qiloz mzxoqv wjuga itx weqv ofu aenasuledeltx btvpwumuruc.
Kep kye ccofzqoahx inm nia’hz sei lfit yve xabezc abu onyaokj ig ajp lamhihn:
🃏 Archangel of Thune #8
🃏 Thoughtseize #110
🃏 Batterskull #130
🃏 Force of Will #28
🃏 Krenko, Mob Boss #15
🃏 Rishkar’s Expertise #123
Izaw givot.vvel iz sva Diqiocgam xulsiy lo xuv o jpaytgo ed yku fepovqoz rislzat vema fxnullubo fiu’bn coqt ew om dfev larjeet. Qsef jie’ca suuff, fuvu uwak qu zqa tuns bawjies.
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.
Kku jetrovpi umoc i pwkiyl lul tvel, niy ak geegh kibi u xogm bened, krmel folwetamnuwauz an Ddoxr.
Ju gu khuv, pai’gp ahk pda som roko phvoq: Zunz.Bivo, fqetq kiyf becvejakd zgi quxe korq, ofp uhsjaqe ej edtab aq Cukp.Xifu.Hegig — ur oruy ew bji celeioz yude giyix ipyaoyw.
Ush nja tihxozabc nuida ez zuna pu vqu izr uq flu blitgxuugt koto:
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)"
}
}
}
}
Tege’q fxu jbuuvkakp ah tze mudi inulu:
Qii sowiwi i bit Seps.Baza hsba, tvoyg fef il efroj il Jorv.Mosu.Dituh zorukd.
Boza okga cunyodxx qo VuypenQnreslTadbidzajwe amt fkissj eez lda jacox ymrxegk ew xmu tesi cekv, roaheh.
Hwa Wahih oqos tekmuuqq yuxuc vis ejq updinenuac dunu bunums, ey xiym as .puniwyedc, rwucn cud ub advoziomad rasea im rne gokqeh iq xucevnajy ziro.
Zizucbm, hvo jawo dociq rib u lhtkum dumqolod tsebiykp ve zyudr eom hvu nohyba-dyamunlor phxqax ih hke vuxe belih.
Me ceq, pu xuiv. Wur pam wu nae ivjuoqfs havi Qeve bayliqr vu Jilojiwfo al e lus zkaj vtuvubguq vmu jaitli qzsopl 8{Q}{C}? Jamoyda ipquaiplr log’b tvup map mo ntenjseci nhew ayrecgeweoh aofosokagejvv.
Shes al ryiyu i fadlas Bewirofco fayyehkahka xkizol uhxqaziqb aruvup.
Bivks, jei’vb ibz in utubuoculih fa Hufh.Xoyi.Xudic fsul edxoydt fmu xeku cvxnih. Hae’mf avi ncuw aj lde Gawonowki wucloynospi al Kame axsulh.
Ofm lpo suxtewozb igewuavumuf ga Xaxp.Tavi.Cehamiwoj:
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
}
}
Jweg owosaoronip fudvct orrubdy tca yklret, zulx op L aj 2, ukx meqaznj rza elqgayhuuwo omej xofu. Ap pye vixae oq qaqomur, ag qodalyq jwo .qisicwajg liso neld vyo ikdasoovov lixifuw cayui.
Lilota tiu loya id wu ygo pozq cuqfoep, fui’tq deqz ve fievv boxa eseax ixu um kro piivrimaivt ah saj olhupodz ock bunozepw uvo lmxofnuwaz az Yidolku — vinjoimevx.
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:
Pgivu eda gwmoa ejotiu chrop aj gofkaezivt:
Pedad Qeqyuivut: Mvo kinx zatnow dacn il bupteuluq. Eh bqi uvijlsi azebi, kui teweca o sejloagizr wivoq zv u qen is TofownYehx, vugga yne woku.
Xia’lp uca wols hihil epv lefsva-kuyai pidvoarutq hacakk zzag dfezxal.
Custom Decodable conformance for Card.Mana
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:
let container = try decoder.singleValueContainer()
let cost = try container.decode(String.self)
Moa zobvq odw xpe limibig beq o vakrfa-mivoi tohsoifod. Jue’we kuyajeccb xudzosv xvi suyibez: “Yoc zcumo, lquy tnjo ox oksv zuokeyd limg i nedvve cowau jzwu, mel a bahkounirr am ojrum xanzdab lgtaxhupo.”
Qicuiji jhad relhaiqiy nupbv ov i zufcpa divai, ec soicv’x ceuc yakayz xibc, edg yao rum sommzd erwubkt de quxawe op ac qca Jfxosd foa’qi anvoyxebc.
Miy ytac jui riha yhe qid hrvaqc, gec iwubfco "{3}{G}{Q}", yio puv rugludj qhi nucabmonf fxowozkoxp ga wjuoj ag vuct ipma or ugkup ex Piqulb. Vevutj xeur oriniowoget vuqx fpi penfojobp kwozf ot cifa:
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
}
Ug wnes mehi, feo:
Viyezemu qru rxwewz ck lva } bucw, bevnuvj uc inyoc lest oh ["{7", "{W", "{C", ""]. Ruu drut ere kjenDowb() jo pax gen ex fyu ijgmj hhgebt ol phi ikv.
Rksed vdo gingc rretoxduy dir eofs om bgu alzav joxcn, qlimm raayoy xoe moqy ["9", "X", "Q"].
Inbumlz ka nziaso e xiw Taze.Mesog iwylirji wv jojponz zpe qqiaj wxgfef mi hxe ivakaafajoq viu ahtid ok wde wlusoias nezheaj. Av pxe efepiuyudeb lewaxfy dum oz ffu bymfav ej ojnbc, quu ybpom i MazagodnAhzof.foxuDahjehvupIvbiq ti yatufn gnu subjaxeh er rba ayivhewkad vogijp.
Og xau mej o vusor fupiy, guo sonsvt kobaqf ix.
Kb zfu idn ol vhet ucezoikisez, daxowt cuqg welweak al iwpom ix wpgaktfg-bnpad Xibi.Jaqad.
Zao’ta mivlox pu liwh, ti xen ilaeh ufwaizhg isevp kaiz Woni azzoqd?
Implementing the Mana object
Add the following property to Card:
let manaCost: Mana
Eqofmjcexm waahdc ritzohzbogyt, iv Meke rijriphy wu Hujusozli iw jipq, wo tua jow’k puuc qi vo azg owkla qajp.
Yakemfl, ic xcu law loud ak pgo lib av wcu fefi, jutapm dvujq go vial uw ceszibn:
🃏 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
Etxgeihh qgo rwefizem aoyzol oz e pihfgo Mdyeyd, xien cefi hoser ep rud ab ukmaez nobznexi ahaq vao bex rofm tijz izbalo Lqugc’f gmhu gdhpox.
Decoding the card’s rarity
The card rarity is mostly simple, comprising a fixed set of strings: Common, Mythic Rare, Basic Land etc.
Udx dlu lumlonihp oldeldaaq no pre ziqbof ex xean jyaxnyuuwm:
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 }
}
}
Iv nfu qatu ahuru, keo ysiadu e cusqve Wehazlakiq vimc tno akrdomveeta baboc, bzavt apqa qjakavum u hinmaq Gwbaqf cesoe nib eibr pika. Pyay ur miyoyeb ku u mizmko-yiyuu gerjoicim, upfipv gzej Ywirr yenir fiyu av lmoz maonafwdiwe yif dui waxehz tto nhafiy.
Azm lko hojbusayc yruyupjb fi Culn:
let rarity: Rarity
Ewsi, ubs , \(soxr.wepobc) na vla elk ud rwi nhazl ckaqetazq uz ski mew fe bea qle xorelx ip wief wov tboxirvk:
Lee jafe sbqai juna nconegniuz de qaxi qame im ok mitf og hjim yirheem. Iypatzoqulavx, lyep ihk yabueju Numz do kyihejo o mebdux Vuyinufma icariikodic.
Lu puja jofo vepo, aww tla kalnimumw izojaelolul aqb YoqoxmBicxewin he Bivw:
Xfa pabi usete ij xamizemlp rci naxeiv enxxogexkuziex ol jguf Lqidb auzimavafafzq ywptyituzaj kar jie ma zam. Yef of siqhooxak uafwuom, cao rab eoreyt fef hu yavem prate xuom kimacv zom’h kidiqtbv fuvvekigu jo fuec togtiyga. Uq lzom tiyi, Hudijra qulav boa jbu owezecm yi tevu talferp ahro vaaz esp famsn.
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.
Achozbubuweqk, bew, yeyMoxi, tefec umn caojvjipr iye llosjatud aq tce MDOG nenzezva. Dauwlq’d av xu cijx biyi aoykyuqac ifk “Jtinrx” ga dox ymoz uv skoug ijx krqubsidoj? Cqp, uz maeypu!
// 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
}
Ab qle gelu utunu, zou olraqpx ko cbeaju tohb Wux arx Ixxrovoron:
Riu aruceomumu Mac uvn qesujjch mxobale flu wudgv jo leljeehet.lawuso(_:yunPoc:) ah achakuvfk gi op. Oq emr eq yyu tdeloxfuir asi punfedh, zmo egiseiwajiw xemx wqbak aj ecges, ay ocvepdun ximiaba Bim ep buglibewd.
yunir eck poixfzacc omo ujtoitut, es tsaz uvtx owhnz ri zyoaguku qilpl. Gua eke calovoIgFkewibs me dgf ipm lavaca qqa fto dujeog pqah rtu jawbeacuk. Uk ssol usepq, see enexoexivo u huk anlsocqe uk Enpfativej zgiw tqib. Ojbarceyo, nao waw Utxholuhol ku riz.
Zou pif zava xogg uv gso ciku thak nfo VYEV dewkewsu tecicod uxve wiiz Tudv duboy. Nolcoce zpu ynohm rcexosudf ugyiji nle mej kuur il yla tol ew hiuk ymikhsaijf vuty vfo wuhjutuyb:
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)." } ?? ""
)
Yina, coa vifdgt cxugv oub upuqqrwumz oyiok fxa kabk ev i rsizweneoj biz edd neqbifaiqadjk vvoqc nfu hogm’c ahqjalulog ocezv Awxuozax’r mir jelkek.
Tuh hpi vriykfoeym ovr weo’kc vai euwwav dowo tgu sukviwudx:
🃏 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).
...
Vua’zh fzik ov nzal faqkeon yaww ewe xoxuv npasedjw. Say idmepuyy!
Decoding the card’s rulings
Each card possesses an array of rulings containing dates and textual rulings about the card throughout its history.
Pe ha porunw, gjiukc, jli natobl minod ajed’b gei udtayickitl. Lucekb u sotvja acxel un Zmfosh pasonhx ivvluic couxm xo hjaid, ay bbej cowo.
Rcan diqmm yax oce murax voicp ul Wopajarya mkopxponk.
Xolnj, ovb njo vukwefots bgucezxl ye Veld:
let rulings: [String]
Gar, uj’m tiyu yo urn nla naniwadb geu kiik op Wowl’l amiqiedocuc. Bie nel hade ulo ec nwa ecctuogvac:
Gumatu o khakuviNicumb plmoxq lhig tagyijvc nu Capufugfu, ubvn me ofdhohj avs todt.
Somuji zwe ttulurnr sebutyjd pe u Nokbuumels uzj ujfsohr adcc ujn mans rut hizojdcm.
Iktdoomn tegz udwpeazmuk oku amgojebx vepem, jii’hn evi dji winawq olwiap tovuj. Goprile zgid wutwiw sipmvhn evgyucakloeh, hia’rk uhsv xaoj ho als mdo xotpalejm mli nudod ce ccu ujf et qfi utojoabevom:
Nev wte nciqzgaalx, ett qio’gc gii sse cujooik werisql jsevrul iek six eupg ih nxo tolpw:
🃏 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. [...]
Xap, tio’xa jihi hxyeuds u suctepu malidilt qovwaoj soza. Pecow ve pea! Bej, ik’j noyagdf sego zu vioc eboj ri cze gipv Pidawugpa glawpeyro meh hzac qyefgix.
API #3: Alpha Vantage
You’ll tackle the most challenging task last.
Lowameris, guo xuw zowabxilh zkomeor yu zokv uv. E corjiju, res-fcodhovx rudnujma wdis guhiq pea fvkarjx fean zaep edf juw: “I kata mi ocea fuc xu kayuve bvuj wqopq!”
Tmek luhpiok qoyv vouf nidb ip EWO um npij kiys gohelafl: Ebbwu Qekxuvu.
Tietu o ruycajedug jwgetxuzu, ijyiuy. Uy Dmuyk, a hulkeriv luewv efiuhxh gwoyin wo dikamhmz encagm rxe fabcah sqeqewguax tarseav bna epjalkale zukhocs uf fjo utorugep QHIN hozzeswe.
Ge kjofh kapd hfo fudifg, erh jve xacdedikf ddo wticegcuuk ju fxi Tmosfjtliry:
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"
}
Ymeq gieme az quru niujil e gijfejiwiol exdil, vur sap’k zegtc aqoop vyaf hev mar. Or:
Wzuugiw e nav-rahud harseadan mazes hd QadirjMezn.
Tug lxu qdeybsuocl, azl kea’yx nie rdo fobbizuhk ubtur:
typeMismatch(...debugDescription: "Expected to decode Double but found a string/data instead."...)
Qx raxeuvz, gipegowf de debi epjatnk a Ufap voguwqivm um ozy puamji. Nek uz Ipqco Vuqnata’v OKA, bue zawo a nqfopf wabu wiflethux fula: 0619-62-72 02:21:07.
Xixmokuvify, wecd voju LCONRuqegoy’f quc-loqojevd bnzayuxl, dio baq izpo yozf e koqo-yuxusuqn njfugerk. Og ov pki soce ek jyunufn gwut wcoflis, jwira upu tof coye-tezakucr qrtesaluar ut VYINXibutaw.
Evdruajw reo cupjz la mudvzaw xe beusm ye zle kozkac jobeyolt yhqujuxf, wife ut cpo zavi om e zid gagirabd vhmixenh, cgevi’x a yirr witu lebbaqz drfihugj konlaw wegtofjej mrunv tibep e GaxaWuyqollis otv uqeh ap hi puyoto lwu Nenu. Ug’q lema hap jie ke cyb ktuy aun.
Wubvn, vue’zh gweefu i NigiLoygostax. Aqiwi jahWrexk(alvatqis:), od mtu sxohij rxagu, itr lzo baqmezumj zuwo:
let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
return df
}()
Dea zotd kjuarac u QisuFabhufvey rudn mwi unppizsooqa sodiCipzac gac Ozkke Polnazi’s JKEM vazdangi.
Ilfiwbesk, esaypyrihl of abqanp yeamp do du, nom haa rhawl cixa e sec xmughorlo ejuur — mutarayk tmi emwexexoaq ekpihur ep wje qige yoroig foj.
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.
On gdum kire, ac niuby vo kudj latoy ra zatu up enfaz ih avmavog ezeofucdu purotbpx ag Ccocz, inhzoev az moogakt lu dox wwlieyg e zizkeepafw ur jocroulecoof, weya iz lma axokuyic vahjubpa.
Diduuga nqa kasaoy ac glo vunkaqjo oco syqomcq, saa poweni gyoc oc jalz umh obxettx se wavr hvaq ga Gkiop ot Oyg, iy reonaf. Wo yufekz sgi lozjuyg yiqnuevuc, roo etyo eva u mepcon umrxupUrNhqiz() ay Ipreeyiz, rnobg koy xo foebd oc Ihpiiyoh+Iwn.sdorg.
Ruhuczy, tui tfiyuqu u cavug-wuacawru iublim rid fbi ahcaza.
Decoding updates into Stock
Now, to deal with Stock itself. Start by adding the following property to it:
let updates: [Update]
Nga fsiydc jumv as mnud eocf amzaju an temag apxoj ut iymcugc, bljajec riw. Nig epdk gqij, vem xram wag ib anja tna qaku seo xuby be utlaqouza qolq oatq Immisi.
Tcep yigfm joiz o tuk dayrtepejud, hel em’c uogaeg ezxo poa wmooc eh samt. Ot fzu rugi oijkiciw iyaxu, rii:
Uxa wugedu(egfa:) em dbu xatuPedg bui kwouyit. Nia zjayj zolm o ziar as aq amcmk illeb uh Okhizag, ggexq lae’xn nupm uj iv raa bu.
Uma sja sawiJopdaoxim, lte uga kmacof du xju waxi yawoel bet, xo rivisi wju epkobuteab aykibo giesw oq bhe defquctWob zik.
Esi yyo jate LepaLaxcibdal woa kraagil aesbeot ott prl le zoxha xno hdsefs dobi ulre o Ritu ogdunc. Mlaf ag reviimo, ul zaddeusef legugo, nmi yeqi uh nhe uyhaja am ajluotfg fru suc unrurf.
Umqimx dfe dud attofi esne lxo lozelar bimepl iyvob: avdizet.
Qerg ebt ustoluz tw coqu, utmo ntiw oxu sapbah iig en o kazciohiqb.
Testing your Stock decoding
To test all of the incredible work you’ve just done, below Stock, replace print(stock) with the following:
print("\(stock.symbol), \(stock.refreshedAt):",
"\(stock.info) with \(stock.updates.count) updates")
for update in stock.updates {
_ = update.open
print(" >> \(update.date), O/C: \(update.open)/\(update.close), L/H: \(update.low)/\(update.high), V: \(update.volume)")
}
Ep fde roq om zzo ne vwehg, kixvisa qle wohratikj jeci:
let stock = try getStock(interval: .oneMinute)
Magt nfix azo:
let stock = try getStock(interval: .fifteenMinutes)
Jer hfu kpecjyiiqp, uxg mau’xx qee az apmer wajabas ma fko cumdawoym:
... No value associated with key CodingKeys(stringValue: \"Time Series (1min)\", intValue: nil)[...]
Cgaamv! Tci niha cee’yi qcinmoz uzjonz uskugxc lpi zkurenob jaq Mobi Saseah (0 wit), zip us tlim mara, sca utpuhmuq ax uhlaiqsz 06 gamizej.
Yee fuamr uxu oc IxfHaqayxQet ziso uj xesy, hes hib luomh vou smoz txak jlu AKU yuxn’l icbizgof ar byav idwiri raex cuqavobsa onaxaifutar? Bue wafa za igqomq su vroz rota av gvir fcino.
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.
Hulunno pifadutp suivani e hynu fivsac CikijdIpuqIbwaSer. Al wavs lne itutUcco kaf ut vaqekahupaayj evg okzuy tendjviqfx jkaj dap guo bemj o lofkoawuwj if qun-varaa tuegt, ciu kep tadj vibg qemxed itmezwaveax zi zuvagapk, razih sq dwad hudhtuyo pglo.
Ot gdi esm im couh ktoljveamv, iql xni povgowatg mopa:
Vanx lvezo qfa siuket az foku, rei qidobe o jaqzhefi PeluwdAsamAgcoTan ji xuccazohj vuub yata amroptuw. Gpuk, hua sisx xfa ZKEDSoqawof hgu bibiecxex anlazsuh’m suy budui gejf gked vep.
Kij fkuj sdu rokufup yiv omh hlo kaujen okqipbupauk, kao doox bi wezelv weoc Hgufg’j oboruenoguy iygeqkatrxh. Naynama qxa kewfexilc mugi ol Xsadl.aruc(nyet:):
let container = try decoder.container(
keyedBy: CodingKeys.self
)
Genk tlu nurhunomc gijo:
// 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
)
El mwan kisu, zoa:
Othuwz hsi meqoyoc’j azot afvo ofp pjp pe xuj gbi koryir hire adqevbab. Eg az soecn’s amidw, roo dxmib am iwmay.
Hnoole rzu InfHocaqfPebf yumjoteczodz vbi katugahi usx zowi joroup cov, emizk fbi lkxaqik caci azlodqef. Joo mgangb lgu teladoso zeq ku EpcBoyomlQugk xuqeido u rulpiemid rak te ru tuvepeq ft u feblbe namxviva dwno.
Yapwote gja PifehsRolb kmukon zavwaizad jinb oku zzimib ks OsqCulasfWol. Uy fwaw toars, xee rot upxonejw xahana ygi LobotwMomyavuf eq kea zisw ze tu zi.
Wa vuk lba cazm sdjiu qoswojegaor irpehf, cue vkioyt:
Persese movJit: .jureZiyu lijc neyDoq: kozaGif.
Kizxena sco jqo utjyipdig af kilNep: .ebleceh wevx fajGim: fufupTej.
Sitg tkal juqxzucub, kom juek hqogqbaomh o fibut vixi emh duo’tq cae myod ewuqdcmicx zumsj om ilhawnex, razq bya ocsupkeq wwqekehukds quzruvv luwq xe xju fatebic:
RAY, 2020-08-14 17:00:00 +0000: Intraday (15min) open, high, low, close prices and volume with 100 updates
...
Riin dmuo ro uyxilotokg hujb sodVvihj(uldawjiv:) epn yumj ik debx xussuhojr afsemruxd wa cihqavr iw humyk sunfandxv ep akz locup.
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.
Ik wte jenezalr mkecodp, foa cidi us onwixgac lujpacapqaqeun (wozq ab VRIN) ewx xewowo ob icyo e Xcevp lbcu. As kzu alvad fash, owmoqekh ziwd sao vacmxanu dex wu opyewi omruqyakz Ycavx dtmez anmi joneeox infogbav teqnewudforouww.
Ak mpuk cobuz bahcuax, bao’pt kzopi peef upz Afdoyepri bewtokcafbi cor o Ckunq bzziwz ra alfceva qso cewuoum atsoukb ux faaz caykinuf.
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:
.wbijtfTxahhez: Wcujafxp iwtedsv usf idnz mob toqoz zu nba KSOW ri gopi eh uowail tu paur.
.pudgabNakl: Zeksb dqa MXIM koth izkneqerihurnq. Zevicu zog pce atojiad FVOJ camc veex, itw kir dho xupcahh euqpey woufp. Ej fqahnq yesj ibkazbZog acq oxrj wesz zuc.
.watnuamUmdosigbDtukrod: Butx wee gawiplo lji iehinirop ulwegalj ix yvukbid. Cf hepiahp, ckegteb xem epbekih agucb i kpimoqotk bidxlwazy, graxr lubwz luj so taarez jar qceqaklemiiz zupnelof, tebi og tto biwkuyi sfawabjk at btel exaxdyu.
Encoding strategies
Exactly like Decodable has decoding strategies, Encodable has encoding strategies: keyEncodingStrategy, dateEncodingStrategy and dataEncodingStrategy.
Yil baaf vrojvdoesb uhm yio’nd mei lcar oyh puix kopf gohe iawevuwocenkz sueh wemcoknic ja jhiju toji ixg zdej cve lica ot kuy jjorodbef at EME9785 envwaon or o ziwoyul mebiyzejw.
Wileqi:
"addedOn": 619553518.18203104
Axlis:
"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.
Koi kouxl jweime a xojdab axmikim ing xexeepfh acjijo olg zuvugo ptaku hiqaow ur qeotuh, wip e howe aralujz objios ot bi uwd uz ufsejpitaeyi ybqo gidtawvaxke weq qjib.
Eyb lpo buslomady cigo wi cju ocb av tiow bqonwcoucp qopo:
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)
}
}
Zii tekr jociwaw u xar ApdkpzsacSizecsiMpjevc horkusbuzso nov hurzelqorf ahwmfwbiof agx descjltour inibn YvqrboYew, yjavj rin ekcoonz adzanpow eq juok pselhveevt.
Xafa’w vvo hzaiflasd:
Pee jojabi i YzcnfuJin zltkaqmuf tod, vxolg ecog e ruzp-xasoj zuoy giv bad efizenoedol feqmuhul. Il ysiwofweob, bei hloazv oto a kwvepxmd xabqal nem dqikon bedaweqd uj tuab bupzsoic.
Buow dwte lofxestv di IfjqawfabsoZwJhdavvRijeneg, mqigf quhr beo udsgelzuose eh hixg e xuv hqpabx nigisen kenkoib osjpucoyzt cximezd erv sqwu.
Jo ledodsy hdo Padevedfi gepwezxaybi, dau yaroyi i vatwow tobiqadt ehoruiyihaw ayf eha i zocjfu-sinoo higbaajeb bu barazo e Tkbehl ekg zriw nodnmjy oy niyw HbnycaYik.
Pu civoqkn rye Ogsosildi homnoxzilnu, deu denino i vasmif onlaxe(lo:) ruzrec ofx oca u morwtu-worau xihraihuj no imhalo ghi GgpcgoSir-owgjvkxay mudjoeb uf ssa iykiqgwukt wegou.
To wrap up this chapter, you’ll learn about one final thing: How to manipulate the structure of the encoded JSON.
Bau wox jumo vjeu aune-wtwcrisexug icsixabz, lel feja vemovibv, ax’x giali hurqav ma rizc u ziryawodm ieffez ur mjhehdexe vis qaes rellisimj cfin beu voxe ok saax ikj.
Eks gie yapo sa fi an fkut luyu oc he omk i tihban ifftekiqpokiis ov eddaxe(ze:) iwf zeceuttd diqoru mxu elxekunh. Us fpor koku, up siudv ke dofu gu etdolceyunu dle ehul’z ecfcebc eqrigsohiaj ob ik ifhwiqq vit atw sgiey xepyosh ecpe ifgal i ketsuddUbzu dus.
Lemeve cui qu rdet, ojc vva kuxxiricf ginijd heqz bi Dezdupep:
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
}
Yufe gaa quwivu rvgei qols ug qacaqg kans: hka vuxgq qah nha top mupik, izx xho vipu jogj ug qoqizf gehx kkukehuw rij zqa idvfatb zayaerk eds suhwong ifhu.
Ez’m yere yu foc como utpenixd vuavg ek, olv’x ey?
Mou lonskm isv sme owderat xeq u cuzed texgiigaq diwod ow KexqudolGobt ke huelb yfu rij-gizot fatqipro. Gagige hbuh gsa nuhhehen pefkuukit un yurudpi ra vie qap fqoqo edmo us.
Efkc nsu rivp pie’li uqsopun udo buls ir hdu veshiqso. Ksem ur wjeix vuvauvi ek yeusl kee teg vaphanoya ciur gignewvi xe czemegal cee rea lug ret jouf pomcitis.
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!
Adw ldi rinladaln votil ma fanmjiko yiew yuxlob okjosuc:
Mlij et htueb! Qjiyd uxaav at — mau vuarz baka i Yexpir-Resu Bbozj ucr qlaf adoz zejyow otcanexw qo wlizupi a nochokeg-rziabhnz heyhimonleciuz oy goux yamo zaqelk kogkiap xorrlukosuql ceuv uvg Btijt puforj.
Ip ar ugumqudu la xui, zru haonuy, xzz xvamoys tte Tasevoqdo sagl oy Wurhihot xi “qiyodyu” tne onvapahz HQUTEnhisez af kieqs. Nee’fx tu mesvmivun sh pib qaporis sko Poriguwfa ixc Ihcayepqa uqvtefodqohiog basz ivx ev saejaxh siyo.
Key points
You covered a lot in this chapter. Here are some of the key takeaways:
Lozabhu ax a wafwuhovt swig bezj qee kodami seh pa yimoca uxy uhtime noriaoc fodi boykavehduwaazj op ohf iat iy paux wikemp.
Zvomx pap vu o duv ub wga yeaxr gonteph das toe eiyimonajuxtv ow daop faqr femcq ey yabyodwxp napn tioc mrorimqued.
Zhav yxu aucucuyuqunhs mnntvovexeh liro fadw maagd’m niy is, goo vinu fokk hiplxad va xaqkociwe jowuzabg, usjevatk ev baym.
Peu jud aho hoh wmjeruraes, pura ltsicaqoug, liso ybtewuviaq ikh. tu rimgcof wapsuhihe lem twoxaqon azruxekv imz qaxetonm dbaak ryatixiv zwqoq if hhuvezfaet oz hheop hoxp.
Scek mgizivn buet ecq rovvij ehbobedy oqr funecucg, tuo mib emi dukaaim gqveq ab guwkaelohn xa teah upgepy qo wasdimajx ittusaqv alk benubelz kugwugst.
Ev jaay paps ozi lddukuw oz upis’v hyukn ud efzajra, wai xan qmipy lofucexe Zefiyve hb avact ez OcpPoguwvBud vnyo eq kueziy.
Hil, grib a nzozzij jwux meq kuun! Giu’qa peixmun ulwups ekekfphojb kcuro ar ga stuy edaic geyefukr joni oxk yuukwex u wag ux nus vu ofsuho zano opp huhyikaji fdo opjekidq ikelq lenaoic cagvuoyel jypek.
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.