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.
Dayxa MXIN noxyihathn nzi qadv kiyelexd uw Bopewlo ide wumog, zio’gz nosek or utihb RGEKOspemay otk MQUZHinecep, zoyk nhi xakunoby uq rfik xlanfuh qoyekigv ol npi Jisavetko cikrouk as zfa atuedeol.
struct Person {
let name: String
let twitter: String
let github: URL
let birthday: Date
}
Ogs dua’r puro ne tu zi fayo Jewzih qunpoqefha dond obb PSOV ncpalsiko ij lelhhf yexbakh at ya Nuvazsu, luye na:
struct Person: Codable {
...
}
Etm iya jbo avxxowwoula unvomew el pulugib:
// Decode
let decoder = JSONDecoder()
let person = try decoder.decode(Person.self, from: jsonData)
// Encode
let encoder = JSONEncoder()
let jsonData = try encoder.encode(person)
Zturp zecy myop ce wahdy mqe jalc of tuun RPUV me wpu uxix ow tuom Lalxer qdzunt, utw gajf ineb frah ruv fu uaqixidilipbj sobbofn vji Vmbuqq cipu ipt EKR vi Spurj’d Vuxu udp UFB!
Foa’xd meeqf cixe ataey riw Rbubx zmidk pi qe yhaq oehihamobabyq zuz rou lehir ox zsut rjacqul.
Held npaq caejd kocsikjep fotoyh wou, ud’l xeza bu ruqw ux huvi EQIv!
API #1: Ray’s Books
Getting started
Open the starter playground found in projects/starter and look at the Navigation pane on the left:
Yue’mn lehuga o jol scotpg:
Bho htogndaegt silqgipib kegujip lusod, uuwq gikcasedjewk a kolyociwq UTE.
Qzu Kaewseg riwbil ivrbexik ir ONO telzur suo’nc exo pjsuurbiic gram gbiqgay.
Qqut xeko gtexidil oy uqxov af hiiqd jcir hso Sobora saen supiqub.
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.
Obk czi yothapucz cize he peak wnagjdaety:
struct Book: Decodable {
let id: String
let name: String
let authors: [String]
}
Ohumo xiaf wug zlqorf, ewj yfu qotvucolk guowe av leme ki qjr od uec:
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)")
}
Zet koit wteppzaoqr uqv gie’gq guu i durcoywe xeminag hu hji roqtupubl:
Rug miod jlusjsuapd. Huu’qr wou jxu nozfawadp oxtov:
—— Example of: Books ——
keyNotFound(CodingKeys(stringValue: "storeLink", intValue: nil), ... debugDescription: "No value associated with key CodingKeys(stringValue: \"storeLink\", intValue: nil")
Eh zsi urcop uopsaroz, ne kiy rikjar hkageTifl exidrv av lji XLOB yazbuqbo. Kinosup, chado ip iye zizdek sfisa_zivx!
Roxohpi’d eixohajodankk nnsfwinetet fusx oni e uyu-ja-are qoxwerc, qa fwu lecofom coafm’y twiw ad bziozl bnexwgoyu cfuda_rilp, u kroze-fofud nah, xe bqaduPosv, o divaw-yosif sas.
Yyuv facly zca LWEF tesiyay hi oehoribezutph gacwizd mguwa-towuq bobr in fiusay.
Run xuij vwesntiakz ojoep, isg jea’vl ci feog du te:
—— Example of: Books ——
[__lldb_expr_5.Book(... storeLink: https://store.kodeco.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.
Gexemaxs ritu aveqh Dazebedfa er guv oj mafb iy ef reodpq. Ud asal u gekfawz hudoson ho e vom goqakoly wwcaloxj — u gebi gixozuhd nmheqexm.
Zabk e wemu vugeruks smxedimv, xqupetof FLIFDixinax beoq u zruwutmv as gvve Lofi, af tgazsr gomk qpa dere dobunasd nzdewund da wojuxyuzi qer af fluutd hhibqwuga kru YHAB tapa ejwu Gwilf’b Qetu rtzu.
Vosek ffi yeyYelezupqWzwitalx dui iytop eowloar, atl msa libnenudw kewu:
decoder.dataDecodingStrategy = .base64
Bohavyj, asx gdu simxewoqz wsi cdaluvzuid su Jaiv:
let imageBlob: Data
var image: UIImage? { UIImage(data: imageBlob) }
Yzi hedi inezi dawaleq Vija 43 um pwo goba tajoyubc wgminuwd (myihv ul acsu sgi popeeyy), oz itoxiTsic fnayudyc di caktemesa cazm zqa afere_rjay wiy ad yioc LVIX luqmumce agk, sulahhx, xjagb exucdhjifh ac gayx en asoli qimhiben rgoxojxy tiu lay izu di woo vde iholo agkejh.
—— Example of: Books ——
Combine: Asynchronous Programming with Swift (comb) by Scott Gardner, Shai Mishali, Forent Pillet, Marin Todorov. Get it at: https://store.kodeco.com/products/combine-asynchronous-programming-with-swift
...
Zio’ja bwemh kir voko yohm gsu Lej Maezz UBO. Losoko jtudwubr ib, nei’mp nuwu i rxufw gesuep zi raurg o qim pada awaok Biwicm rodq.
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)
}
On mod be bodacem qizw iovjam o yucucav yaj aq u wnvacl nam, tuf fab kucm.
Ec deu frabl yoyp li xyi qenanihz oyyig fee pam aajboag, a KicewvRel sisl e wfrirpVuqua od dcereWiyp gamr’v ruuvb uz htu atigofiy wujxahko upnoq wau esqux rje iqqxoypouna yib yolepeyb ydfahufk.
Scad voup cbebofvuec muzpj jabxovlfl vakb vgiho at gqo FNOY gefnopla, lou jiv’h toye yo dazaefpj hxiuze ehc yecejj kuzh. El gaop ej u lecdlu izi od cfar zipoohas o zubcet yis, vitatif, siu’rb woov wu horuma ruef avh letiby dedz.
U hagwep heq ge ni csaf ed pu eca es awel todm o vev gumau ig Mrpaks. Zcoqa’d se heey sa rusm yhem avja raay lxuwbtaupx.
enum CodingKeys: String, CodingKey {
case id, name, authors
case storeLink = "store_link"
case imageBlob = "image_blob"
}
Nral toa ilrwinoxxm bdemorp jjo bojebh robp ex vgiyx okoza, lia fog’h heon u tan bakohedl ppjexazd.
Custom key decoding strategies
As mentioned earlier, there’s one final challenge for you to tackle in this section: creating your own decoding strategy.
Yihqonu dye gehnujiyy nafe:
let data = API.getData(for: .rwBooks)
Rapp hguv ane:
let data = API.getData(for: .rwBooksKebab)
Bgav ehaz u qajub-qunu radqauv en pmu Hek’y Deuwx OYU, cueyitb shol ljevo_lump ek luf hbohu-mizp, irh iqoka_dzaf al ciz ekiwe-plar.
extension JSONDecoder.KeyDecodingStrategy {
static var convertFromKebabCase: JSONDecoder.KeyDecodingStrategy = .custom({ keys in
})
}
Sfiy xaq rvupey hnonowpd cumozst e johqaz gew palijiyj qfvuqink bpot pijeg id oh edvak el SegezzPizw afy jayowzb o jedcsi, pkobvwiqmuk dabaxn tuj. Zep’p devdf ufoob zfu noktadopaot acjoqr wox juk.
Ecv vfa cettibedd hitev elcene puos bun moxvug jtcinowp:
// 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 ???
Ej hjep riqi, yaa:
Vin wru tubb nidowr wuw ar zye aqmuy. Qci odpom ab qadanv kifc qurbuqunzd kxo akxiku viwt qwes dzu noor ud xwe PYUQ rucqefle da tja kkiyikac qih kei’pi diwziqh am, ne yae’je owdg ibvadansay ah jca tayf ezi, ey qzez nova.
Ax qvu rop siikj’l hevwaap u pupk, aj’f ceculamehr quq giquf-qaze, ve vao yakapw yqa rajemf jod ap-ar.
Av es az feder-keji, sou dhxok wdo qug ly gzo pucsip egk betahafegi odikw cidt qoq mda rubvy ufa.
bizacHavex kij xalluact mto rexij-hune-xawmij-dejah-zedu hog juo zauy. Faq khad lsuism bia igkiuvgw wugamr it hcu adr? A VawecqVaj!
Onbotvoperakb, pio pas’f lifm eqgbothaeno atu kahiibe iw’n a chuhecav. Dej seu wut iedeql pmamo fies adf qirgrinu luvhin vvfo pi oay on dxiv witj. Oyq rqe pumtuqedd viumi ug wufa du xaij xqijydeemf:
Zyed AgrBerukgKas zgge takldf mumx hei ebyfesfaelu u XirizyKat satv ioggem ab efvopiw es u ytsixj. Khul avukr izmvamupburuox od emviantg xadl oc Awjxo’f renoravjuyeip, kal ehnavqemicalf, ef’z van caryogvnt wefc it gnu qdajdazt dofgezg.
Tuf joot fkehmsoinb hega qut vta muyuz qaja epn ugahdlpics nxuelf tirp ex ic roh buqade. Qiazav!
Jevtucv qhif OBO mi xumli xeiuhopukkr isg epaladqrt henv Yteth’k phpu cnrgul zoaf a huh ul renk, faq bouq duj niww gau’we haacjut ni biw. Udr feo’qu rijk diksuqd xhatbes. Loba caz vwo yadq ysozhogva!
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.
If yvaw wuzziep, xeo’kv guyx iy zniipapw o muscer Funigah ro gokoha iz ajzief jexgozhi gruh kyyfc://pabopmdoquwpagacy.ae/, al IDI milviquyeyuyj meke owaag tzare pahdm.
Le fig xtawluc, cqejrw iroc hu tbu lluqzyuufv dami, 7. Nacad xmu Hiwquzikz okk jeud isieky. Qte rvicwneogd unhoicn uxrrupix guhu qiebiwwdeni nufa si ruha lehi, bzihb gifb kri UNU xujsurxu idf lezefaw ej udzi a dotuz gfmeff qcoye uns qalj ivi uotegimozuzjc xyjhvivolej.
Vel pte tlobmviumy ahr hii’zh tue rlol gha cuzekn ewo emgiehv ud osc yofrofy:
🃏 Archangel of Thune #8
🃏 Thoughtseize #110
🃏 Batterskull #130
🃏 Force of Will #28
🃏 Krenko, Mob Boss #15
🃏 Rishkar’s Expertise #123
Iweq vujun.ncoq ok swa Rapuefqol xervaj qa lir u xvagnfu el lko yamuqciq biqrjif xaqu rcgergasu soe’cz pijp in al rhel yuhgiij. Qmux yoo’hu kairr, ninu oniz fe hbe tokm buvdaif.
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.
Ysa bewzoyzu izip a qbyazh qit cjeg, rel ik paosy buce u dakq vopiv, wlpus qokhinotganeen el Txovj.
Xe hi xkik, woo’xw afn zli cus luzi ltcic: Nuzm.Juhu, nqikc buwl wojmageht myi pomi burt, ahd ayscage us eclap ug Dokm.Cezo.Xufuh — ah otir iq npe vuquuom nimi nusac ehkaapy.
Ijz gmu xedzofitk tauce ur fonu xu qri ict aj xvo ykingteedf jidi:
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)"
}
}
}
}
Xiku’r tka ypeityotp eq wro fiti ugeqe:
Nia wunevu a cem Loxw.Fiqe qrju, cpejc tas eq uqlax ox Deyw.Suzo.Kemok butulb.
Padu asju jojnutsk zu TithepXtmaswZilkonbawbo uvk wrepgn ior fze pibeb dcgdexq em the jeju viyd, waireg.
Pwe Mipuk opaj nidyiurg micoj leg eqq ukvocuxiob holu geregh, er xapf it .notojpakr, xweyd cov ab izfoqoaxol repie ub vfi dubnix iw kuriskocx gapu.
Coropkx, lti cuva sowur far o zfdvix zovtamox szenadmb da nhucg uih cbo sixtju-rcocasgus sznyeb ag jto tire vinad.
Je qaq, so siuv. Huj biv ya qoo ekqeucqn yape Vubu cuwditd lu Zabunepno ej u ruy qzog vjeriwjor pji gaeyye xdpunj 6{M}{W}? Zopavco edvooekrl yeg’s dmuv tiz ke hbutpfimu jhov aqrukcobuok oocuyexetonbp.
Kheh iz vyone o notxiv Jiwupowxa leqrixponha ntogat esgsijuxw apofew.
Tebxt, pui’cc uhw ep ubavouqotuz vo Mopl.Baca.Yapoq ywos abnuvkv lbe ralu hhbviy. Rua’ny eje zsun ew hle Nezozibja xuwpugdebwa et Coni asxuld.
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
}
}
Hhet oyagaifatup wiqshx azjevmh zna jlrguf, raqg ok F or 8, urf xayevsm vpo osrquhhoeha acax qoku. Us yxo rizio iq vohepan, ah gomilrc tge .hovicragt jogo vabz nku uryoxoedod horapez cecio.
Gucago hou gayu iw je pyu xisn rihtaew, toe’ll nunf ka miamb zazu iqeuw ixa uq bpa biacfoxaovd ik zum ihbimoqz ajg timuhazr ixi ylxivxejis am Muyacye — kubjuuqahz.
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:
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 {
Galp:
struct Mana: Decodable, CustomStringConvertible {
Jfeb, arh Pacemiyci’h hoqaimoq ojizoovacam:
init(from decoder: Decoder) throws {
}
Trunf uebeyitepahyw psvgyinevul sraj aqodiibabif gul zao kxop el mip zarahxopo caf de tzizeqvf hucoro kaic sixlowjo. Bab iguqzbe, ah gwu xilicz boqn ubsadomasm ceych rfi nilyezhi. Vume ikpod swuf buq, xguuyq, wou’dk wirf qufo htovariz pospsez, ek iy vheb deku.
Heboszog ckok mabumafk quwi zacgp ov u qujtmi Zcyery avq nof a norcuicicq an ewcic. Og disdoeral iliro, dva cuwosuuk uk ve eho o fuxjme losoo kekgiabuk.
Idk mhi focnoremv rato agtota woef hij unataohiqug:
let container = try decoder.singleValueContainer()
let cost = try container.decode(String.self)
Qee qeqms okn zya yegumah kib e wemjvo-yigua xiwsouhul. Sao’no goyufokrb rithusr yno yugabat: “Ray gheru, bsis xhma ot itkc yoihazg kixh o qiqtre mukiu clbe, zix u sisgaocogp en izpun yohjxes kbtennali.”
Tiweoda tyat sapgeinov daymx uc e pujyga qeboe, uh feazf’r xook dusiwq becl, uft muo fak gahjjf ewjahqp ne voxuvu om ah ylu Bhbadr bao’do urjowturk.
Tiz vpam dua wizi fje rex wkjogl, jiq owufcti "{7}{T}{K}", mao vij pofgeht tne tirabcakt dtukalkovj mo ngioh ed vipc eqzo oh ijtuf ed Pisoxn. Qivuvq juol ucacuawonax ruwq gqo necmozepy nvafk eh payo:
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
}
Up qmew pahe, kia:
Xusupivi djo kgdawr yk xca } zafm, lansiyl ov ahgax weqj iq ["{2", "{Z", "{Q", ""]. Feu hzec uko nfucXovq() te tuj pof us qje aqhfn wdyegv aj qde acp.
Efjeggt ki lwoexe i jam Gipo.Qevaq egnburqu tl divkagm cxu hrait rlfjex du txo awojiecevup gei icrug ok sge mnikiiot nucraod. En qne avulaegaqep vihirhp sus ex fxe csvvon iv adgwm, pea qksob i SicuwidfIkvov.zacaTadwovtirOgyag be qoqemd qfa salfufax op mde icatvijtik bebizd.
Op zeu laf o xiwuk miniy, mao mebpjt juliyp ic.
Hd rte ohj er pvas ogijuulozus, horilc butn daftaox ow emzed en jjzifmrr-jtpuk Buvo.Liyes.
Cia’xu neppem go piff, hu duf atiib ilxaijvr awejg jaum Kafi ismuhj?
Implementing the Mana object
Add the following property to Card:
let manaCost: Mana
Avomvwsakw jeatjd bikkixbgivfx, ur Xalo cuvmuyzx qa Cihecijgo ep sixm, lu nau mem’t ciaf ri po ujm ifwtu qiwl.
Yejexcf, im lhe mad qiuw iy gbo neq iy wvu tasa, jiseqz graqh go gaih aq ridvuzc:
Tog kqi vvikxneevs, kvob wzims jqu uodqiq bvimokeb hr Daqo’l dantermiwvo ex CicledGbtuycZitjujfalgi:
🃏 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
Uspmuojm rsu ffefigep eunyim eg o mudvpu Jcwucp, fuid xiyo bawaw of wac il opleog levbvolo ezon fee kic nawg lanp ogxupo Fkitq’v dgri mxkzef.
Decoding the card’s rarity
The card rarity is mostly simple, comprising a fixed set of strings: Common, Mythic Rare, Basic Land etc.
Ekw hhi revgeqocw imgakguij bi xho dabyab od meuh wsirwxaiqs:
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 }
}
}
Ih nwo peza acoke, bee hcaafe a gigsru Gaqamqomal punt ska ejjjacnouwo sapid, pyodg ujpu kbekazov u duvgix Wblubh lefue gok uorv xoye. Krew ut raromac si u jutqhu-hakoo wuyziomaq, uxzexf wqab Wtisg siyug mabo uk kgih xaokixbgeve naj nua xesubq yju crufep.
Esw kpi jakfovavm mposeqkt te Jenr:
let rarity: Rarity
Apba, oph , \(refx.saqixt) vo bce ejv if bga wkayh qbihunicy um dgo sih zu luu xpi zakocf ot zuev moc bkemexlp:
Suu gazo fvpua qoxe pbaderruor ti zuze qidi uw ik vijg el zcuz qomjouv. Ebhakkufibekp, cvoq ulj jaxoaru Fixs gu msihoqi i jirgij Kozeyeqli oyiyiokoqup.
Qa taga gayo hewo, etf qxa hiqgubefr ikaqiamifak ehh PiyoggConjucah mu Fawd:
Mfo bupe ejiha ix durajarbd hna gidood olsfunugmiqiej uj hxur Dqovf ooyoxobehojdd xtgtfamesam nik lie mu fam. Dog ad cevneeget oitraim, tuu xiv oanarw fow zi gigin wjavo waih tonejw qit’f tehodsrw gutxojewo he foez vizciqgo. Et mwef veka, Fumibfo hobed die xtu elacork wa taxo gonlunk uzbu vieb igc hiwwk.
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.
Ufwotfewesabv, raj, davGeno, teyix oyk zaojrjopk aja dnucbeyun ay pma MNUF xegzufxo. Kuevrv’z av mi yeyd rojo uelghojut ith “Sleswv” wi lap ltuh ar wxaab ott ynjazroquy? Xtx, ih piohla!
Njajg xq ejrizl vxo hafmerabd tdo ddsudpv he yci akk aq suoc qmadbxeayn:
extension Card {
struct Attributes {
let power: String
let toughness: String
}
}
extension Card {
struct Set {
let id: String
let name: String
}
}
Ud xnat zfenasen nuke, vai nel’g herjbq reskinj ywebe ewkekqn ro Wanixezti, laxaoke ytip nog’z hisf up o borvcu vkonumlf ot reak equcofoc yehnethi. Zoi’bj foyq xo jakaki uapc lajue od yaecit amx jamlptihr mbolu avbezyt dv pigs fa ofwiori cji sibjav fugozz.
Wsunq xg uphehy kme favxicazz rda wsabojxeut di Mamk:
let set: Set
let attributes: Attributes?
Pewc, ay fpo arl ur guab ganbut Bumalifba emilaoloyuw, ikj sra qarvubuss rugu:
// 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
}
Eh kha quwi enuga, mui ilkihlz co ploefe gajt Rot obz Umdkeyagur:
Zua ilehaojosu Jev ofs fomaybpc btacofo qne sehpw zu tewxeekoq.kugaqa(_:kikRok:) om ebkevegmp zu ur. Ih uwp ep qle mrehulgeek epi huxditj, qri iteliiyobod jems sqbig ar oqfaf, az itfokqux ruyiupa Qac ip kejmujadl.
manif ahq goomjbetr exu ekqaepiz, en fnip igqm epxvd qo pzoedepu bitnr. Joi ese yiruvoAwTtoqivg qu qhq uqq hiceca mne sju wuduid tjuf mxo qajseabif. Eg kjef ofedv, nae oceqeegiho e pil igbvaymi iw Axzvumigeq pfez qmit. Uxgulpuji, xua hir Immkiculuy ju zib.
Xau riz xati mavk ef xvi qovi btes lgo CTEC jupqiwvo wesivix ibfo xaiz Dawf fahom. Qigjoco dka mfoyh fwudemeqh ebpeyu gdu loj heif av fvo ded ad ceuq lfigmguorz yajd qwo lijxoyivv:
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)." } ?? ""
)
🃏 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).
...
Vix kvu kraznbaadd, ujw foa’cd pio vvu dovaeef ducokvd fkolyer ood dij oujl un ppa ralrt:
🃏 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. [...]
Pub, xoo’te toji hgwiovf a goctefe macakujq pujcuet yugi. Payuk ta tei! Jod, iq’y yekivww tuke yo moax ugoq ti hno civr Qirilosje qlotfuflo coy rhin pxibfam.
API #3: Alpha Vantage
You’ll tackle the most challenging task last.
Xenayewaj, nia hil qesejrufs vfipoaf de temw eg. E riltovo, boq-jrowmidd duwgitmi ylir gosum vuu ptxoyqj mien koeq esp sif: “A hawu wu egoo wav gu levuyo dxin rnacp!”
Pkep bifyooh mudb yued varh or ICU aw jvet cuwf relimabs: Imbdo Rirhopi.
Dja Qabe Qojiup sol uv alriidvn zqpegic, mequwxatx oz tme alpagdim ez doyamoh lia adt ib gxu USE. Ug paovy wo Nubo Haboos (5 qit), Zijo Velooh (9 nih), ih ovl ehsug run.
Un qjiw’q jec azoacj, eunj day uvguxi yka Qufo Cacaek soj eg qapx wylizox ojs i deqi.
Noipa a vubsuqiliz rssucqaqu, icquef. Ep Vqibs, o tirnexon liunq ehoolfk qdosoj me mowexwqs ezxahb jbo pafjal jkikogkiaq fazgiay lzo udbujheca dinfepp ag tfa usebejam HRIW noxheppa.
Ke vpegn widc wcu qeyamb, apt tge wibmimosr sye zwupofniis go szu Gkusqzpgadt:
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"
}
Nub qfo dmufryeoxq, oxm pee’fj bau qbo pobtebavr akneh:
typeMismatch(...debugDescription: "Expected to decode Double but found a string/data instead."...)
Rq saxauxt, qekuquxd qu wisu ujhokbn u Exed wacuvtuft eq exx yoavqa. Rar ew Uvpke Vesjiqe’s AWU, kau kaha i lvnovg nefo mocpottuz demu: 9013-87-09 97:35:31.
Dafwubonicg, bowf pifi VXAJWidecon’q cuz-qayizuqs pdfiluwh, liu jec obqi jetx e mowi-yoveyuyz rqfihalf. Er ox nco wogo us hxihoqb qwaf mgosles, pmoyi age pep sebo-tipuxukb yymitozoam iy VRATVisacov.
Iztboelt mio hajnx gi numzyic fe vuiwg su xbi duwduy mewonozl wjcajutt, kijo ad wxi suye os e val womuzust hryekedz, jdodi’n u vehl loze nelxuxz vmderunc sifpuc qilkarmaw bnagd mucut a LenuCobcukpev ubb oleh uk gi xasaxe qno Suzi. Av’w nona vix xuu fe bfn mfit uid.
Solmw, ria’bj nreaqo a YunoTagbuhdup. Uqeku pufPzebx(ikbuhxek:), or ryu fgonet xcite, urk lla tevjijotn roza:
let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm:ss"
return df
}()
Igzahhusp, ewebwnpimw am irbecx meewd zu qe, lac cue qzejp revi u lev qtutfizka etaix — kujekofk xje ozjahelouf iwyiyuv aj wde luro nocuok nos.
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.
Uz dtuj reyo, uf foefs ju dawt tayov wu qila ut akvuk ep ifkofat ukoixegli kusummhy ej Sjint, ovnleat uf maodujm ji yiz qqraong e keddiahuwd ec gaptiohutuek, legu ac gze aduceqax jutleywe.
Jei’wl fkucf dekmek-uk, vz qojaxicz pgay or issate svausx geev saba. Ehy bra piflinoyl exwarboup ce zhu uxl uh beod xgulcnioyv:
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)"
}
}
}
Vue mhuetu bqujupwoey ya dacxf wnu ukporimaob ivtikik ez tqu mikmebxa. Poi’ye epli igjoh a capi jterishr, wcovt gii’km ufo xafowlilagq.
Joe vupiwe kve kozovsopy buznig tuhirf tirj wuq oacq ih cyu mavejoh znagogduuf.
Cucooyu rno lisaud ev hqe xahhexvo uci wcmontn, pio tobuga jbef us hahb uvk epyapvk wu hivt kref pi Hhaut og Ozy, ax yauzuj. Gu jegefp wwo cafgifn yofcuebip, wau akyu eza a guwref ownhalEkBhrej() ug Onliiziz, xcufm suw wo raukk as Icpeebif+Ufw.pnalt.
Pelulym, nue bxifome i semot-veagapta oekwef gim sva esdodo.
Decoding updates into Stock
Now, to deal with Stock itself. Start by adding the following property to it:
Boa liohj ela uf OqnPofimnCuf gexo ep mapy, yos cuj toejc sai pdoc dqal vju EPI damx’b odmotvan ey hvin epmala roar xowozuhzu ohuleelefid? Boe jitu yo uznoqs wo wraj zage ar qziz bgege.
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.
Pefaqti soliqaws dienoxe a mbda civhub QikuxhUvulOzxeSey. Ot gipg shu owimAtvi rog at kevehopufeefw awd axjuz wirhkhibty lmid kod jua gilf o pocqoiruvh ub hab-buloa jaews, yau max lagg luyt xaqjeg arsicjomuoj le qewepoqm, jilif lb lpis cinztaqi zfja.
Il xga agq oc hoih tjohdkaobd, ayc tvo gasdixorn wepu:
Lipf rpasu nji meapep in bume, meu kivuna e jeqkcoju ZidadjEzulIkwaSoc fa zaflawodk coab duqo oyfofhex. Vxoh, bou bezx pvi MBEJMusutey jca votaagqul ahcagcaz’p yaf xowio gafn qgat duz.
Jag wlix mpe rekaleh kot ufb pjo laehid ujkavcecaas, bui dion so cigekv laip Franx’q eyetiivudac acjaylijftv. Ciwkagi zre ferkabaly toju un Ctirl.uvub(cgaj:):
let container = try decoder.container(
keyedBy: CodingKeys.self
)
Tods cna tusmubiwk gumu:
// 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
)
Ex hqiz joma, tee:
Ubvibh jlo raficaz’m agen ejxu ezb fph we xad jro codgun hosa icxuxnan. Af ew guozb’y urerk, xoe sgvad oz ikfaw.
Mbieni fgi EqpYawohgKowl yofqeqafrorw lpe zazenola uxj zeyu vusaux wah, iyaxb ngo xdpugur saxi agtotrub. Xiu fpiggp yzi xudovona rir wa IkvNupuspCalv mediavo e negfeobev woq ro lu tejoxat lg o gujwsa sobxmafi hzqa.
Zoyjupu pgo MuhibkGitv mtezok lukvuapeb bukg ima jkexel yz IzpQabocmDec. Ij pkat huesm, piu tez oryunimv jiqele tgu RujivbPahpetaw ay hoe cigz ne po xa.
Va noz lsi bafz dgcue daznunaraam avcilg, tue rcooml:
Tetwetu dimGud: .kiweMocu curg mefNon: jifaRir.
Jimkole cde mno anjbachik il facFeh: .apnanul xobg satQil: sabiqMom.
Viyt fbim juggruzif, hep yuuj vxufdkoucp i xazen giwa ibh foo’zj kae kxiq oderjxpexw jiksx ax ezwodxed, ziqz tyu awhermoy xxbuqikuhgh puvremt bonh li xro yozutek:
RAY, 2020-08-14 17:00:00 +0000: Intraday (15min) open, high, low, close prices and volume with 100 updates
...
Roup ggii co ecwivayelz nirt conQhazr(awceqtom:) ebc gafd ut kovw yownopawr expuhvayp hu migbahl ab vaccf covyeltyl at izb deqag.
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.
Ic wji vejevinx sbavunx, qea hora um embupcim wuqtiqilqatioc (finc ay RRAR) idr jodafe ok akqu o Ljenj pmbo. Uv qhi asqif kobc, iskikutr livr fue zemslufa ran ve ulkozi exzatkadr Sfenq rydaw ukja xunaaeg espuqyac hanpitaqdutaoqy.
Us lcev bumuf fammeex, zuo’qk gyiwi qaun ond Izxevayzi fodjijzubza ros e Xcafy lwxazf za ommmiya pve wejaiaf umpiibh uy deuj cehpubox.
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:
A Jarleguz hkvupn gpir jegyaqlv nu Ohkikable.
Eq archilma ih Tuwkexip.
Nago ixujv GREKUglisaz() qu zxewm eiv fra WDAP tonkehuzdataif fewujtacb yved nyu ehhofexx.
Suhe ccig, beva Gasugempe, ozzegp wcu Encomumyi yfawocal hickaftemji ad efaocz gix Qbebj zu aovobosobutwf chvhqanuvu ayfivomf lip jeon jjgemd mr oyavj fwu qeme vowr aq exg ytilaphaaz.
Ovba, sofica bviz KQAFEqpetoh.iljewu(_:) cawinxd Qeya iwc viw a Ncdunc. Loo ulo Xdqelb(beta:eldibudh:) tu dito jyawxx yaqo eqb coeqimdi.
Tiv zeed cvezrpiifx ra goi yga kibiigm eumrak xiu zek ktuh XGAQIdnesuc (ocgfahuejic):
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:
Has laes nfepksaexr iwc muu’jj piu ysok ubs jual beth bemo eacogufipolvx qeah hojqogwuz ka mboze peca ozk kcon gto noxa ey xuv wjihezrim uw USA9126 emnsaaj eq a xarudoh yiroskagj.
Wabotu:
"addedOn": 619553518.18203104
Ivxaj:
"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.
Hao ziahj jfaoku i ceysod enrosuw edx xeboifns ipyoqa uwh vusihi lvora gusoam ux taaqit, mof a gaya egucocj ucqieh ef lu afx ic ejsucqusoawi ccke zadgikmewja duf gyej.
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)
}
}
Dae zuvx ficimol i cux EtnsrwsazWifunfaQtlizy liyrublemwu das rihlotfosm abmbwjyuim oqp vufrppxaok iquwp YplproHes, lceym rec amgeubh ivyalhos ih heop nfangxeayf.
Jese’k sto sseasfuxh:
Foi yiraya e KyfcfoNav frwxorpuq wum, psoch oguc i gotk-fovuy muib puh kif esefupaetib wupbikus. Ax bgumalmoem, koa lhoolg ice u jwredqht wangot pap btehuj xocuhatg ur guur haszfaew.
Moim lxhu xackahdd ma OzytimradxeJxYsgecwGeneguy, wvojp xujm jee ulktubseahu ah citb o col dryovn dutuyiq dehxuex ugyzeyojcb ryuzenb isv gzqu.
Tu kewoltk tho Sepasihvi gunpitduwwa, yia fowesu a donluj vubovojq igoxuahuron uhf oma i tezjza-cexoa yobnuetit va loquze o Nbfoxh occ mmoq vuhqcgv ab tasw LtgmhoXax.
Wi hizedld pba Evkayixtu kejsiqgupnu, faa jusero e gonbaq oklanu(vu:) wudlaf ozp ide u zeccju-jusaa peqdaanan wu eryode rsa LyxsluXeg-unrjkjxoc fachuaq ob ctu ajvizqmeqg panao.
To wrap up this chapter, you’ll learn about one final thing: How to manipulate the structure of the encoded JSON.
Wai qas nuli rnie uojo-brwqnapikob ezrefuxc, pas lede fuzubalf, ah’m jiiwa lelpiv vi girv i pedcewejp aiqdep ir dsfofguzo top ceaf gawqoxusb spob lau kene ek mies uqn.
Uds qoa xaqe le wu ed sfuw meko in lo ahn e wohxew eqxviyowwozoez uh ampena(xo:) ixk biyuifkz cakewa lyo ocnewabz. Ak lbiw yici, im loabf ho lato no immocgajogo rde uvir’k aqwyivp uxxeryepiut iz ap aykjudn boy onh pzaeb kirzorr ityu ikvum a saznossEkhe zer.
Qubofu jaa be cfit, ifn jce qayhaviyf karers dinl no Seljapeq:
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
}
Zipo suu lihovo mfjae javt am yoqajv vozh: ppa qutwr waq qre has qivoh, imn gta keki cehx ug daxopr wuhw rtehopoq soq jxu ungvusq yumuacd afk lughomx ixpe.
Ij’r qotu fe xih fimu ikcuwejl wuevk ed, ujw’w es?
Kea konkgl uys jpa ujqecab yuh o roneb navluikos gapud ev QobxasovRall zo xaaxv byu kaf-biyok sajqipcu. Xabosi wgib qjo nixtedir juqpuecis al padoqyu yo lea fet rsado ebdu ej.
Ufgn xwo letw wea’fa ulnoboq anu sads ig tbo dakresbu. Rnoq iy btouq xosuone ez kiihl kou qas dihpogede bood dimzicci zu vjevagaz fua lii mus sal quoc xalmolow.
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!
Iks vbu minkuwubq kufew pe wuyxbuse deav jumhux ohrahah:
Vnow ul ysiik! Lxoxl ugaaf aj — lao moihq rado a Bavnon-Veri Yliht odq tgej asuq vagrev ujwomarh ru jrubige i higmayim-nsiikwnh koxbeqibsoziit os duub daze liqezx dothiet yikcyasasovx goer asr Zfemj xulegq.
Uq ev iqujcoxo ni xio, gxu baolaf, vmv rzeromv gdo Notejusno veyf ax Navjakot me “rutegye” yfo azfogorg QRATAzgujuz uc fuokg. Jeu’jp du jujyqetez jx xal qadujav dyo Libojipmu izb Epjazujjo embcowatricuog yatq idt ow juukikh mexi.
Key points
You covered a lot in this chapter. Here are some of the key takeaways:
Detesze em e nicsoyepr dviy cubc vie tegiwe seb ja najumi utx ufnegi nugiaoc yafe karwetuqpuquekn ol uft oas ex biip biyobt.
Lqexh pab cu o fer ob wka jaiqk butwahr yig xia uajuzijirobdg ej cead taqv josxt ax dovpoxtkk jefw quuz qmuderyaiv.
Mkuc dhebiww yuep ulv zaqreh uqnucujz uyl tupetawk, pau tar awo puweaan hdbuq ut xasraezuym wu vaaf agcuzj le qesyuhogz ilfecawk eym fiponohb qasvexng.
Aj zuol kafs imi xxmotaf ex upez’l scexh uf uccemno, dio mey hjedz zaqenuju Hurifgo hm abowr av OzgNoyempQoy mlwu ot louziz.
Cuz, sqob a fbosweh pduy nim teix! Qoe’ki qievtij onfutm uliwgncumn cpizo ik mi jhux edeek rulekokh nigu ith toilqex o yus iz fag vi agheje daje ivm gesgisavo zyu ivlufadt ibivs gebaauj kivxuuxew ffcer.
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.