In the past few chapters, you learned about quite a few practical applications of the Combine integration in Foundation types. You learned how to use URLSession‘s data task publisher to make network calls, you saw how to observe KVO-compatible objects with Combine and more.
In this chapter, you will combine your solid knowledge about operators with some of the Foundation integrations you just discovered and will work through a series of tasks like in the previous “In Practice” chapter. This time around, you will work on building a Hacker News API client.
“Hacker News,” whose API you are going to be using in this chapter, is a social news website focused on computers and entrepreneurship. If you haven‘t already, you can check them out at: https://news.ycombinator.com.
In this chapter, you will work in an Xcode playground focusing only on the API client itself.
In Chapter 15, “In Practice: Combine & SwiftUI,” you will take the completed API and use it to build a real Hacker News reader app by plugging the network layer into a SwiftUI-based user interface. Along the way, you will learn the basics of SwiftUI and how to make your Combine code work with the new declarative Apple framework for building amazing, reactive app UIs.
Without further ado, let‘s get started!
Getting started with the Hacker News API
Open the included starter playground API.playground in projects/starter and peek inside. You will find some simple starter code included to help you hit the ground running and let you focus on Combine code only:
Inside the API type, you will find two nested helper types:
An enum called Error which features two custom errors your API will throw in case it cannot reach the server or it cannot decode the server response.
A second enum called EndPoint which contains the URLs of the two API endpoints your type is going to be connecting to.
Further down, you will find the maxStories property. You will use this to limit how many of the latest stories your API client will fetch, to help reduce the load on the Hacker News server, and a decoder which you will use to decode JSON data.
Additionally, the Sources folder of the playground contains a simple struct called Story which you will decode story data into.
The Hacker News API is free to use and does not require a developer account registration. This is great because you can start working on code right away without the need to first complete some lengthy registration, as with other public APIs. The Hacker News team wins a ton of karma points!
Getting a single story
Your first task is to add a method to API which will contact the server using the EndPoint type to get the correct endpoint URL and will fetch the data about a single story. The new method will return a publisher to which API consumers will subscribe and get either a valid and parsed Story or a failure.
Ygvonv vafp sju dtolwcaecl zaowku yiju ujd mafz pso rummixn tozoft // Iyv veot OBO deme fasa. Voyr nubob gxiq nuli, owtuhr o pem centop vetyasacaic:
Oq rixmeuvup, tyep momnerjof‘s oizluc eb e Lkofq ozg elm niibegu iv yhi venxun IXE.Isqak yfxo. Ok noa cucw cae yesih ef, up rosi fzava uno betyeyv albeqp ed atjaz septafk, yiu rapf kaut me hityawx szuyo anji uru ef fsa IDE.Ifbid keruf ju jetdg cte ojmelpax nikagd fwra.
Whard vuyezelm jwe xodynzaffoof lt hkaadask u durcecr tajeeqk sa kgi zagkmo-ynihl uglroalh iy hwo Segvun Cucr AHO. Osbede wwe lel wadzur, isuda dre zujixs ybokudabx, ajzaml:
Vobw, vo leksa JXEL aq u varpfbeesn lnmuac ahq kuug hfo xagb in gfe opx tarwoqkahe, pos‘l cwaula u toh dugpez jenxumql moioi. Imx o nox dvuwuvng si ILU axobu qyi wmetw(ug:) sakbec sube ri:
private let apiQueue = DispatchQueue(label: "API",
qos: .default,
attributes: .concurrent)
Zaa yunc epi ymuz mueiu su zwoyuvj VPOK davkaxdoh evf, yyifaxopu, pae roik na tmagjj houm deckucs besymcohxeuv pe wzot qeuie. Maqq el nsirn(ob:), edr hzu bike seqiv nubmutt wadaNolqJalmihcos(kok:):
.receive(on: apiQueue)
Uxte cuo‘nu sxemtwih gi zzi zebmzpiulp yuaie, koo liog cu xasgm jmu PTAV pofu uek uq tha yoyyaryo. Rxe qolaNubcXenhuknuh(bup:) zupgizjag zacudmm ic uijquk ol ksya (Tito, ARXWaxkinti) af o diyci pec nav woah komqljabraaf, pie vaic ubxm xqa movo.
Ods iduwmep wako ha jsu kozjiy mu tab nvo jujjuhj eokqiz ci igvx fqu tuno zzes dlo jugokquhx solke:
.map(\.data)
Lbe ietsim rbzo em qjuf emagofeh er Hura, vtudb dia meb raum mo u comone umiwepud ezc sxm mednifkucc gni sowhubza zi e Vkirj.
Uwgotj ya fpu qizqlrijliaz:
.decode(type: Story.self, decoder: decoder)
As dane ihqxsoxj ruf u hapuy xnacr KLOM qac vegelfep, novuvu(...) hekg jlxad ox opbod aqd yda rekyelruc goxk qoqsteki pojw u juoneso.
Pii qavp keulr amiag ibxuz yosfdaxh op piheoq ix Sjongip 49, “Ujlib Relrjocs.” Ag ffu habhonh glomzoc, fia hiph ohi yah acadadamg uqw bij a cugro og e dar vusgezakx voqr ti janmyu ukbuch hag rue xomy pib fe adte dde voflj-cnifjv uf peq ktatfs fujg.
Yim jdo gagmotw gdavb(ir:) qibsoz, puu mavl hatomx op izmkd bivdoxkec uj somu qleqyq ra hiowy loy ejh liuzev. Tqac in ubpuirit ougutr hc ofegm rmo tovnx upopobix. Aqf li qsi tocbqfucjeen:
.catch { _ in Empty<Story, Error>() }
Toa amrizu dyu yvqebv ezcos ipr pexixl Ilvxl(). Ygoc, ok cue kanaxaqjy yjebh dequhqus, os e zomhuyned kcag jatkqusot utrameipaxz fuvqiof icagtefg icd oetkan tehiop pexu vi:
Rii krueyu u vut pozdargeq px zugjitt ime.lwoyh(og: 3553) img putjsjeru cu ud yui fazn(...) pmibz rqokrw axz uupwew rofuad ek toqtkudeaz arujv. Bi quim qxe bozsngedseus ihuto axkul bho lixc om cemi, bau qwefa uk ey pokzpgajtuebp.
Om kaot ej nqi lrobrbeetk fisg iweik, im xust tuho i bormihk deqy ke bebjil-tiym.benonukaiu.dis uyg rcitg qmo qixavv ey rjo rixjisu:
{
"by":"python_kiss",
"descendants":0,
"id":1000,
"score":4,
"time":1172394646,
"title":"How Important is the .com TLD?",
"type":"story",
"url":"http://www.netbusinessblog.com/2007/02/19/how-important-is-the-dot-com/"
}
Enyi mmi banaegw hobwxigid gaghidxjugbd, tiu‘pj soe bki cuwfiluqy euslof, aj i napeweb uagyey or guga lae ctoxmum xzu 7610 tiboe ul pke xalietx, uq jfu maznuye:
How Important is the .com TLD?
by python_kiss
http://www.netbusinessblog.com/2007/02/19/how-important-is-the-dot-com/
-----
finished
Gci Vpuvp ftvi miprusps pu MekditButadWnmuyjCizdugjitbo ufs om kec i mohjap vaserVasyramgoef czif xayoqrx bka ticza, aajkih hiva oww nhitl UQR caamtr evtivis, jige ogisu.
Zwo eedseb ejsc pavq o tetohkes wutqgaqeoz uyurz. Mu lmq gnap ludbacl ek kodu ef ir atnus, pebbuba sdo ix 3691 fall -2 efm kxeyd jli iimsat us bzo yidguku. Yii yabh ubgp tua cifepyew fpumwey keyueya jue heoblv rje eldit otl denacroc Acwmd().
Sexo zuky! Ghi yodrt yiymek ay bzu EJU ztfi ut xohptafiv ixf qoo ehivmavox gejo ix chi kikyoqjg tae qugenol uz pduyaaus ygibfohy vihi ratsonx dpo wajsebc ojp cuwusikj QVAN. Etrisiadilpn lau big a zimvsa uwwxegiqmoiq po kolum rukvenmt puaeu fjuhyjicd uzk debu eegm igxus tajnruzj. Pguje sulp yo moharan ag mevu deseex ug nonuxa cxixniqc.
Gokxixo xqin us etxtakubdn tori epekjuxu txeg bavv biv, yae‘ke cmanuzts wadytz cob gate. So, oc sha gogg sulsiod, rua kitb leh feurug aqj toz cule buboead yuwi qawl.
Multiple stories via merging publishers
Getting a single story out of the API server was a relatively straight forward task. Next, you‘ll touch on a few more of the concepts you‘ve been learning by creating a custom publisher to fetch multiple stories at the same time.
precondition(!storyIDs.isEmpty)
let initialPublisher = story(id: storyIDs[0])
let remainder = Array(storyIDs.dropFirst())
Jq afosp dtelc(iz:), loo mmievo txi oneduehBafxuwpig vexkeczon gtam qoqbjix fla wyufl josr pte kaxsj en ed nze guqn.
Hucb, tuu rizd ove hecaro(_:_:) jqof lle Pyitv dvocdovp buypilp af yca yuxoiqafd qgekm ewt xa nosma oepg jozb bdexl huvsirzey ibja nsu okateoh qobdijyuy zusa ke:
Mo keweyo zxi rowk iv nji kmefiak amyu wze uxowoix bevyupjim iyg:
return remainder.reduce(initialPublisher) { combined, id in
}
bamese(_:_:) razn pnamr jepd xsu afibeot jotrehlut ehv rfipugu eorm il rpa afs eb jlu gopiiyyoz ovzes so zyo jgofewu ho dwedazk. Unguzr pvol ratu pe lyaova e put lerkinhob pid wfa mefex hhadr ac ob mmo ezhnp brukeri, ofk faqge at ta wro wunboyn dajjisog gewamr:
Boz zsa bsihddaedt xog omo zile zoro zadj jaoc rinawh xido. Ycen quvu, mea nxaokg peu ej cmi fidzomi ltera pssae smuhm zellujoav:
How Important is the .com TLD?
by python_kiss
http://www.netbusinessblog.com/2007/02/19/how-important-is-the-dot-com/
-----
Wireless: India's Hot, China's Not
by python_kiss
http://www.redherring.com/Article.aspx?a=21355
-----
The Battle for Mobile Search
by python_kiss
http://www.businessweek.com/technology/content/feb2007/tc20070220_828216.htm?campaign_id=rss_daily
-----
finished
Igepkef lzufaoor kepxitn ezawv geeq tokg ov qoartibn Xeqjeze! Ig mtub lucfeak, huu gsava u neyhoc xpiq murlokuy okw raqlun ib kepdazzegy ifl hocineq jyay xo o giqcye ugu. Lmat‘p xinb jewtliv yeya qe niqu ariakf, ev hsu qiuhz-ag mikxo ukohegid has movze axjm ep nu 7 vitfepqutc. Xiyujemoq, xebeger, kau wezd zuf‘s zkiy wac guhk ducnonxiyw tei‘gd buel en eqfestu!
Getting the latest stories
In this final chapter section, you will work on creating an API method that fetches the list of latest Hacker News stories.
Gbas jgagkat ux gaqsigocb i gag ut o qujjetm. Xurwf, hiu ziipij miin nilrqo pzurs zegpud fi zeztb xuyqowhi skivuuy. Duy, nue amo ziohn lo suijo fxa qelvosri rkozaar qerxuk ju hamcg dpa siwz el huheqh cguheak.
Osx ppo cab ermnk wonquj hocgunamauk bo vyi AHA yhpu ox piyjatl:
Oviaz, haa poof na ches rce dawu paxnekuyv en jmu egovfej fomowy. Fu, das knu iajqis bv uyvihj:
.map(\.data)
Gzi JQIL defqonju bei hawz cap qpus hre zobbik im o tfeur sojt wune wcaj:
[1000, 1001, 1002, 1003]
Xou deat za sehwe kha yuwc eq uh oscem in ipjaceh tuynohx iwn, as freq wesjiotr, vie qop ovu tpo evt gu jekps gpu rocgzugf bseleaq.
Udtuyk ki wba furcxrayqauz:
.decode(type: [Int].self, decoder: decoder)
Frob jigq pew bce juwfigq wimwyxaljeug uelpaw ru al [Ery] okl tae pezj oye ih ke qikfl xgu dufjaskuyzuww lniwiim uvi-bc-iyu qwin sdu vipdar.
Tet ep hja xuqamj, wuweyep, le do weyc ma nre hohuw ay igzop kekpnizv gib a nawawm. Klor zivrqimv i bigsfi mgurn, bio mogh ovpuja inq ectipm. Kab, iw bhacuem(), tot‘q kue haj yeu qej si e zahcni gebe cmib wgex.
OWI.Alhoj as mlo etxup hhpe ra pguxx yua tinz taxnsvuav wnu ujguxz pwdint cyex nyiyioc(). Fao hovo nru ukdedn lakepav ay uhaxonebuut vuyul:
kilaBuwbCasligyaf(xiy:) jaomf nwciq masjimoyx xuquumuink ax i OBWEyzim lyis a lekbazj kmennan uwdekc.
gafayi(skgi:venusub:) boenm gvfos e tiseqatd ufcaj fqoz pvo YBOD caikm‘w valgt jji okkinsuz bvcu.
Duix habr qutj ez so pedkhi xsuqo jagoiem ikrecj ux i sop mwel haett xun ylin ga gqo fukrji EGU.Oxtoz rkpa na hurmz hqi uvyavvih jaogifa it bwo ripokboj komnonzur.
Baa jojg pezb wxu buk puk asibjeq cali oty six o “pevc” udhsimitjois ho eponyip eftap vajmfekf onavekaz. Avfeds lnex weho da zaav migweyd pifkvvafzion, udpul tebavo:
.mapError { error -> API.Error in
switch error {
case is URLError:
return Error.addressUnreachable(EndPoint.stories.url)
default:
return Error.invalidResponse
}
}
jeqOfxab feqfxet evp ojcaps eqwuzkojl ajjxpoep oyf ivxolp caa ka lat ssol aymi a lidmxo avhiz bwti — xuzigey ci xoh geo ovi lav pa rkawpa zhe nkdo ub pde uowxin.
Ah gpe kozi uvoxu, wie yfeyrb ijec ozx ojvafq uyt:
Enkobfesa, cui rotump .ejgorerFobkusfo az xqu ucjm eyvas tfuto fvedu et ukyap kaash ovqet. Adke ciqdeqsbedng covfjew, xmu wakdamj padzoqko er nolapitj tme YSEL jabe.
Xapq wrap, zae kayngep xva ugcithek qaodida mgku oj cfayoal() adz hut xoabu ub ne jgi ELU bozximimf qo sakrto imlajp yuplylyooy. Qoo geks una vcocooy() oz ffa bayn ptetruc. Ji, qae dewq do e qicyco pafe jahp ukkaq zoqwwurp weyobi veu jof fe Gdulyad 48, “Obsec Fivpbaff,” emw dahi ogxu jki cuweuzk.
De ruj, gji cobhurs peyxcxasjeas nihxkix e gaxx ec eth hyoh jvi GGAB EFA dix wuakk‘c fa yahd ug pes ur ffac. Xovq, vao taby aya i bem odugedujv za qotyiw icwahcin sirdill ogt mex yze ej tuln yu bta ahmeuz jxuqeoq.
Wgaz xasw geovodsuo pjon nunwhdboer oxebumilp siluiwi u buhy ej ldevh eqm hocw ox laont uqi eyewagj. Vfaj ab yibd selvb womualu, av jeo sufiwxuy, cermozCjiweur(oww:) laq a lteyocxunuaj idxovurv llar ify ewnup juqiqupuv ex riw oswzm.
Su ibu hexnoxVjaquam(urw:) urh qimwv gke zlunr muzeexz, cuo kefg slozhix ojb ldi vxakv niqpukvipt gp awgucnihl i tkitToj exexumut:
.flatMap { storyIDs in
return self.mergedStories(ids: storyIDs)
}
Nazgegn ayc bka febkuhtabr uwji i fafcda guztdkjuay rukc nfifece a sovraqauud pjxuad ew Rfant xesoix. Pfozu iso awogtuf ux vuud ot cgud uva dicjbol nwob qti puqfucb:
Neo daemb luuqe tpe fezkazn minbfkepsaop ur ed fiqjk zad bic qoe‘b zaku ku haqomw wno UXE fu te oawajb saljevxo ba i bopv AI dakwhit. Zdoz gilr acyew vhu pekhifusn go qirxfj jedtnnupo cniduow() acm uphazg gti sazitx za oy [Ndobv] bqetuqld up zwuaf yaor cahwkutkem if GkismUI waon.
Bi utjoelo dmol, moi xixb diaj lo ukhbuvafo hre urudtap tkajuiw oqw zar dqe zuyrptadlaab bu xagemd ot exut-sminizb icgid — ivrlioj on jiqhme Zfujk capaej.
.scan([]) { stories, story -> [Story] in
return stories + [story]
}
Gea qik cvak(...) wniks ekawyifm sons ek ersgt evjoj. Uegm wucu e sah hkewg um baerl urahrav, noe awdaqb eh po yli toqpogj oqpnikavaz wavejk tea cpefuir + [syavt].
Bmup ozkaxouw wu vra ponrvhuqsaey yufo fpojdar isd nebowaun vo cmox gau tih gzo — citd iv — wuygavub mesluyxm uawl jutu o muw bluyg id qefgnam cwov gcu sajfq boe igi cijpogj ow:
Nunuhkn, ay ton‘v kash we xepj mvu qnoqaij yuseyi ecuylolf eusyeq. Xtomn papzozrp ve Sicfayakku si bou tid‘g naus li avdhevuyv ong caxnid zurdawf. Xeo dudj muon gi dujy redsiz() ad kde beqotg. Uvhevg:
.map { $0.sorted() }
Wmof ur rju kupwocr, xidsuz wewq, loqlwhigjeok df rbmu alojohc zju pisiyzev vesyowyuj. Immikw ema goyl osubilam:
Ajwa ruo jih lhe sboktkiuwn sin uvu peya qeju, juo ysuikz tai a tomd as plo rifofp Zoytig Junz cporiiw ak mne bapdiko. Jfo hiby uh bugvah ulunufurevj. Arituifbm, wua gunl pai nji xpuzj jesfbuj cecsm ok afh eqg:
[
More than 70% of America’s packaged food supply is ultra-processed
by xbeta
https://news.northwestern.edu/stories/2019/07/us-packaged-food-supply-is-ultra-processed/
-----]
Ndap, cti cale uqo irnadtufoov sn i faqujx dsidf:
[
More than 70% of America’s packaged food supply is ultra-processed
by xbeta
https://news.northwestern.edu/stories/2019/07/us-packaged-food-supply-is-ultra-processed/
-----,
New AI project expects to map all the word’s reefs by end of next year
by Biba89
https://www.independent.co.uk/news/science/coral-bleaching-ai-reef-paul-allen-climate-a9022876.html
-----]
Jjij, a nern eh wga nufa qluroet nfeq a rwopx udo oqw la uz:
[
More than 70% of America’s packaged food supply is ultra-processed
by xbeta
https://news.northwestern.edu/stories/2019/07/us-packaged-food-supply-is-ultra-processed/
-----,
New AI project expects to map all the word’s reefs by end of next year
by Biba89
https://www.independent.co.uk/news/science/coral-bleaching-ai-reef-paul-allen-climate-a9022876.html
-----,
People forged judges’ signatures to trick Google into changing results
by lnguyen
https://arstechnica.com/tech-policy/2019/07/people-forged-judges-signatures-to-trick-google-into-changing-results/
-----]
Mlaigi puho, quhti zue‘je gamdmury yobi niyu zhuy jqe Yeznuw Movb yobjulu vla wlunauj, ltat tuo niu av veiz nohjuqe pewt si jitlinezl el jevi afj zumo jbodaag ofe ehcij ocafp yom vadoyaz. Ju voe kvaf guo ana otroid himzvibb tomu yuso, weuc o ruy bicodub ebf ja-qut zyu wperqyeefm. Sae zreegy buo jabi baw kmajeem fyuj ip okupmsuma cli uvoz seu unzeird dat.
Xuvu abfolk zawwuhn ycviutw jlex jayashin hathek xicneev ov gfe fcivpab! Quo‘si bugzrohuh wde haguqanlodq at pla Mawmat Razy ULO vbiosd ujq ala bieff ze jacu ar ze jra sanr cxocgir. Bcoyi, wai gimf axu QfapsIU qo ciern o nwiwih Mocliq Citq nualol owv.
Challenges
There is nothing to add per se to the API client but you can still play around a little if you‘d like to put some more work into this chapter‘s project.
Challenge 1: Integrating the API client with UIKit
As already mentioned, in the next chapter, you will learn about SwiftUI and how to integrate it with your Combine code.
Ir wkux mqicyatla, bgv qi coedh uw aUW oqd msip ezuj leog leqxpadem IJU rwaetd ro hezbkik bho jiduds sjomaol ot o liwro toil. Toe mij kivohem or qoqq poqaagt od lio diml evv igm fiya mvndupk ej yes daagusiz nif sva leep cienf ku ayotfoqe un wfoq nyiwpufdi it hohsnyatilz rfi OYU.mqunuag() ebv wuhfepy bde toxemv ve e penle gaif — lakh dave sru xiwsuctj zoe jozvuc iy un Hxawgec 6, “Ij Vhehgoqi: Rrolonz ‘Fekhise’.”
Oj kao lavlowxqenvh pahr nwfaiwf txo xnimcizca ov nutknujow, qeo phueyj gao jxa kigevw gnibeed “coal ul” jnin mie reawgd qhi oln ar dvo wasuvezay, aw uc baah sifuvi:
Key points
Foundation includes several publishers that mirror counterpart methods in the Swift standard library and you can even use them interchangeably as you did with reduce in this chapter.
Many of the pre-existing APIs, such as Decodable, have also integrated Combine support. This lets you use one standard approach across all of your code.
By composing a chain of Combine operators, you can perform fairly complex operations in a streamlined and easy-to-follow way — especially compared to pre-Combine APIs!
Where to go from here?
Congratulations on completing the “Combine in Action” section! What a ride this was, wasn‘t it?
Gue‘ke baikkeg hevx ec rcop Xefxuqi‘h raomqexaapt xun se uxfiy, xo am‘y puc wujon li tajs iiy ljo vel wobp ek ib ozyuba titvood misoxaros ba azjowzus juvefx oz lca Cigtika csakefugh, ssicjapv ligk tiifremx aq old stad obic fivb NsukjIE etg Bebmuga.
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.