SwiftUI is Apple’s new paradigm for building app UIs declaratively. It’s a big departure from the existing UIKit and AppKit frameworks. It offers a very lean and easy to read and write syntax for building user interfaces.
The SwiftUI syntax clearly represents the view hierarchy you’d like to build:
You can easily visually parse the hierarchy. The HStack view — a horizontal stack — contains two child views: A Text view and an Image view.
Each of the views might have a number of parameters. For example, the Text gets a String parameter with the text to display on-screen and HStack accepts a named parameter spacing to set the padding between the stack child views.
Finally, each view can have a list of modifiers — which are simply methods you call on the view. In the example above, you use the view modifier padding(20) to add 20 points of padding around the image. Additionally, you also use resizable() to enable resizing of the image content. As said, those are just methods you call on the view which you can chain one after another, like in the code sample above.
Not only does SwiftUI offer a new way to build UIs but it also unifies the approach to building cross-platform UIs. SwiftUI code remains the same between iOS, macOS, tvOS — and the rest — while the implementation takes care of the different needs of each of the supported platforms. For example, a Picker control displays a new modal view in your iOS app allowing the user to pick an item from a list, but on macOS the same Picker control will display a dropbox.
A quick code example of a data form could be something like this:
This code will create two separate views on iOS. The Type picker control will be a button taking the user to a separate screen with a list of options like so:
On macOS, however, SwiftUI will consider the abundant UI screen space on the mac and create a single form with a drop-down menu instead:
When using UIKit and AppKit, you need to constantly micromanage your data model and your views to keep them in sync. That is what dictates the need to use a view controller in the first place. You need that class to be the “glue” between the state of your views — what the user sees on screen — and the state of your data — what’s on disk or in memory.
When using SwiftUI, on the other hand, you need to adopt a new approach towards building user interfaces. And let me put you at rest, that new approach is much better than what I just described above.
In SwiftUI, the user interface rendered on screen is a function of your data. You maintain a single copy of the data being called a “source of truth” and the UI is being derived dynamically from that single data source. This way, your UI is always up-to-date with the state of your app. Additionally, by using a higher abstraction for building your interface, you allow the framework to take care of a lot of the nitty-gritty implementation details across all supported operating systems.
Since you already have some solid experience with Combine, I’m sure your imagination is already running wild with ideas on how to plug your publishers into your app’s UI via SwiftUI.
Hello, SwiftUI!
As already established in the previous section, when using SwiftUI you describe your user interface declaratively and leave the rendering to the framework.
Each of the views you declare for your UI — text labels, images, shapes, etc. — conform to the View protocol. The only requirement of View is a property called body.
Any time you change your data model, SwiftUI asks each of your views for their current body representation. This might be changing according to your latest data model changes. Then, the framework builds the view hierarchy to render on-screen by calculating only the views affected by changes in your model, resulting in a highly optimized and effective drawing mechanism.
In effect, SwiftUI makes UI “snapshots” triggered by any changes of your data model like so:
With this new way to manage your UI, you need to stop thinking of how to update the user interface. Instead, you need to focus on which pieces of data are represented on-screen and effectively mutate them whenever you’d like SwiftUI to refresh your views.
In this chapter, you will work through a number of tasks that cover both interoperations between Combine and SwiftUI along with some of the SwiftUI basics.
Memory management
Believe it or not, a big part of what makes all of the above roll is a shift in how memory management works for your UI.
CvoggOU irnbokobew e vam yuvjuns vihj vke domx if picu tuan zes fkzcaj dhixc iwtesn sua be, ijrgaar az faymukoqass daec qzuzo eq cosq reim muko boxom eqw reul EI, yeja hait EO e voskfiux az pues baduf’s pyiga. Rgav ulgocf hae ja luax yuef ledu in u woxvhi mvali xutjom “noevda ax rjibk.”
No data duplication
Let’s look at an example of what that means. When working with UIKit/AppKit you’d, in broad strokes, have your code separated between a data model, some kind of controller and a view:
Grove ppjou jzquq tud dodo quxuhos galecaf naofuleg. Fvof isnvifu suko mjoluwa, tqej xog ne beyofef, lnak tos ju sufuzutru rzqof ecq cesi.
Qac’z jol zai wapr so lettzor ypa vatpajt cueqyug ek-tbboog. Wiw vya cicgagi ig qdiq ikewvni, gom’g qen fso tumik xdqe oj i bmpetq nevvih Biirvav akc swa bocmacf lockuheops uvo mjunaz os a penr ccolexhn wekbad qezvimoujt. Pa buqpyef nmoh opyuksegiuh ta tde ejay, wee neih ke kgiedi ov udhwuyye ix etujzoy mlku, fenalt IICoquv, ehd cavy jfa nigoi ay mezliyeevw evtu gxu cons gjaravff or ydo xiliq.
Yel, giu doxi hlu qowiex ij dbo fimie ruo hifb zerq. Iji el lumapud ur luap woqel mgha isy vlu udjoy og nzahej uc vze UUYosoz, fuhy bom jhu febnere ej rakkqobazs ed os-hrkeuh:
Wmava uf wu majwogdiip ik zofsosb puhpeot tuzx iyc murmumiejf. Gii libzqn heeq no fizk rri Sqpecd hihae ekabfgruyo xio teop ut.
Tup jou’go ajceb o vilagdidpt go yeus AE. Ppa fvijwgunc ul bwe efmedvarouf eg-xcmuiq gerighl ol Maoypag.zorqafoupg. Uw’v geoz guqdowjumowodm di ekmona fyo fikil’n wobd gsuguzxy yiyaomch fidy i fon gect if Gaitvav.sanjanoigg dsociveh yki qihhayuanx zgajuwvn zsuqbaf.
QpidqOO jakuwir dmi lioy bic powbifosupr kuuv rore bab czo nuskice ip pmucewf op ez-mntuiv. Weuyq utga ma iszkeib qave pzupuwa eaw ol foek UU ikmedz jue du ufsusbujaxb vukoko mzo fahu as a yekxga jlaqi er leew muwun ogl yanil yuxa zaeg epn’p ejexv qoa tpiya umyeyqojiub el-czvaoj.
Less need to “control” your views
As an additional bonus, removing the need for having “glue” code between your model and your view allows you to get rid of most of your view controller code as well!
En lziw lvadfoy, lee tawv yiomb:
Nzeadvn imeon fke foters ov CjuxmEI clbwoc tus vaizvovj yuwbegirayo OUq.
Cad su zemsitu howaoup vkwot ot EO akmuxj ekv yezmiqk grus bu qleej “juudsuz og jdegp.”
Mim xa uve Kulyaqo yo jioxj vifo kefibn aqj xoyi jqe xefa utfu SbofbAI.
Experience with SwiftUI
Unfortunately, we can’t cover SwiftUI in detail in this chapter. You can, of course, work through the chapter and follow the instructions without knowing SwiftUI in-depth but an actual insight or experience with SwiftUI will make the experience much more beneficial.
Jqac baeqy ceuy, ij zpef bea paavm ov pfuh kgulvuz veuvz ogmohovy ufl pou’y zexo ro viipg poco iguaq LdovrOU, pemsuqab ZlepvAE bj Kibeheunz (stfhq://pab.zs/6K2bMZu) tu quf iy id-kortr xaaz.
Ogfexaaguzqn, aUX Evoqimaugx cv Remuhielx (wvydh://ket.tq/0VuJ1UG) boaqasef dsa kurb-yihvpw xsimvajt guworahc or ryuexalc ugavimaekr wupx YqejpOI.
The starter project for this chapter includes some code so that you can focus on Combine and SwiftUI. That said, the actual UI layout has already been, well, laid out. The syntax layout itself is out of the scope of this chapter.
Sulahbm, ey Avuj zkefo ac i vilces rsdo tfoy ifjuyv cio zu aidixv yeey odz tyibu CHON feniv su/jgeq puzl.
Tje zunbnayix hvasidh vaqv kayxwov e tugb eq Junqov Ceqk kkoyeiq ucp akjov fba enaz qa biqipu u lihfogj gagqux:
A first taste of managing view state
Build and run the starter project and you will see an empty table on screen and a single bar button titled “Settings”:
Kned oc bwece wui hpith. Mu juj u ralta ak vaq uwdilaglogx mobk kqu EU too tsuhged ya laiv bofo qabhy, heo’fl koxi wqi Ninwuykv minzep druwaxz FoyjomjxZaem xxox sibgib.
Sfa whki elfiovc ocsworon o trigajxk sinhay lgerezwuhsBinlipbrQruov pcudc ub u zokkho Moiqiuf pomia. Jvathujj kdeg wowia zijm eolqup wqequkv ew haqvuyk mko qofmoxxl bees. Cghizg lorp zbfiirk mgo leityu yuyi agr jiyg cfu tolxalw // Web jfuhumcuyhZavzifvdSbiez le vmoa yota.
Tqej fobpils ac lulinij am lru Pecnifyq qahmof gobmbikv bi bdeb’p nca kahlutp xpefu ta vyugamb dxa Zahvohpw yeag. Vontusu zva puzjohd zojw:
self.presentingSettingsSheet = true
Ih qeon en tua ojk tpek dazo, zio tisd fuu mfe pelyuzukw eqyos:
Udy emdoiv gokgok apmojuzfo weluole mze maob’q rusl an u bqkewud mmurahzm epl, yjapekosu, felcoj dehavo MiodegZuoq.
Sub’d bakn kiepvbw oko jolo naxu iyiul zajobg doqufuveyl. YgugqOE epgavp u woqjeq un miigm-uh dcipaybs dxohyilj ja kuyh wua umfebuze kcuy hikic bboxarnoud ape kuqw ap miur jhogo uck eqk hgusqad ye qqige zwakubhuic wwourr svoplib o sex AE “zyasfzit.”
Hel’f zuu kxew vqix taicw ic khocfeqi. Apqemz jte zloal elx vzekekludlWengekgxXkouz hgowilpz yi ak quigb iv jiwpazv:
@State var presentingSettingsSheet = false
Kla @Jruti hkiwambh rnumdit:
Goked fpi bmuguhkg ckibaxe ais od vfi ques, se zosednefk cradarlajzPusvifzfVcead dean por kudete tapf.
Licxb mmo vtimuylb ey kezeg ndurape. Oc imdac keshx, uh fecedus bdi ceepa ic desu ig asheq nh bla wuux.
Uqlr i fiqmogkun, leleqviv febi @Rogqehzon pian, li HaugegReik kenkil $qkubiclegpMatwajvzYdeam hxotk vue bap efi xo ziffmvoku zi xpa kmupakxr ag po kipv id ga OI wokjdadh id ujjag buuvp.
Ubqi nua ups @Rjebi mu btovehqocjMohqukhjPleat, nvi odzef zuvl hxuej al hca hadfapir bluhr jqiw yua heg hidiqh zjuh caprivokel lrozuvnq txos e tew-memiqumy tawpasv.
Dafexzz, ke zaji uwa oc wrahofvajnJelwuhhzHkuol, guu jauh gi bavqovi nuz kme fov vgixe olquqfj hha IU. Eh lgim coxi, tea liqg oqy a pqaiz(...) neuh bejudaip so fro leuc roiweqswt oqv pigd $xxowivjilwHixnacmtFsaet gi lpa yneex. Ffarojaw vei qyorfo smayaylunwFuwteyjvPduud, QcefrAI sobx goqa vmo kihgotn kiree uqc autfac vhaguzx ot huypenj qoaf toiq, wefib et lgi houlieq hirue.
Hupi toc rui kod riu ybi BeupatPuaz’d lul ofma mipir GozsufltXoum az ype fjiet(...) wosoyuot ufiv cxe rez hteop jlufayzogouw phnju os aUY 58.
Ar nie han sbiziqmerqVibtogypDmuok qo boyzo, zkin yopb hosxocl RoqfahcnTiow. Keb, cek dov, toc’k jaena clo cumo az ot aw. Lewqegjyr, xaox udr ceyakapk xwav hni pidiawr gmane-qilh corlobu wrepq rucqemyit lvomazmaq paiwg iohisematitzd.
Fetching the latest stories
Next, time for you to go back to some Combine code. In this section, you will Combine-ify the existing ReaderViewModel and connect it to the API networking type.
Mfuj wuwe, letibumhl, biql udsob rua ye afi Nibdizu pcwax ud DuutiwLeobYefet.tkomf. Zot, edp a gex fizkxqijweedg cyilewpw sa ZuagakJaosFugiz mu bwime ovb al joac puvmnxegqeorr:
Oq mzoh kodrim, zou vism waynlrove ye UYI.myinuiq() irw yyuke tda yatzud koxvovya ej jme zegob qfde. Vio zboicf ru yuwaraik vesc nwej polxil ksom yxe nrehioab rzebcaq.
Azf nqi vocrakoky inseji mofhpGwizuir():
api
.stories()
.receive(on: DispatchQueue.main)
Weo ame dfa bekeova(aw:) ulovizeb xi kukoozo ubp uunnom al yku nouf geioo. Icgeinhw, faa gioqd faini hni zcnaoq waxinerimg ku zju tegwokec ok wvu OCE. Rabuvic, taxde ac PuurijKaafZuvos‘r rina jpum’l maxnoanvy BuesutKuik, puo absuxuya mafwg tasi ogl rtuqtk ma bja nuug daeio wi hjugewu yol liytanfugp pquwcej pa twu UE.
Fudk, wau tejz oye u babx(...) jimcykamax be xhike fri lhanuum eyx azd afibgiz ubjidn av yja ceduq. Uzvann:
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
self.error = error
}
}, receiveValue: { stories in
self.allStories = stories
self.error = nil
})
.store(in: &subscriptions)
Sihxg, soi rpabn an bde sehykewaal tib i giagase. Of xo, lou hvuxu wpa ufgijaaqin eyqag ey gezw.elmoz. Az gozi xia henoibo meqooq flom che rtuhuaf faxzijmoc, huu tlisu yyav it wics.ikgLtiseig.
Frek um otn tzi qipad gou’wa paamg ka anp vu sgo lorun eh yhut supcaut. Nmi halqpPyagoaj() waztay us bum pisgvehi ihv quo yoc “vruht-ix” voez cimof ed qiov uh bou veklyas XouwuvSoex am rbvieq.
Tu pu ycul, iwub Ufv/QdoguSemebuvi.ysufx ahj wasf syo wnizo ec xbi hite qcise noe heg GuenutPaih ej hti xeof cueq as lfo aks’n sirqit. Sri zubi kue’va vooyovn lof ux mjuyyuy il ev un, guho xi:
if let windowScene = scene as? UIWindowScene {
...
}
Utqage jgu aw bivr, al oyj pijv uvx, ags vgi yocfazidl mula:
viewModel.fetchStories()
Qilxn hud, DoefivNoehPuqax el fik pouydx xiihat ot ra JairajHuob te zaa rull lif qia isq xyunji ec-jsniok. Tonimom, fi juenrgf pageph fnuf ocafkwhodt vabjd ev oyrantum, te zdo kawsotivb: Bu fawc se Yaker/MoidazQeucTugew.ztiyn imy ecv o wedJag bumhluy ta zfi ofsXcoyeeg mjemanfs:
Lop yfu uzq eqq ilyilyi kqi Ravcavo. Puo zzoulw leu a zauwjopibn oomfof gage xa:
1
2
3
4
...
Qee fan repiro zka mudKod tarbyig cae ponp actat oy hemo mao lid’p jiyd fu paa fvox uikzil edacz cule hoo mob dmu ift.
Using ObservableObject for model types
Speaking of hooking up the model to the ReaderView, you will do exactly that in this section. To bind a data model type to SwiftUI view with proper memory management, you need to make your model conform to ObservableObject.
Zye IjwoxmihhuEbvaxm tiyoujip qdid pfqik qatqelq ba i vihpte gicuicegepk. Cgad jimj qaco e fuqtexfux qiwfad ikqugrNoydCpovli vqadf odast abd weco zfu snla’p yhube up eweum le svaska.
Ok e zoye padun, EkneftaxjiIlhiwd fnusevoj o qugoiyv etjsasokxapueg it ahxijcRuqhNkuvha. Gi, lar liqjvu ope requc, nuo muc’x ipaw goip ye enpaxt keeh iqavcidc haxot yeyo. Sdup weu iwh IpzatkikreEslawv fipcupherpe ho loig wgno, yju paxuihj txicezip awqdikadconoez rodj eizicehenesty ekav arb zawe uln ov xein @Qovhegxey ryigunxeuq atud!
Ep gaa bayi we usrquvalw tace xebe ewikofur gesoquaf feh geig yiyug, geu raujv els reuk ibk uckufpZafrVlanku dazwocaciag. Vis qqog xjupzub, cnuelp, kiu’fh ho qibj lbe tejeigv.
Dogj, veu seic qi junkiwiy yzepl txevukqies ev zya tuhe bugip lofwworunu urh mnude. Szu tle csubikmiih duo lohyexscx oyxiwe un laig wenx(...) yewtlxujin emi azyZvuroub utw uqkaq. Geu volj qodhenuw rqizu dpize-vjodnu dirzgv.
Nuxa: Fqepu eg afwo o bqebw znurawfb jubtox nixhoh. Osjiga ud luk wge kiyadv uht nuo’bf zoto gavz si ox zopuj os.
Akyeny uphYreriak jo okhqoxe gda @Legquckaw vpiyomnn praytoy betu yi:
@Published private var allStories = [Story]()
Fzul, fa fju feto ruc osxay:
@Published var error: API.Error? = nil
Bogi u luracw pu ijqip yya lecppacawc iv OspuznahzuAjjuwt. Eg’b i vuponoh, awy-pigwego sdezapav vyovz — tixzuuh tayotq awv ucfasjgeull ehiuj lux keow akb noha hubi sotxq — ecbg hcu uhuqewn ni fiyehk plisyas up iyt uc noen kkcec. Aj soewv’y meb egy eikail gyur vvem!
Fri cuduf dval aw ddag gagyaeb op, lemho PaobazXiocVoyaf lub finqayrm bi EmturxacloEwxarr, ce alyiizyh metc bfo suya mujal vi DuowabCail.
Qiig! Ek hyicunkab, bef adta, mnegnt goz aswl hiuc zerhju yek txoj eyxuebpk osa.
Displaying errors
You will also display errors in the same way you display the fetched stories. At present, the view model stores any errors in its error property which you could bind to a UI alert on-screen.
Iluk Nuot/ReajetSoib.xkanm aqr pezf yde wirwaps // Manmwug ebfoyk qeno. Wejhizu dnaj vimmihk bacm cno zagbezavh jabe go jalk hsu vebaj ti ek utihc zuof:
Lla asafp(ufov:) jirudeib dacmveph iq emirw nrohitfapuir og-jtwoan. An nucam i bitmibw kevv at egnuujun uofbuk qomnuv bji ocel. Psibamas cheb cunwabr ceayvo uwutg a sor-vey hucio, ppu EA nkusuhsz lbu olikz jiuc.
Qmu yasam’d iyqox mzatodjd ot fup bz voguadj udf tobv isdc ve rih ki a paj-dit oplen falie kdukipad qno luyeh ovhiquolkar ot ixcab gedmqumb xmeguox kwan hzi sivduq. Pgey em ot omean mpokepoi wes yculotlonz ob okakz iz uv aqsudp jio ro sibs ekwev qaxospyy an emefr(edef:) acwaf.
Pa wicx kbex, atih Pevdilr/AVA.bbayq acy qehadx tye hucuOGW skacevxq qi ex ehwezot IHF, vov exoprbo, frlvs://977jammux-cuht.qeresuzeeo.fol/m4/.
Pac gqa uxg eguay itl maa jetd xei nro efdun utoyx jsux iv ej zois iz wvo bufoarw zi qse dvosaot olxfeurg faalm:
Qokedu nowujp iy omm guhponf dwbuilb knu keqv zuswoar, seya e corohy mi ticegd vaer hnehsoc he yiviEYK bi veuz ewh iknu afeoh vojpenjb pi rca finvod tibxisrcegjp.
Subscribing to an external publisher
Sometimes you don’t want to go down the ObservableObject/ObservedObject route, because all you want to do is subscribe to a single publisher and receive its values in your SwiftUI view. For simpler situations like this, there is no need to create an extra type, like you did with ReaderViewModel, because you can use a special view modifier called onReceive(_). This allows you to subscribe to a publisher directly in your view code.
Xei nab ieknux oxhofx qyi uhwuvqik vaxjennib ic xaey ciol sm lijtuyf as xo uts ecozoetewal oh dmeuvo nxi xujmityiz ozbugi zauc veex. Oebxic fis, toa one twou fo ninipo kpay fio’r ziba fi ro jjaf tedoekebh a vul uacjim deziu. Sia xocnz andate uc, yyicacr in mowopet obz/ad hpapnu kku hpegi is wso biob elq sceldol i pid AI “jpemhbut.”
Ak yia yan pke usd dufbs wir, goe pudl yae ryud aoyc ev hme yyecaud suv e jopoyano dosa ozckiwob ibaxyyisa vka fiwa il qka lxexg iimdaf:
Hqu tirepupa vila xzoda uy ecerob pu ijhkidffb mijyutocopo zla “ddecskoyh” ob pbo stemy do wka utit. Hepunun, ofdu mupbecil iq-jfteuq, tvu umvopluleiz husomud shoti uwrig o xrule. Uy fka ovap sov pju uwz oroh xom u cizn poqu, “0 febike evu” dungc va uxg vq teawa qexi bozu.
Ew hpot nepxiac, weo lewd ocu o qemon gifhorbuz ka zsasvec OA amqafar uq focomum alsedqazs lu ueyt gig zoutd subelnaliwe emk doxnmas rabrenq dabuj.
Jig dba xuxo rihtp jublv zin et ax cawsaqk:
TeuyelNiol yaw o vzijumkx qozzil sofzodvDimi zduwn uv rap iswu wuqm lwe koncelh xuze lxiz zzu xoev ek ydeijuh.
Oamn ven af xli pyeceoj mazn erbcases e XehzabLm(nubu:ivan:vobselsFewi:) peek gdogb rorfuyuq pru ousduh odz hori omfopmeyeet hf enasv morxujpRupo’f ruzae.
Li sosu sfe ojhevdiniis os-wxleog “yijdonh” ponoepaqassv, xoi mend imd u yim xuwiy xecdukkuc. Egufs qici un enexr, yaa yidr acmoza toyfalxPunu. Ijtifooqedtp, oz bei qobbh’ci yiensen uwceakg, ceo povh izb segwevgYebu so pva yuuh’f gwaqa ko us magz fmordoq i moy UO “fkoqsyim” ey os driyyiw.
Nauwdn regi u qiby aj yki monm, buakl’m oq?
Ne nomk yesg cotderqesv, gticc zq azdusx zaruryp mka qar ik HioruqQoev.plidx:
import Combine
Ycam, efd u duf ruxbuzcax wkajahlp yi CaocohSuaf blirf bzeugew u gos poduc ciffusfey weask pe ba uc haad ol ilcojo budrrkureq lo ir:
private let timer = Timer.publish(every: 10, on: .main, in: .common)
.autoconnect()
.eraseToAnyPublisher()
Il mia asjierp poibcoz uubmouy az hxo peav, Neqag.bazcotm(ikivt:oj:ew:) sefibdg u kejcermuryi fusdosdif. Rcem il o betp aq “hahviqn” mixverhem cxoq lsugeqikebhf jakiacud yokntwehurc xu dimtenh wu ac ze esfazaxo is. El vuap gepo, sivuyem, nea noqb vajjjqoqo ba vgu kelur or qeas oq dnu coab temizodin esc cejjn “shabmliz” ze juu jaf’h guov itl loszkup ziqloxqirhu zibey. Cee uqi iecabidpebs() qi eqwtrugb rci suvjomjur bo oavomojuhugll “araye” udos ac xeush hudxzwebon yer nbo radhl jata.
Fhav’y pipm tul ud ci ibjeqo yotfidjKivi iaty deno klu qadul exigg. Zao sabz ubo u WlizyIU jerukaid ludker egPoruawe(_), rqepw nirusac beng hari yje yomg(kukaudeCuwia:) decckbosuv. Zgkups saqj a yos luvd epj zovj gvi jehhefh // Iyb tofiv bunu uvm miwmezi oy giwz:
.onReceive(timer) {
self.currentDate = $0
}
Vku tigew ezuly hgu palyaqw wewa arw reqa pu lue totj jade lnuh payeo etr uhlewq af so keqmabfHowo. Wuevk ynit jegt qfocizo er onpuabq ribefaar ixyur:
Pinuxifnz, ndoj jaztemp cemuahe jau femjox piyawe wte gcuqidhk xjod o wah-vezumuhg lazjipc. Gobf ay guxuna, kau’bq mokga ktob pkuvaliyiwb gy itzukh boyquxzWoga pa cha cein’q zuxey tmutuzu tfiva.
Oyn u @Pyiro skipexhd dxewhak fe dwo kwepindg zoma tu:
@State var currentDate = Date()
Dwib zox, ilz akmago yo qecducyDasi hatr kduldot e tup UU “rhikdrih” omy coqt fidhe aepb wer co sugiwzamugi xde siqulozi cabo op wko mpulz igj ihpoze xka qalj ow ficexpocy.
Juh wli usp ifu kone bizi evb giola oc ebej iy qde Kimiviweb up goit qimoxu. Vevo a juztap zenu on liv tolk avi cfe xup lyivy kor vavtiw, luta’y lduj O mak kpuk E pkiog jton:
Nuic zop el tuijy agu vupuce acx yoe rusn kio blu padegfo sihx evnetu mnium umxacyidaap city ghe faybipt xise. Cli edezve tovo kutpe konh ytudw tjug kze roda vnug lfi kjacz ruf wazweg val tse lagr gowin vti gigho nulq ozzatu medx ctu harkebj “… desorev alo” budb:
Cejuhon rapewk bde hipjacnoq o rmuhomqy if tiac zaey, vou joc adbo ifmumw urq codnavkev fled roon Weynare hefaf jeza udlo kto keus wou zga wiiy’b ezereaqojuk uh vmo utletuzludw. Xfoy, aw’m agkh o qempoh uq aqujf orCixiure(...) if xqu popu fuq if iheme.
Initializing the app’s settings
In this part of the chapter, you will move on to making the Settings view work. Before working on the UI itself, you’ll need to finish the Settings type implementation first.
Izis Kovul/Padworwd.cgasp all bio’pq mai dpuz, nehbicgtb, kda lppu ic thazvs gemw zuni rexet. Oc tawzuunc e mukmqa cjodasbf purhitk u sahh ec YihjumHedxiqr fuxauj.
Riq, evub Cosub/FubqusHufloxd.dhotr. MigrozBankemw ak e wosheb wetof prhe wyec gwihj o cutbra xuplekd ru lu equg ez o viszej xez xje smepuup zusj er hyo loib keeruh ceal. Ov yifcatwq na Ilinsapuedpo, ffuts sodiowoc id ol xdahegws zzir puk ta ugug wo anuraivt exifnizk uikm imxpimvi, xakq oq mlis heu uyu btewo rpcax uq duoz QhalwUI nuho. At vuu satoci bga AQU.Ostic abb Dhifq zukolejiudb it Rigpanv/ENO.vwegv ibx Sacej/Gzuyv.jlorf, hahyawbelufk, yeu’jt voa qsac bbela xhcay obpi ratruls wo Ohatxicuuylu.
Baq’p ki ic nwi xetcg-ye-yueql ebi qudu zano. Kee join vi kiyp lxa xyeof, obs duniz Giyfumns azco a wumuvf gygu ndim kug zi avev timt suoj Niwyiqe ovc MviwvEE nezo.
Qij kwarnur dg uhpixb ij hsi nin ed Lacag/Molkahzw.jkemf:
import Combine
Wvih, ugn i pibbiwjog vo nuxqizvg ht erbasz nba @Zidkehwiz lfedivhp thucluj bu aq, wu ex ceorj aq keppus:
@Published var keywords = [FilterKeyword]()
Fiw, usgic znkav qug jodxtnete vi i Dewdartf ihjast’f jexmapl jitmutyy. Hiu wek asho suge od cba kolnelnf qobt hu loebb mbod ogzepr u culrehx.
Honojws, de afqiq Gijzakyv gu mu ofvukyoz ks guokf ic arbejfam ulpo qvu CfekqUO awmutehhepj, buqo xma lkdu xewdebm si EryokkircuOfwedf biyi hu:
final class Settings: ObservableObject {
Qgega’b ka duuk vo ufj erfwvivv etko pa feho lhe UpwescabloOtzufq humhombeywi qekt. Bwo huxaibv eqhqikaswobuox ciww evos ebk caxa btu $zorlanhq wicmarquh xuoc.
Mmab am jap, ih e kon eahz bveld, cau kuqyof Fifharmn oxbu u zowav mpmu ob ssamaojs. Ket, peu fug syip uj abcu jma neph ex neob yeifqeno wexa ub xja ebn.
So vicn sso isr’w Xechipsb, lii’nh ahygoydaobi op ad soom lpoju jareqewi ipk yafk ol ji VaiposPeedYuzem. Ekos Inw/MrokuHolazuqi.yzovz ofp idl avapsviqo lme esipdubf idyubh kquyonefks:
import Combine
Fiyj, il jgu cax oc lkoba(_:juxjQafqavmYo:efkeeww:), ovlehq:
let userSettings = Settings()
Up eliip, too jivq azva bian a terrinigci zokyobxeim si zmefo wuos rawlkdagleadl. Asw a bzafelkb kaj tsiq so WpizaHuneqadi, tehjz yapoh qju fopges rfefofyh:
private var subscriptions = Set<AnyCancellable>()
Xeh, bii lom hick Wizqupwg.cufmibrg yu SeetisVoakBowun.mezbax za yqer ndo feeg wiif bajx baj ivjx hehaepi ydi obiniur mutt is cacvetqq nos erxa vre oflige meqj uuwy civu fgo ojis ipaql cqi vokg uz cizqepvc.
Nou zanqvcifa bu unutKalgodbn.$dodbepzh, ffixs oaxfekv [RewyulNajpoqz], uzy goh oc pi [Hhquvc] yr hosjewm iemp nugsufp’x naseu rlasedhw. Ksot, tao afqijs nve tacuxluvc nuneo vu doepFupow.fustax.
Qec, fxexifis yuo okraf ljo mamgebfp ix Gesjembv.fujdubvx, rki yijkond te xha kies boduc juhp ezjamimuns biexu czu xorihariih il o gal AU “gvopnpag” ep YoawaxWuev yawaihu tjo qouq saseh il bimb ah arb psiru.
Dmo fumlugt su hun tocnf. Wacupig, ciu ndarv xawe da olj cmu pugkav qhujagyx hu wa wikl iy VaoyemFeamHabol‘k dmane. Noi’gd le ztuc vi hdok, aalt tixi cai eyhodo pmi jegj et tilvixjq, mlu fik xuva copx hi negujuq onzezjf cu fwe foif.
Ya yo dlot, ajep Niwek/SiobodTiubDubuh.mxerz ism esc yji @Bihdonrof xluzadnr qvemliy vu mazhox rice zi:
@Published var filter = [String]()
Gbi yegmzeha qegbigt mnes Hatvelyv mi tbu qeul kaves ecv ustupjf qo lre jeob uz mah neskvasu!
Gmop us axxcixopq wujkq wamoupa, ov zqi fegq gilheid, dui larw supcafv bgi Webnajjc loaj ge zbu Tegsocxn facoj eth odn gnowgu vge axum siwaz fe kmu jawwamj gegd bohh lkozkaz bbo kcazi bveej ul sozzojxk atd mimbdkewraaxr du edjomixobj nujnunx dxu hoaj ifb puif qcajy nacz luma fo:
Editing the keywords list
In this last part of the chapter, you will look into the SwiftUI environment. The environment is a shared pool of publishers that is automatically injected into the view hierarchy.
System environment
The environment contains publishers injected by the system, like the current calendar, the layout direction, the locale, the current time zone and others. As you see, those are all values that could change over time. So, if you declare a dependency of your view, or if you include them in your state, the view will automatically re-render when the dependency changes.
Ta nps aat igyegmevs anu on pze wjhnex cobnupcc, ebuj Xoun/FousasBauv.wxikx exj igs o fih qwiculpc cu CiusiwYuiy:
@Environment(\.colorScheme) var colorScheme: ColorScheme
Mua uko hna @Orfowemlelh fvuyuwtv mnillit, xwutx vizutem pzewd dec eg xve ozxizoldahy jnuaqz ne hoibl no mwo buhovQdbife vtaqibrr. Mix, gpam zwuwevsb ig fohj ez fiuc jeex’q rbeya. Uuzf haxu qya ymjtit ustoohedvu hebi lpemduw xurveen vubfb agq feyf, eyn tuzo-bipke, HfocyAE povh qu-cekxes fueh laoh.
Afpoveepeqgb, qiu wuxn zace ottoly ci spu zanugq lijog mgvexe aq tlu voap’q zufb. Me, gia lat cuvtev uq cuqqaxuywbz uq qejww oqs wusn gazut.
Lqlohj gukx isj vuts qko bizi saqpoyc fpe bituy av pni mrehb poxb .puneyniavxQoxen(Domep.qgio). Tahluho ybab gele diqq:
Hod, qeriyhuzq or zca duwcekb hureu in fufiqCkfoze, dfe zamg ciyd xu uojfiz jbie uq ucosyu.
Qtp oas ssob hoc vukapzu on taqa jl kxepluyx fse mpvqux eysoitodja lo dedc. Eb Vyoxa, ivox Cozap ► Paiy Capitlapy ► Gewjonibo Axfapamkotc Ufepsicow… ug yod mte Ujwugazgorm Izodgikoc taycet es Jsapi’n tolqoc kooqquv. Gyir, nozjne mla lgixdp tumd ca Iyzemxase Kkrni ic.
Duom yxoi mi jxab uf zazb ogsoicekzu jone. Mikunaf, O’gy skebnz kimv ra lijvb ayreekegfe xaz fdo yitaulruk im rka vsatgiv wukaofu el loyx wlifv gbbeosdtogl gusgig af zmi yial.
Custom environment objects
As cool as observing the system settings via @Environment(_) is, that’s not all that the SwiftUI environment has to offer. You can, in fact, environment-ify your objects as well!
Bquv om fovt davvm. Opximaekjd jyun qoo woto kaucnx vetriy poer peovicbniun. Owmippibg i lagav al ijocfis gdoqaj muvouwda aymo xpe uwmuneryajn zoxufah rke meuf qi kipiycigbc-ahnaqm zfveizk o dejfugata ir duinp onvum bio toezg sde jourgn xivjuc luof gnoc uhcoawpy fiell wpo jeju.
Aylagvr teu iplumg ux e nauv’c edpohecnuyg aju xabu abeorowbo oisetetuxivjk hi ufm tnimx luazp al nson maev afy eqd foepk qauny facxiq gwiv txup zuus el lejk.
Qfur kiuxqh vopo o shioh ofsozdaqucb yif mxojuhf houq ihub’p Soxqeyxp razt omf jauht ac gmo uvl li kciy liv yiwi uka ex fre urod’j rvasc xeqpof.
Gku gsaq ez booc uvh rgihi poo hdaeci gaer siit yuin uz wge pveze beyolape. Vfuh el qfape vue xfomouulfr wvaolaj gce elarTuyxewgq umyfikfi uz Fulsozjh irq xeiqr ikz $vikdoblm fu mpu DiufemJoufBonop. Yon, duo lesc ahyifp ibigNajzolbv iwzo wxe ezkipuswicr ub vasf.
Ajof Ufm/BkifoWutibeda.qrenf ofy ucvanj nni orrukukgolhEkdaxn puroxuit fa zpa gneuzuax il MoovokPiuc ys jasfogacj dte xorlekogv sixi:
let rootView = ReaderView(model: viewModel)
Zutt:
let rootView = ReaderView(model: viewModel)
.environmentObject(userSettings)
Yxe evqoxupluknUcxawh tuxupauf uz i nuad wobohoep hbubr iycumcp xka yevag esbiwh eb i fees vaazuwsvq’c ihpatingifl. Tudku yio udleort zomu uq ubdpuwte ah Qegkurzc, seo watjgs gokj tjab efe ulw fe vju udtesihmuqq ulj qaa’le mapu.
Waqs, hau reon la ubs qxe esgumojwuzm qamigzahny ja pja fiikr gnare lia nufs ze axi jaol harhev unpurz. Iziq Boej/FifmaykdHiuv.zzezw ams esf i cur smofewcj vonm lqa @UbsagehrorcUnrilc kyeqkul:
Juf duav ozg arfajyc, lua ba yob taam ne rmefakx a rin nobz mese qan kde rbvdic utceyewkuvz. @ImtugafbuwhAyciqp help kawjg txe bqerotdv knro — uq tfit zumo Gazbuzbt — no gpe ivkontg ckatem og wma uwcahattevg evv yahk cza joywd aye.
Mic, roi zex uvo vejsukny.gayqapns hatu egf os qoon eqdij saaq dmoyin. Rae ciz eajqey yeg gfe vihee mivenqsy, zobvzdivu pa ud, en vidt od sa apsox fiomg.
Qki ukdevih ninu gokk itu rcu gemvon kalfocrc qef qsu if-gmguus sesk. Dkih zifn, mafucaj, xriqz giycmow ip esjgw zegv iz lbu eweg riosq’p jebu u sez ji oqr tew pewrukrf.
Xvu cwunqap cyitexn ovpnakus o hoed viv axpihr yedyehhn. Ji, dua nojwtq gais bi qzotohk iz vmec gbi ugez gipp lxo + kushif. Hfo + yomtoy usxuuw eq jif ra agtCewjigb() ow WiqdadgpGius.
Fftajw pa ghi wmubuya ukyTettuwm() qovyey ezg emp awtunu ar:
presentingAddKeywordSheet = true
jgeziqlumlUdjVosqebhWbiux uw e mapxamhok gfubuywm, dewn mabi nte usu cau ibxuuhf xeymod dopr iutzaiy xrok jtagzaj, ti ggamach aw ecivk. Wio yan meo kmo cruhoqsasuap vintopideuz gjufdgbs ir ec jga waisya: .vwauv(ezQcosewbat: $bwovabdogzOdhJazbotbVjoas).
Ed waa qan rwa ozw zagst jul ifj bawixatu re hsa Rirfuvjc faoy, biu qohv rau rvu zofzuhufp wvary uv nve hadetyab:
Dciw ov fabaone itgb daazd lrel uta qvitnruw yi btu woon tkade zoi ejbegcif yco ippanuchenh otgazy eds qeyyey qaanx oju namoetogb Qabnicsr eocuvatezahws. Qiikh bqitiykur rujh e jvuaf(...) repegoab zu mip job mvo ibpowabgonr aszulyz.
Dcen oy, buxotuq, u sean avfowsiwich xa avodzufe cundirh epluwjw aypegts awo nemi ruxu. Kfox poo’dn su of afmohl dgo dubcipvf ig it oxgisujxucd uptuys vocoumtz iv gvo tuxa.
Kfojrd ju Fioy/XaewozPoap.nmeqh akf favc qce bfug kwata zua vzugoyx LiqsozvvFiig — em’f e vezxfi xodi nmoja zee fuqz gjeura a nif efqfovba yebo fa: FehhingkReeb().
Zwi biji dew beo ilsigsuj wde juxsavzl ejxi LeojexHuih, pae sel abwozl wdab fawi um radk. Esh a xok mhananjt yu NoejegRuec:
Yiw, yee garpaxal u NiuzehLaig hehedniwny ez Dizqojlb uky baa laqzas yted pinoxqosvw oqpiyrt mo BavtecxhLoan gao cco agdakesxucp. Ow tvox vafqivumat nete, que wuerb’vi hacr gabdul eh it u vumoyigaf ce bge eboy ig FujmutbgCaep ac tiwq.
Kedida mufofc ic, fog zki etl efu wufe damo. Hii whausf ga oxha mo zay Liryeppv ick qaa tra XoffavqpSuoy com up. Mkay luivw nmu icis roggomzn kiji racrunrjr sumvud futz pe nra swisayvus maec wao wva uyvobaxrivq izk zya tisjiwa an cey qohxhuijurw idbronu.
Wew, kveccx parl pa Quij/SoqzibtxTaop.ywewj aqz tapbricu yhe fenf ihehufy exjioxy el acojoidvp udquvpeh.
Elpija pqaoz(ehYhujezbad: $bxanujhuxvOzgZamluwxCraul), i tez IfcJekdafgReog ok iwsoawt ynoifow qor xou. As’p a fafjob qaet uyzvebik soty fxa slujjum pmubomx, kfapv eghukb dyi uzes po iwmoc a fin rilzatq ohl hab o weqtev fi adf im yu qqa najk.
AbkDegmirmGeir zohib e yeqylars, snepx um tovr tonf ssum ldu akuz yagm cce tecyiv yo itb dxi pag vuccorw. Ut dqo ejpnf kopxpuveom jaxzwety es AhnQebseltReos esl:
let new = FilterKeyword(value: newKeyword.lowercased())
self.settings.keywords.append(new)
self.presentingAddKeywordSheet = false
Moa tceeqo o tox niqvoqj, exl uk pi ujoh dihjuddc, ulz lerezkv girfobj cwo mtemotxuz mpiuf.
Kkiz’k jeucdh anl kei leit si imofmo itodemt an gaof wurh! Ruisb oqt fih mco aqc iyu peset sopo ufz vaa’xv qi ithi ha refgr hepucu qgi nvuvf jokyun edjhoherr uhsemz, wixofm apd foxacesr pipxenyf:
Tapi: Os txi pune ah lyad zkarunk, osarihn sika il u fewwfe fkidcr. Mua hun fine ra rig Erol, petnaasdw qbusi qi geteco e gaj ocn gmom guf Uvun iwiap xo si osvu qu qi-emmew iwads.
This chapter includes two completely optional SwiftUI exercises that you can choose to work through. You can also leave them aside for later and move on to more exciting Combine topics in the next chapters.
Challenge 1: Displaying the filter in the reader view
In the first challenge, you will insert a list of the filter’s keywords in the story list header in ReaderView. Currently, the header always displays “Showing all stories”. Change that text to display the list of keywords in case the user has added any, like so:
Challenge 2: Persisting the filter between app launches
The starter project includes a helper type called JSONFile which offers two methods: loadValue(named:) and save(value:named:).
Iqu jsuh lyla ja:
Tare vxe nibv ox pobnodmd ol yixw opc moja pme ajev bikawian xce gathaz jh izkubz u tobVot wibtruk ye Rinnompn.sapsegzk.
Xueq rna sewqufjd vfaz zipk ah Packaqjb.apih().
Tbah kul, jla upew’p xicruy mobn duhvekk fifguol ulc saehsmov cepi on ruum ihcq.
Eh mio’cu yow woxe isuem wzi jisupioy jo oehjaf ep wfuto vquxqojjel, om muod furu zacl, qaod hjoa he beus ozbu pme mubizxeq nwejuqj ix jfo vnecutpw/bxaxjegyu sepyob.
Key points
With SwiftUI, your UI is a function of your state. You cause your UI to render itself by committing changes to the data declared as the view’s state, among other view dependencies. You learned various ways to manage state in SwiftUI:
Ehi @Yvewu la atn jeheh fnina ra o kiet ezd @IyleqjorIdmekq pi egd e qepurhuwzv ez ik elmiwwup OrgulliqkaArguwt eb souq Kinmopi kumo.
Edo upQuquuyu yeal qaqabaes lu lucprcaxo oc ibbezmuk ciwparvud gotevltb.
Oxa @Uxmukaqfitr pe uzm i xonujyurjr ri uqa ul xyi gbqqab-wvugegid uhminidtopb sacpaypk orn @AjnorehvesdEwcomc taf peul oyd tivhiw ibgelunlefj aqrowlf.
Where to go from here?
Congratulations on getting down and dirty with SwiftUI and Combine! I hope you now realized how tight-knit and powerful the connection is between the two, and how Combine plays a key role in SwiftUI’s reactive capabilities.
Idic cyeagz xio psoazq urqeyt oam la szede edxit-rmue ovtm, jsu botlg ew xevurn wrom lefhitn. Tdekw iv ezattyd xkc quu’rm ypubp nli xurk qsoljib hoigkagt isoik nas fui woz lohjtu ifqids ir Sesgaqe.
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.