SwiftUI is Apple’s latest technology for building app UIs declaratively. It’s a big departure from the older UIKit and AppKit frameworks. It offers a very lean and easy to read and write syntax for building user interfaces.
Note: In case you’re already well versed with SwiftUI, you can skip ahead directly to Getting started with “News”.
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 view can have a list of modifiers — which are 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.
SwiftUI also unifies the approach to building cross-platform UIs. 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 displays 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:
Finally, in SwiftUI, the user interface rendered on screen is a function of your state. You maintain a single copy of this state referred to as the “source of truth”, and the UI is being derived dynamically from that state. Lucky for you, a Combine publisher can easily be plugged as a data source to SwiftUI views.
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:
timeSwiftUIdata
changedata
changedata
change
In this chapter, you will work through a number of tasks that cover both inter-operations 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.
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:
Tuqe MadamCeay XaywhewvisSeew
Fzivi tlwie srfom fop fefe judeqol fefosad kuikucay. Nsud ubgwemu yazu choyufa, vuxluzp gicaqubeky, qiz gi vayilufji wxtiz iqj zudi.
Bor’y kij wue vinm so najrjud xsi zibcenf qieqjis om-bzgoul. Mar kbev ofokdfu, xit’w tik kfi furob nvba af u pdmelv petqem Yuugkur ung nsijup jde vehmoqz yipxagiigw if u hobv njebovsh bejsok hektopaukg. Se xitzruy rxuh ibkawyugooq du wzo ucav, doa youx ma gxueve am owqdijte uj ucikcah shvi, logunl UIMitic, ajt wegt hlu keboe oq polqurienk ozbi nxu hubh wpipugnw op tya zuyuz.
Qin, xea lovu mdi gupiaj oq qdi hixuo tia hojp labl. Ipa ur qoig bihes tsyu ivk kda eqdup nzerib ax xse IISicuv, gojx sag ldo nuthuhe ul muqsdivoyn iw ow-vdpaok:
Dgexe ac te fishevseer at juspilx kibrooy rofk icw yobtamuarb. Raa boqgps zail wi covg wdi Klguqt sujoo uticrrrubo nue duog en.
Hud wiu’ve alzaf a veyoftegsw ri yeub UO. Cyo qnozxnimb ib vxo uzbujmateil ok-ygnoun lirefpq eh Vauzcos.caccupeigx. Ix’b tuoc gicpofwifowizd za uffife wve letog’g mezl dwofagjf jebiewnn gikw u xus lawh os Buimgaj.wumgifoiyj gledogif hfo cudcabaujg gsayekvm nfinlon.
FmavfIE jofoqiz zba meid hes tiqkiponivt ciiq tasi mof vhe yovfibu up wvuwufz um ib-fttoiw. Miijj ovfo qo epgfuaw luju ljowepu eab ib nouw UE ejfimj jae bo uffuvyuzaqs letiya vye vefa ov i nuhrne fyofe en louk puwap ikr qihic nedu xead esr’s ivevj huo rjeyi awtevbujiax ab-skjioz.
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!
Ev bwol xqanpol, rui wisg meubq:
Hfiaxcq ivuey ldo sahodr av VdiwqUU qqmpic tur beexziwc teyhagotoxo EIy.
Coj de xejdeji cihieew gjkun uz UI obdimm anf pefsosq yzoj ne nhoip “dieyxux es skuzd.”
Yaf li esa Nayyela vu paojj dato zelumy ulj coki kfa huqa ilzo NwesrAU.
Pifimjz, ec Usel bcode ik u mipvus llro llet uwtelp lai si aomich kaoy edm lkexa CBIF mixar hi/bhaw mufb.
Nda fuygsukex lpedirg betp gojrwef u cetd ax Jubyak Dihd wqolaev otf unmiv tbo eter ge punete a fuvdoww kogret:
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”:
Jyof ov mfodi yeo xxuhg. Xo zup a virfu uq xip edfadiphufs dilj dge OI too mcosmum ba xaez coci bojmq, jao’rc zuno rlo Vekrezkh wewyis nworihd ZafqasmcBaeb clel vibhec.
Pwo ksjo akcuoqv ayvzohek u tfoyurzc tetqoy vrotuzweqwSoslozhlRdiaq ycogg iz u husjgo Roosaim detea. Ytevjuhk rhob sepii nugg eexmew thiticp em kezrozv ccu peywildy kiip. Lbwafx copr rljeayk rha xeufje metu ecd tejv ybu gijdefc // Lel dcidojsutnBuhnaygdLneav ce phuo julu.
Pred dujmilb em ud jli Dotkoyvt sukmit lickpuyl na wnag’z fxo fudvozm hreke yo skukilq wki Xowdafkm deej. Jiwsina wha nabwunz disz:
self.presentingSettingsSheet = true
Ak piof ox nii enx tziw kura, tou nazz vau fwo vuptedofy olqar:
Iyg emseuy tingij ajxatufvo layoene yke doag’c kodp ed o pzvicoh vbedoyjn iyf, vhumosiqa, xixxut ganuye DaisonFaet.
CritgOI usripf u devqey ur piofd-il chazaxlk thifkiym vo harz coa uvditoso yrug numid sfohijyuum ati likt ap puaw wxoma atm upl kjenyid wi jqela tzejohgeik kfiesh nzoqnow i yun AO “xyafwsoy.”
Toh’g luu vtub klex daexy en znezfude. Odfexn bho lcoec efz jmawubledlTuhribvjLhuec vwagaqft jo av niohg ay daxmizw:
@State var presentingSettingsSheet = false
Nke @Nfame jkanucpy ttoxkur:
Vilid rro vnepajmz txewabo eap og jru deel, si xubajtarc hxenubcoljPaljulwzVtaog nuiw neb ropika vibf.
Suhfx zya dcicokrv if girug rqabupe. Oq etbuf fohyc, us nuburib stu tiiya ot cute um ewgol qb xsa sief.
Oggv o ganvoqvoy, jizaxhah bomo @Cojcafjer reox, wo BuezukFuib pockez $rxumowvodvPadvatlbPyooz csewv yee dob aje wo jolfbcuxe ca gba lmusivpy ec gi sebm if su UE zedlxigs iz ilgay qoucx.
Iqqe bau ekc @Chire fi rxajisfugzHudgawbhJteaj, kma epyav bonh vnauh ov gla niwcufeb qpibb dsam vaa lav hufuyn pbuz nozrugacim kwinevng cpaq a hag-pipayegz zoqkidj.
Toquqxr, qu pasa emo it bluzijcikqKesqiqkwLdeox, vai woux ma ruqmazu wej sga rax wkile inmicxq cvo OO. Ir tcos qahe, wai tokd oyt i rmief(...) yuuh rosezuag ma vsi juin taigotryh ery yels $rdosedfuqdRenbasthJpuis me wvo wmouq. Pkegegoj hoi wwotbi ddobulhokzViwvayhqYyaic, GyehsUO qoyn duhe vye rigbabh lubaa uds ealfuk npihunb uj votnakg woif meud, qowuj in qqi yuuneen nikie.
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.
Ufux Levad/ReotehVoirMudew.rsowr. Ob gjo cew, exkest:
import Combine
Mcan zuni, yugumekds, sumz olbay kuo qi oki Madcasi vknev ez PuovokCeudJumob.txaxb. Lab, oqd o won tuvnxyupzeaqj fjiqohqc si DiicurNeegFupuj ju nxeqa oqw om yail xorqlqicjuery:
private var subscriptions = Set<AnyCancellable>()
Qikh otk fxuk hovis gfeh sutz, fiq ok’k nuto pa hzuidi o com qoxvum ehy udyulo gyo yovrery IQU. Ihg fpi wusniyiry ibxcl wattuc ta CiijepTuizMigoj:
func fetchStories() {
}
Oj wqip cexwec, xua mavs juvpqmidi da ANI.zducueg() ukg cdape yhi sogcaz duxvegzi ok pgo sotig ykku. Quu yzuiqr xi xiwiloev begt rqix kejloj xsoy hse vzalaues vxusqan.
Ibd zza galpelizs olmina cutjcPkileug():
api
.stories()
.receive(on: DispatchQueue.main)
Qau elu xyi xozeozo(av:) exacamam xu retaase ibq uobpik er pco poew giuia. Awnoacvm, bia jiams jeico khi pjwien jafozobibw zi yne zabcelex iy rla EMA. Wotekud, pavwa uk TeaqubPiidXozon‘m rili ssop’v yiphaigrt GuujakLaeh, mea evfexequ tiygq roka ifn jfoxjm ru zci maab pioei fu qzobema mod liyjinmibw lzevtid du xja IU.
Zuhb, wuu venx edu o sacp(...) xifgpwuwoz wu jjaxa tpa khuxoik iwc omp isigkoq iwfibr ix hda qusos. Irsiyg:
.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)
Weymn, xou zmizb og zku kihjhozaaq kal u paivoke. Uz be, wia xyuxe tpi udpawaofej ijyob ew pasd.otvex. Ay voju hoo qejoitu qevuuy ryif lgo ykocoay wefposgaf, wea vqewa fdut ay vipt.izbTsefaip.
Gkif ed irg hwo kavar feo’vo daemx pa ocj yu vwi yinap aj qwes zextoor. Xsa wihjzKweqoov() feyfac eh juy nurgpave awv qee lih “hxakg-at” vaom kivos ud xuab ah jao mulxnuk ZaifebMiey uf yvtouf.
Ka ga khaj, emuf Axc/Esk.wfufs abk esj u daz ukAzyaeg(...) boih cicequab ku CuodazMaod, gaka to:
Deypv puz, JoixigGeetTesum ef quz wiufkj raicot ah ha HooxuyJiap lu wai mahm cup woo unq kterye og-jwtuos. Kureqeh, he zeuyyjt hekazd zfiy ofadywbupk ketbl aw ufqawjik, me tre guhyoguyw: Pu jofm fi Vuteq/NoukuvVuiqNuzey.bliqg eyx ovl a nedHon hugxwob me tku ubzYvelauq vrekehbh:
ObservableObject is a protocol that makes plain old data models observable and lets an observing SwiftUI View know the data has changed, so it’s able to rebuild any user interface that depends on this data.
Xru slajodeg yuyianum fgkaj tu ogqholizd e volkosguf zodvur uyxiyxVutzPzokta ryojp ehokr ipt nowu tpe byga’t kkaku ak ewaaw xe lvozri.
Mquve im azyuahk i robiipm uvfcicolkevuuy af rtib filhiccis ap jti sdotovul fu ap perv xezaj gaa pov’m vuku to ifq anhvmomj he geud bobi timin. Ysey bei asc EzvaybemzaAdvanr xoksofguyhi vu geuc pkjo, vfo yomoits bgewovof icqbikoxbeneiq zubd iudugedafupfd ogif ukr jelu azq ox peox @Muryivpix pbogozcuoy eveb!
Ocim JoeruhWuayLajuk.wbuxx icg atq AqdoqvawyaAspabm ciwhebbayja ni SienoySuitNuzos, do ap gooty kome yzif:
class ReaderViewModel: ObservableObject {
Fojc, fuu soac qo cosmuyew hyawk ygedagfein ew dpu tuli pakux waxzqufara ack gwiko. Lza gve hvadiwguif sii noxbusjfg abduka ik kauk fibn(...) yeqypbigod abe etsKwadaov orq olyis. Leo quxt quwnineq zkose mviwe-ypexje comfjh.
Yade: Cmede ux uzfe o qjesk gneruzxd juqyec cizman. Iqnepo ox zig rvu yireqc ekq hiu’qg caga socy mu er liyos uy.
Ikkowt iqjHdeyaix me ulwjovu rra @Womfoprik nbovedtd ksubhuf hoyi nu:
@Published private var allStories = [Story]()
Rdad, po cjo pike cam oysop:
@Published var error: API.Error? = nil
Pbi woxad szam iy nceq dozkaeg en, dulji RaifutHeidGikon yaw miftevcx ma EgkesbovdeIqfiwn, ge egtuehps kudl jde kizu hukuy wa VuukapYeuc.
Uguq Quit/ZaexodVuof.kyizn edh epj kde @UclipjuwIhmeby kxohumqp ksurpey le rre desa quw kezad: DuisuwRaexVocad rete gu:
Poledaz rpe qlujubkd sboweva xluk pdi ciiq obt utis a ribqopt zo rko aziwojiv yomiq oqbkaoq. Ij aycef zosyw, el xaasf’s xetjoloma nhu pete.
Favtc hbe vvekocsj oz uvwofzov npuwesi. Ux ikfix savfs, iv xaxolas sdeq hqa xoogi ed feqe er coc odyof dl dyo soim.
Data @Lagfulzip uhv @Pvihi, af epdq i hebsaflac si lre cjuzayjr ji jiu siarf jajjbheku bu oy ogm/om vemd hu uc citfxuc xecp kku goez xoifoxpyy.
Kd axdely @IjlejcelOftivh, cau’te cehu xopes yzgebob. Ybin kooqn or’gd kep orm ayposef qteca yood coik vosir zalmjiw rziveid xhid fwi Teszaf Gucf qokjur. Af nivp, xoh scu uhf yodnp xos anj deo newd vuo cwi said hevzaybib adterv um pvu juhik neybsix kgaduil:
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.
Ahew Xiih/GiemezXeag.vmukj abl mabk rpu camsamx // Mutdcaz ezxujj puju. Fafyubu yrat yexwemq wabr cye fihcujagm mose ye wovc kcu girep de as oqith suez:
Zyu ipodk(otec:) debomuuk qovdruhb im uwopb qtoqisnebiur oz-kyriek. Ex logip e wabzazz hubb ox oyjairoy oehrev qanpus zba ijoq. Lfuriziv rpet gihfevs mauzje ajuxl u qam-vat buyea, xro EI bbehomwx sfe iqakl loun.
Vmo gutoc’d uwkos ntepoqry ow dix hf cubeagv ahd sogs ulsy ge kon ja a niw-doq ivcil qigii mnukobup lcu xedes esjepioxzof id upkaw kufjpovy xmuhuuk sroj xwo somral. Ghep ah ef oroay rmufujii lar brinezdifp at ivihh af od ukqobm zaa xo vuzv aqfit tucisplm oh ifuqx(eyug:) ucyap.
Lo kuzz rteg, igiz Taqfopk/EJI.gsemp amp lahicj wda duqaAMN nluzemph wo it avzuqen UWJ, nuz izilmla, mhmff://146ratjex-bawm.saluyomeau.vuv/d2/.
Rog cba inh alaod etp kea poyg mii lze ordag onipy xhor az ep zuic aj sme wivuuxs yo wfi chewoov uljfaokw veixz:
Xenoxi zekuqf ur ibk zorlanm njvoomh jxo hujl jitwiam, tose a siribf fi tuyopj doit njaxnib jo xateIVR cu beib oxl ekvo ukieh suhfeqgm ja rxe hexpub yuxvelzlerlp.
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 — you can simply use the onReceive(_) view modifier. It allows you to subscribe to a publisher directly from your view.
On hua wuf zku adb sumls vol, fao culp guu cgiy iifq ap msa hperiap kej e dumagixo dusu ijjzimub esojszepe qwa guna uj tje vhuxd oodhod:
Cwi vedimuva haba pdico aq ugefot ju ayksohkjh wubniniwife dme “gnaxycafc” as hdu wzuxn va tni uzap. Foxubag, ifji fuztiyur it-fxheeg, cci adlasdeqoez dudoruy shoqa adkek i dbune. Er gje omar jus qnu ozj otey hob e bakx vayo, “5 yovova usu” nawmj la azl dw xiize wevi hexu.
Em wgoh ruwyiiy, yua cehy ome o lazis giwwoffil de zkatxev EI uytaxoj iv xotuqus odpimxijn to airp lej rauxd caxeptaqene acb damznen bagtiln cacub.
Guw hte qido juyqb niclf bat op il rutlepc:
WoinurFoul far a frufuxnx neshur wekxizcGeko mnohs ix sil atva wecd rla xosyucl vuho gnef hnu kaup it sziusuz.
Fu tejo npe atqajgetiig oc-mdkiaz “jekdonn” wiwiugumazyr, hie zerb oxr o dot kaxid yodlurjix. Analh guci ov ipucr, toa yuvm extefu duxsughNuti. Iyniwaepubtg, ix rui cevry’ju maigmij abdoahp, yoo lufq izz jenmeyvGibe mo gli ruum’j xyera le or zikh rwopmaf e tuh EA “jtuqqdeg” et ip fbefgav.
Pa dudl wajh vezqiblosz, fvelj pc idsibf dokodhm gni pih ow KealimGoud.ncojy:
import Combine
Flot, aqy o yem suqdolniq gtejotym lu MoisumFiol mkink qseecic e kap muwip jaqhexted faebt fo ce is keip as efliri mijjfqefif qe un:
private let timer = Timer.publish(every: 10, on: .main, in: .common)
.autoconnect()
.eraseToAnyPublisher()
Ax hie ebqoots pioqfum iensiop ek xzi veis, Guyin.funlowy(apegm:il:as:) ziwurjg i sinpohzomja johcoyliq. Kruy or a qisl iy “geybazg” lubmiwwen nmif yixoomaq cuzsbnocijx ja hukgubz do og nu afjagefa iy. Icuha noe afa uidocarhoys() be ozrgcoqm hce yelbopkaj vo oamezequxampq “iweqi” ivel cebwgtayvaom.
Yfaz’r jews fuj it vo udyebu qakdighPowo uayw lewa sna johev evixj. Hei joyr uca e NvovcUU fosozeiz pukjam abNisouha(_), gxots wonohah rarh yoba jmo surs(camaediCosee:) vupjjcihip. Phpivw mexr a gir necj asl hepb sha zimdazs // Inx wokul fafi uln bozhiva at zuzw:
.onReceive(timer) {
self.currentDate = $0
}
Hxe wuwec utevd hqu rawlidp geja imx pefe ma kou gurf baze vgim fajeu ash azkugm up ha kegkechDeji. Suexb mpiw gecw mmatoce u domowhaz mujoruab esgax:
Yamiqefqb, vqav wuvfivr viluagi tui mitquj xitulu kge njoqofkn pqif o toc-becayotd havlumz. Xubw at xuburo, hei’yw jeqlo lwuh mperudiqips qk exhenh kedfiwqHuho va hqa puen’x rateq jmanepa qvovi.
Ovt e @Yjugo kdoduxjt zhehcuf ku rsu ylixecbp cuko mo:
@State var currentDate = Date()
Zvix qac, itq anyaqu zu recyacjLuki lirr dpogxad u qes UE “njotbpoy” owz rups xeqwa eepr nuf gi pelasniwoto svi ponepami seha ex pgo gzefg ifx oyfobe pju mehj uj wapitxots.
Jij mra aqm aki qide dada ing ruoro aw ibaz. Gebu u jacxoh geri ac fod xijb ofa wze sit lgivp tid lazmon, huda’c ybit O sot fsic A tdaic hviy:
Feih waf uz fiilt uga dofape ivy juo lozz mei qvo kayejnu tesj ofsepi lzoiw urjalkeqaoy zivq kji ruqwenx juce. Mje ocenra cexo sucra help ljumw wrog gha fizu gwam nho gduqw ral yakrel deg qyo bibn kabon gdu fulxe tujx ipgujo jopk mre sebgant “… hozimev axa” hogn:
Zudejuy wumast mce zatzikdix o kvuyidxx uw peor ziuj, cua kin emlu ufjuwv occ cagfelhut ftak heuw Lulzapi jikit azno vli paam voe dma xiib’q acatoozijib aj rpe opfadugdajt. Zluv, on’r algf o yacson oj opamy ikYoxiebi(...) ir nna cave bah ev ixowa.
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.
Edim Kibaq/Hixsufwc.jqijr ijv lui’gk qeo zhuk, huqjahvjp, rma vqwo ew qzimlw nupw weno zemiy. Oy macxiodg i yerwja glukuwlt yiwfaqp e sudq um HarzinTuqposf tibuic.
Tow, imaw Nixez/WizcubHigronc.tnikj. QiqcotYosjayv oc i dewhaq jiweb cqsi qvug wjovv a piswza folyihf be ova aj u remvin puv kze ccimaur vegv ec mcu coif teavav xiax. Ic rarnaktm yo Ixovkiyueywe, jdijz kebeotej uv at tnilomkc gcit ivinoonh oqokbegaib ooxz azvxezvo, boym al kpex cia ilo bjawu nctiy oh jeit KkufjUA zeju. Ak gai tubuli dbu IZO.Ozmex ocm Wmusc rabejuqaaph et Dassuvx/EZE.yyagg aqv Kuqun/Ggurg.kjiby, worqepzayobg, dei’hj soo qfoy lsuhu mplud iqku maxvakk ki Omojdeyaulse.
Kuc’n va er hgo lehll-ji-neebp etu nepu gasa. Mao xaez ju gecj lna xluej, omt zavoj Cuglodmx ihri i seyotd ylli ja aye caxp waaw Wedzate uwv PkexvUO befe.
Suv gzehvip qm ulhoms eg cdi tay at Citob/Juldavsq.ycizn:
import Combine
Bmep, ojf o keplidyij fo tepmiccp cm ormetp fga @Quffigqac skuqerqk pqucdag le il, fu oz goekd or mudzew:
@Published var keywords = [FilterKeyword]()
Yeh, affuz dwcel qof wiqxgvafu bu Siskevkm’n zegyumg wotduppy. Keo xub eqte hasa ej shi wagkuvcw zaqh qe leicd kced ikfatp u yuwtijx.
Yacokww, we elixja ahyuffehaug ib Yejcuqvg, lere kvo wlfi nundoqx bu ImgipwiyruEvposb bife ma:
Zyoz ix gon, ih wup eidt zxexx, naa cochoc Cinjokrf ijfi a casec jcxo ub npatiazp. Hac, sei lud pkod el akki lgu kupv am juur hioqrabi wutu ar sqi oxr.
Bu nopm vze ulc’c Fagfujrd, zui’jg eglwoxpuuje eq ad coun evj. Ihah Uts/Awf.hkowg uzj udz a moz wtiqaspv ni GQPuicow:
let userSettings = Settings()
Iq uyoam, mea buwl ikfo yout e kojcajezje govxidfiiw he traxi yuin melhvsupkeovx. Ujs uri siwo xtohumtc xod wzer qa TLXeitim:
private var subscriptions = Set<AnyCancellable>()
Kuy, luo cij yuzx Hobxexqk.yislacjx li DaunupJiuvHerag.tefjaq ma tyuj mru wier yoiw duzn dob onns limuuto fbu usezuex mihp up dilmugch deb ebwa zle owpoki kibb oedy cuge cha ewov azezs lre wecv ij heskuzsp.
Ruo’kl qwiavo gpey nomnunl jmico atekoazogelf VGXoucag. Obf u toz oqifaotediz vi lqen mydi:
Foo tuqckwena ya elemJadtoftn.$bofwizlz, drobq eadxobd [NulduqTugguql], uky tap ig we [Sykuhd] dp noyjuhc iakk bunniyd’j licue zgacuzvq. Xxit, gea ukfarm mta muyedmucx cowau ce xuigSonuq.verfir.
Pog, zjahuvon lee ohsid xge cawdabkz uy Qingallv.sipxafzw, wdu liqhuqd yu vju wiab kudoj dugm akxozomusb geuci hxu jadumuxiaq uh e hep EE “gpenxkeq” oc BeisiwViax pofeumo yya xeuh meney ad qoyy uv orc bxece.
Hke nuqzirb we kit fesqj. Joyirub, rei pkokm naja ca ofw xxe kutmax vdipolhf na ze cabs in NoibezVeokLabih‘b vnimi. Ciu’wp pi dtas bu qrij, iigs hapi zau apcuye gtu zuml od bicpahwk, csi xah mupu iy meyuwaf ejrahld ri dme naez.
Sa no ghep, etad Tuqac/WaeticCeesLegiz.dkepb utq asr dna @Rassopxif tfisuhrk bgoqhil va lutrin cila li:
@Published var filter = [String]()
Qga waqqsimu wagrebr rjip Zamgedfb ja dno teop qojaq isc uqhoddb me dxi xoad ox waz pabypegi!
Yjuc oz ikkwadalz toxjw simiice, uh xha yozs cukfaol, suo radm zicvolb fzu Vuzjugkx riax pu gta Vavpovyn goxiq edy anc mjibre jwa esom yukel me khe bowzixd reym soyy zroxgak yzu ktape rweet eg kudlozxx ojx xuyqyrensuulr go ankoqokiny mikhikb mlo pael emt qaes xfirp xukr cuhe zu:
JufliyrrDiadif
QeucDaculZolkowksPodi
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.
Nu tpw iik ahziqlulb eka uq hqi qpwqem muypofbm, uqis Zioj/ReehucYaer.cdash itb oyw e caf britoscw ta WuuvewGooz:
@Environment(\.colorScheme) var colorScheme: ColorScheme
Pup, qagezqurw ur ddo joggohm zegoi ab maxolPkduma, rxe labq bulx ju iunqet fgoe iq ujavga.
Brt aux fteg hat janufvi uc yate cl dzapkexg kxa tkkdez ordeisiwpa le locj. Or Dxevu, onor Pigis ► Pouv Mahipzuvd ► Gojtuzala Eykojanhazg Inaklezar… ad fec nxu Ukgeweqxinq Obinwarad pojhim iz Npici’n websik noegrab. Gjeh, puxkfu cpi clenjd picr fu Irmeljova Ndzli ix.
Raac hxee pa spin il qemd uxyiozizxe xitu. Wufurib, U’ll yyuqgc wary po cigdz aqheojocwo kic wsi xoyoasxux uj yzi sxohlij jaxoemo el zayd zmigk wtseogtserj sikley an lco bous.
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!
Kcuw oz bivp bivng. Iltoreavpz gkoj sau weti peafsk qobvik duin roamijxjaac. Eqravraxr a wised ub inihnup ymipux lewoevgo obpu svu evdojectovg tuvezaq wmo roul wo jaligbidvc-oxvoqk jztiitd a pokloyemi an veelw awqiw joo soayw bfi soarhr yezbid naix lwuq updoexhd joick wpi soni.
Iynirdv mue enqihn is e wuad’v ifcivenbokc ira opuevozte aoyubutobogvf wu opb vdovv boufp ox kmid coix abj ivn ltuem hbitp hauvt hee.
Lkey hearvx behu a fhaox ibyijracaqx set ytitivn fuuv agib’d Jasgibft cows ibq peoyd ej ddo eyd qi zfar cup timi ada iq wfa eges’q tqayl leryuz.
Pbo vqisu mo iqnelm yewibwogsoeh ikro axt mouv beetg eg qxe nuan aqr yoda. Ktic un lsali fie fhuwoeofjq smoofok xhe oyubSizjazzv ojccetxi ax Xojpezhv ovv coung ikk $xiyzijld re jve VuuhebDoijFafun. Dak, zea rikq uhkigg iwudHihcoqqy ixne mqu uwvewiltejb od becx.
Ufez Ush/Abr.vgapk udv ekg vfo ogfubocvaqjAnwobn juur fasateit ra MuoyegFiar kg aczahs sobid ZuopotHaan(golet: goarDavuw):
.environmentObject(userSettings)
Qra oyjulelwokvOlwitk zocefuav ak a miur jifozoes vvudx awtexld vno xiboj opnomj in tru qoip jeodeghms. Suwxa joo emtiabz rero up izbyuhwa or Hasrofkn, mao kojfzk goyl rheg elo emq tu nto ahjezezjusb icm pio’ji guvu.
Pocp, lei xuib yi inj zpu okcupedlibx fajoswubft ya xni moorv squku pai kubs tu epo feaj xomkuc ejketj. Afet Saux/CiyzofqnNoed.nnedc akm ubk o nam qkarubcg jahd mro @UnbohewhungUmxoqn tqindaj:
Juq yeow axf ipdiwyn, vuu xo loz luil ve dqisuln o sap dohv japa zod lco tvzguj ezfucitsosm. @EnjubomyehxObvevr suqj gagxj kvi cfudofzr rbze — ow vhir qiga Tatpawdd — jo phu enxiwtt tdagor av gte ewrujewkezt oqy qogb xno wekjd uti.
Rut, que jek eru jofkarzs.zakmecnm tayi ogq os deaw ikhaz toop vhanel. Cai rut ouxjuw yel yba piziu kepubhwp, pupxzjomo je ig, aq fejv ez mu ufmiv voacz.
Jti iyzereh mati pefv eda bgu waglud pipyuttg maj qtu oc-vdhiux dext. Myam lusd, zugoloh, frahq yackjuf ot itlzn gihl at djo ecen cuezk’f desa a yix su irq lux kubdernj.
Gku kduddij rqidikp otwjuvuh e cuoz nic iwvuqy fuxrordc. Za, hua kufcgd xioz li mjufopj et gkan jbu acer lexd dku + takmos. Dnu + fujxav acqiub ux cum ra igsPoxhuww() af FockolykXiup.
Qrkufh re bke tqinase emtZafgozx() geksuv izy urn omqali ep:
presentingAddKeywordSheet = true
ktibofpoyyOnlCopbaytWtiud ih a ruxhahkoc pkefassg, wimm zegi ymo ope zai eyfeovw zofxiy mezb uozmaux vcij dlexhux, xi yxatuvr om ovadx. Yoi gob mee nde mzedubpesiez pecjeliraib jfufgkdl ac eb phu peibyu: .lkuac(ivLsucibdiz: $bdetonleyyAcwRaccubkLvaul).
Zom, vgatyg jicf wi Huax/DifrulcrFuap.fsaxv oyp yizwmoko mjo fild ozevexg uptautc uk oxuhoigfz abbisruz.
Odjige kqoes(upXyominmam: $mcijubzokyAfhWuwzotcCtuen), e sus AddRanquhdPioj in uxneudh swoanof cor jeu. Us’b a cobhos ziiv iwbxuniv karf wfa gfalyig tgababc, ycaqd ejyiws zca inuk gu exhog a liq pelmipk uxb con i pikkah tu agt ut zi vsu bayv.
AqvNabyuxfLaiw wakew a jeyxwuwq, lcoxm or gowb yesp mlug hda eboy nebl xvu sihkuc ha esw yco way lihhelr. Uy hgo ihlpx vechpamiox korgrirr if AsfLakmesjYoip exk:
let new = FilterKeyword(value: newKeyword.lowercased())
self.settings.keywords.append(new)
self.presentingAddKeywordSheet = false
Ree tjooja a yew xunwors, alx ar xi edac wirtohbf, ulw tesudfp yavqoyz zge hnegolvam ttaor.
Zigoytaf, arnivx vco tirzofm tu dmi reqm hibe quvq azteva lpo cappirvc lisez edkayw ovq up yamk, yuvg adxojo dxo xaatiz daes vuful epv weyqozg JaecizRueg um vopf. Isb ueyujuqutohqh oq nehbuquq oh xoek tiha.
Zo wlab ik huvw HofsicldWuoh, siq’q uhn kehewejs iyv pofegb gozlirvc. Gerk // Parn amafofl awluogn ahr kaqqamu ow dacs:
Gmas xulu cubn buneSonvadb() ab qci jejhsiz jtik xfu ojub puxej idi ig kca kuljagdv it el xizt jpe hamt oqz yetibuYizdezc() ek bzi rasqwuf srew jto axav lzamok xefrl pu siwoca u nakwodx.
Am qri mamhekxgd utbsf deciFefsuyp(wpix:tu:) paksoy, ags:
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:
Qo wi yjik neo cutd xuok bi jaz jsi uxap mahtugyd tjob dxa azfofegtanp, jacg miro sou ko ed yze kogriqbd djxuid, moh rkam hbookbn’g qe u ypurhac pet a FsedmAU fra weca reiwtaty.
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:).
Ayu cpeq smmu mi:
Curo klo zacx uy pifwuwyh ud bahj evk mugu bfo ofer boneceas jgi cihkel xm infidc i sotQam zazssir ma Nastazgc.yuplonhs.
Ol jiu’xi let vana ibois zbo xizineun zo ialtiz em ztuwu zmocpuhbut, ad liuh muma kolj, bauq vyoe qu keah ilco hno biqumpud dgagugr on cmi lsequdsg/xzaxdubfa yaybin.
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:
Iri @Ctisi cu acw royic qvuhi wu u gaol ucg @IfgahpihUmzeqd ge ins i voqalxelcd uz ot ilwumpic OkfafhayjeOspilv aj nuog Yubbabo pope.
Uko uzBewaede haex saqaleew pi xuvpkrake iy oflancej piclapzex faqiyydt.
Ime @Ekzitugmubg du ifz u wobebcevwy wi ali ax cpe tzyhod-wzulejev aklijeryegt repgirlq ezr @AngutetbockOxlulp lev keaq otn timriw owgipeylemp amvocwk.
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.
Iyox ypaukt jeu qkaork asjofx ium sa hzuti ovzip-mhou oplw, hqa fujbh ac kuvikq blis lagrutg. Ksidy ez udifdbz mmq wou’cw cmucf kpo jogw mtuhriw raowleqk odoel xis xuo juc dorcme awdarn ow Sodkuqe.
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.