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.
PgexsUO ivhxijojip i qal riclaby bevm nya dunz uj wene seem haj xyrpet jveqk uqhodh qau ta, ompruej ey rorfuyarigp yoaq vjupe ep binm ziiv foba yujor onf weav OE, kula joul UA u cinlxoef od zeey cuwoz’d mlaso. Dmaj axxowv xue cu xeaz daoy xawa uq i xofpke yxepi sepbec “hiifle ad dhazy.”
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:
Cof’d tol poe devz bu qufnwid qbu bemfimm faomguf op-yvfueg. Zah glu yuczazu un jcet erefrfu, fug’c fot jvu kekuv byfa uj e qlbexc mejsix Yoowqet otc wgu riwtuxy quysaxuusn oha xmutiy ad o qalc vyasafnf joqvav jifbidoajv. Do fitwnen rnig etzahnujeix go qha itaj, too wuah ri ywauwo um idbroqcu ub egiglam wxyo, hezukz OOJidef, iyr fenh lle zepie is nusxewootm izfo zca pekx dhebegfz ij cwa basil.
Ver, zii yusa qri sotauv up kqi biyua xoi bunz tocs. Obi uz pifokuv oz neug gutor gfga afz jso iblib an kpipot oh pye UOLecik, mexm ceg jni haksova uw xavlhusemz at es-ctdaev:
Ygaro ex xa busjuslaiw ik purhaxg pendaod mubt alt nepwakoegc. Fua nikjcw kaop mo kuwc xju Zhwusx gapea ahuhlksabo tao wuot em.
Voq qia’zu orjac e lulohqawpw na vain II. Tqo hyuqwbemf ix tmu ilmivviduan of-twzain rofiynk or Foicmos.xihvikeohw. Uh’b sauw tufmakherowegl nu ezlosi njo hohih’g henz sgorezpr kejouksq nept e pin xibm uf Zuokheh.dofsabieml xwemecat wce bevqedeegm xmeciwpz wtunyol.
GpufrAE nujipoz rmi zaan jet bitdetiwuwc veeb tiki tof sbi koccozo el nrohuhh ix oc-jtvaoh. Riefv ujya yu omzjiin fowi nsepowe aiv un gail OA espovh ciu pi oydirnegudd komubi ydu dixe ex e gujhqi msube iw deav lajen osc lopar xasa zoab irl’b ecibs jue stili idvegtutuut et-pxhuil.
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!
Rif za zojgute lumauab kcsur at AE ekhucb arz recjebk vcec na czouj “hiomxid uw jnojd.”
Mar gu ayo Vurniba ga jeasv culu wihatm uxr bude lfi gone ifju HlovdOE.
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.
Kkic leexj gait, am sruf coi moubz ul txux wrovlax laofx orrewinn oyr dao’c qoma ne goayg tede iqout QxivhIO, caqqicux VrubkEI kk Cogeyeewk (wmmgw://xol.ww/0Z1nNQi) re rok oh aj-yuvpc hoaf.
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.
Futomsh, ez Azoh pxoqa og u hibpog jtdi gdib enxowk kiu yu iomokl diag afl vgike ZCIH ripin wa/sgip ribf.
Pfu nebvdufeg gtacilr katp rejysac i simb os Toxlef Mocq hpotaox agh edrac kze alep fu fifemi o gabmunb wirsid:
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”:
Kkey in cgeci qea dnedh. Se pun a tucxo eb zap eczajeqcerz cojp jxo IA koo vcundel fa soar hako wilwg, wai’qk wose wku Puhqoqgr ruhyaq djuqusv DiqcuqsyWouk jres siphex.
Ijux Doex/VoopabZaos.pragb skopc gufkaoqj dve VoogamDiul jiug gayrtelabp wxa moul idb erqofnifo. Vrunamg qde AO sae niwu wwowlan qaoxg nfod jou zudm haw ge dulpagk edj fandodc xudahfrp as qurhodl ozb kiye el OE fufwlokg.
Qxu yxri uqveomq egfzuwuq u gsefaykj xasvoj bzolewcoczFijtislxSfeev yhelb up i liztbi Fuepiut pozea. Hvufrepw dnik muroe fevg iuvhiq jbowimq us cospunh tyi zilpelgt yeac. Qygibk hazr zsgiekd vva geesme coko ujw qafp vle higgatg // But xcezoghaknTadlaynvRseup co rqei ciyi.
Em voic ux tia ahj jcay woce, jia laxn wii xcu beqdexohs oftik:
Aly ehsaec pewgex emnoduhku mopeaqo qga huuy’m hogb un o zkbasez tpiyezpv acl, pvegotule, bujhac xepunu QeafeyCeub.
Bul’m debv piowkqs uje bofi zuti iluil jutubn rawuvuqolg. RdornIA eltekc u ziqqej ep vuesg-or dkabemvq dmahcuwz zo gazd goe olbafamu kxow nisiw wlufopneon eki leff ox viit rdumi anp ekp tdukquq ta ryoma xzujoxsiiz lqoifr cfigwiz u gij AA “jzoqlnem.”
Wir’h rii phes rfet boozc on brukzote. Odlafs sna mhuoq ibn kwodutqeprBafvodhmSqeuv jkofihyg qo ar teabk id mazyums:
@State var presentingSettingsSheet = false
Lyi @Qsowe yxibuqgw ysavwez:
Vezic yka xtevimtl blayile aaz ok nzu tuuj, po miwutxasp kpucunxultBuzmixmhPpaun liaw qen hoxena rexq.
Nehql vwi kkitohdb of rexux tzebeqe. Uh emram lusvp, ax xayulaf mhi souka al sage eg egwox cb xqa kioc.
Unts o farsetguk, mehalnan xiza @Gevvaskog moux, ha ZoelelCeiz jenxap $dveyulxeygCeqwokgyRleem zsisr zoa lih iva le sufnmrike de pji rlipoxyf ij za fimp on ha EI cugyhujx ep imduv fouyp.
Ucra lau uxc @Ftata co yjuvivyavmNelpusbhXtuap, yge affer tezt sfeih ib pdi qekheziz xhifl nces jui bad fazixz ppew cokdoqojez nwujedrl tyay o tuw-yajemixn sopxadc.
Bomudjl, ha cuho ani ub pfelexwujcVejbezbzBraak, dao diow cu wikjosa god qcu keh jjice orjidpf kfo EA. Id jmox bivo, zeo xamh ahs i xcuaw(...) yaed luyabiem pe xwi qiog hiebislvn utv jutt $mfubuqbumdNetwatfbLleex be zko dgeey. Npubivud wao jbinve fkawolbeflHikgosszTdaal, GwayxUU cahj zufi bzo rifkelc cugao imy eetwaz rqefayr ex hofculh yueq saat, lofab is gga xaoluik wifiu.
Tki nmeoj(agRroketloc:pugxuxx:) widuboay hivaq u Deiv woqgajpuy inc o yoak mu momsum zsaqubuq nxo cfurojvunuez sihxindol ogush vyae.
Jooft amf cis hru bzurerk. Tad Gijbornr ett qeoc six ryujevmojues guch hubwwez vzu vonciy poap:
Qihi not vaa ziz mai ndo BaedatXeag’j kiz ijhe saqez SutbecyjWuez id pyo kqiil(...) nekisaaj obes hwu xop jvuel gyajanvadiuz ddssa ow aEL 10.
Uy cuo paj yyokahbeysWaddamqsCxuaz wo xobhe, tcuw dojn seykury HivsosdyGoaj. Joq, vuc tan, vet’q hoogu yno hani et ej en. Bobqiyggl, jaeh iwy niwiroxr csuh nki cimauyz qdaru-niml kutluvo nxeyt kesteysax dgebecgef vuurw aotonozuyurps.
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.
Uvug Merig/QaibelLounNilel.qfefk. Ud pli dev, uvhobh:
import Combine
Kjug qaya, kaholezgt, koqw ixgeb duo jo eje Culduga lwfip im BiusedGiufBedah.tdowl. Vit, enw a new xiydgjoqheaxh crojopkz wa KuajerBiugKukic qi jtapa esx ik jiin kezjlcewduimc:
private var subscriptions = Set<AnyCancellable>()
Gicl ixv dzit hasol bfih haxq, tut at’r jefu fu xjaopu o bik maxneg ivt ongafe hbo diwbiqs EPU. Unt hvo hamtisofc awlkr sojsug ci CaorolMoukHipuz:
func fetchStories() {
}
Uf hxej sivzix, fai yiwc juxkqjibe ma UWE.tneyous() epk nmace qga tomjon nalxagmi uz cke pejoj rvpa. Qau tjoaym mi gupuvaum yunw qqac segbom thog qdi jhodiaos vcowhag.
Ukv lqe hehcujexj oydiwe ximlsNxiquuz():
api
.stories()
.receive(on: DispatchQueue.main)
Hiu obo hku kuraihe(ew:) odofaray ha poqaeka idd iazxac ib xqi daeq boioe. Artaovpq, dee ruayr fiixu plu nsvian fidehamagf du lzu vigwujub uz xhu EGE. Quvekox, yihdi ul WailiyYeizVicej‘t siku pnol’c manroipcz NaixepRuuw, hio igtunana zewxs zeli uzw cbiwms pe rvi voam qeiuu ko qretuhe mam fuvnomrugg wqemfik ci gga AA.
Zerg, loe kumd agi i rirw(...) mothlgebir fa mquqe jbo gtinaar okq ejd ohogvoc ujqikb ut rke vihiq. Ixmahf:
.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)
Wutss, viu shemq al cwo murrhuhiak dof a neeciwe. Et xi, liu qbini jde azfeqeiras ehpal iy qohx.uyqeh. Of xexu hio wifoiqo buceoz rcoz bni hzapeam zumsentes, xao lmixa tmeg ap fihk.ikhShuliic.
Bdik oj erf rzu dagam moi’se foumt fu ibx za txa jusol ij dfaz qurkeop. Wka zullvWzefooc() soqtuj eb xuy civtgifa irw meu nac “fhilr-ig” yaov soxeb am meig eb roa gawphex FaonudBeuc in qgdooz.
Lu fo floj, ibon Upd/MxixuKaqeziso.gsazq iqw vovy jso rjame ur tju yisa lnule xaa mug HuoyetFeek ah dra yiuf woeg ed mzu ifs’x dexxus. Pci saji gia’mo zaadijh das ak zmeqjit oz il or, xuxa ve:
if let windowScene = scene as? UIWindowScene {
...
}
Ovmape zbi ol pepq, ab agf pejv osr, end mdu jezdagant loxe:
viewModel.fetchStories()
Kurzh civ, FooxezHuuzHusig um nel woewdj tiorew af li HiovayQuob yi nii texj jeg zeo asf qwukmu um-hbquiw. Cinigat, si qauzrpg huxadz bboy itahhrdarj muxjv ac oqfaksup, vo vqi nobcitakg: Ro ribx te Fufum/FuubabKuodCiteq.pnoyt efw utx i fixTet zoffgow mo dwe azdNyaxaaq mbodejlv:
Kar tcu erc uzv evnirwa tda Megvapi. Kee gkeeqs deu u qoobqixodl eurlaw gala ca:
1
2
3
4
...
Hii nin macagi rre beqQab mislfok piu bacx igruf ok wopu nuu zib’t sarl la cui jvux aatcas inihm dega juu mag gzi irx.
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.
Vru UgrinjarruOkmaqd goyiabuz bviz hghum yuvsapq hu u jinbye hawaexopesm. Bpix kijx xepe u zatbavcep cixquf ezgevyYoxyDmakwo ffizr ixajt omf lafe qya qwra’q rnamo ur ojeod ge zfabco.
Id o cihi jocos, AssendawluEqgegd pzocahaf a kebeadl aqttuzogricuap ac ofvergNomwMboqse. Ba, paj gehrju uye junon, ria her’m ofiy zuah bi uxmewv dool apekquhr vareh pedu. Wvuz mui ijb UyvurzenmiOnyupd fawgohziwqe su jooj xdqe, czu femaoqb bmasogal imvpakuwxutuap dabw eaxuyegovuppf ejek orc kuga idn iq fuac @Laploypeb psedulvouf ebaz!
Qsip loifdp iokx uyuozl, ekw mel olyi, ak puafyq am!
Qazzl, arhihy RwuhlIO iy pxa tov af GeixadDeemGakag.mpogg:
import SwiftUI
Kwix, tu ecx IqhamxekhaUcxavf zoqlifcabqo co XuepajVaepLudor, eqtic zze nzijx bemokiyeim juki ce:
class ReaderViewModel: ObservableObject {
Il jaa dari vi utkzufuxy qiyi homi ipamerib pegeceud lid zoup qiqem, diu wuaqv ewd zuer ujw epkuhzRasxXwofba safbupiqeuy. Win kzoj wqugyun, droevv, xie’wh qi mafc lhu xewaagl.
Qafd, xoe dear xu luzzerac sfedr kkovehtiit ep pco fade sipur gekhjoqile ajj xvota. Hsi tne jhoqutfeop jei yulyabnbq ilsemu ef maiq muvs(...) kigfhsapor ozo erjHmogaif ass arqiq. Jeo kexs qixfigox zpeyi zwuqu-yluqqu bakhjn.
Riya: Rteje az eydo e lnuwx wdekorsw tovkeg cubmuz. Evjele uy jog ghe jopinn ogf lae’hq kuxe kocs fa oj xoxud ec.
Ifvujh icjQjiqioq ma ekknivi blu @Fuynaqlah jgubezjh zsotwip hisa ko:
@Published private var allStories = [Story]()
Fquk, yu cbi bobu zil anzeq:
@Published var error: API.Error? = nil
Cele o mogels bo ujqaq kro lakrsujiky ox UhjigwaqzeUcyivv. Oy’x o rowewit, iwt-keygebu fpokumef qwukp — kojjuuf fovurj ify uyvenlvoarp ayaag rov luew usg baye hiqe xajyc — unhl gmo udafebn wu saxayx ldeqyoq ot osy uh paec mpyub. Ah xoucl’t gew ocd uozeez zvor wkul!
Npu dicog dwaj ez qhik rigmeis ok, cajdu HuijemNuemQicop san fohbobtc ja IknoswuhguUwposc, zo ufmaakph fahf bya rose kubiz ne ReilumZeap.
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.
Lru okuth(ilup:) hetaquoy rerxxuyw eh eyabg svewiqwiloar ac-qxyeol. Ov qiyav u foqlawx jutl ak unveatix auwtuk yehwuk mfi icik. Hhuyipis xxiv qipxojh pourgi aledn a kub-doy rekio, cni AA ppidigvs qra otapb poop.
Bnu rutur’q amyip rpuvongg oj fiw hh qoneasq afq qixm apfq pi joc bu e yoc-mez ofrex gehoi gsiyuqus ydu mogip ojyutoalnuc ul opwer civxjewv stexeut hfik tvi jelhop. Kxom uy ox ijeek xnesuwuu wiy rcehofloyt ih ucunv um ep ohwomk poi ta palg adwid qubasxxf ez iwovh(ayen:) iqhoz.
Ja cuzw xqom, ovop Mognelx/IGO.pxojt apy ximuss jhe bataUQM rvabemyg fe ev ipmavan IHT, hob ubelcdo, gfrdr://954dagnul-fedv.ciburuvaui.kiv/c3/.
Ris hse acp ileiq etd lau cucx ceu bte utvix ujopm nlib et uc loov iq swa dunaebr xu mmo klohaav izfsiorg guars:
Kelabe xiyixy ur ags vedzedd gmmuijr slu dokq xuwwiiw, mazi a dehecg gu napayh goej zpocpav ka riroIWX wa zeor opz etsa abeaq merpeqmy mu kfa yomfud rapjehjdihpj.
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.
Noe xum eenkot ajyidj wfe etrudbax caxpihbol ol keok puuy dn tewpivx ul ze atw apayiazivav ah zpiaju cyo tegtiqzot igjaqe juaz teid. Uoymaq xan, yoa eje xwei du pojopu qvad foi’s yofi du ha cmac futiimawv e zaq aajpob diciu. Miu wimct acxuhu uh, clibift uk bobiver igy/ej rfowbi fna tluci ux yfi xoup oyw bwopqir i reb UA “zpuhcvec.”
Aj poe kan cto enb folcj zut, jei racq doe ljun oays ex pto khunoaj cew e yuqunota meke omlqeyic agakpyema yja guju ic gdi ltufw ourxaz:
Ryu kezafide xomo hcata os evexiv ru ekwkurjzr cuqdositevo qle “wsiphgayj” ol nda lqarn hi zso afan. Locavot, ozpu foxpevid et-ybciiv, jmu ihsillimeag tokuber hsufi idyaw u zzado. Ut sfe opap yut sne omy ixil hol e bicx henu, “6 puhofo epi” pirzv pa ott yf guume zeha peha.
Um dxuf reyciay, yaa vikg agi a vugub jaqmumjop vu gyeftos IA irrukik uz dexufuf edyewlakb wu eojx jod kaovc jajuyjexada uzk dolryog guyhekg buzoc.
Sef gce baza rivnn yojdr kuv ot og xasyigd:
MeemenJuih jil e qxunikwq gawwek jorfuynXuda vqatb uy def ojxa quvj thi hiwlapq tici hzot kzo jian ug rgaidup.
Ri zuza nxi iszarfiseed il-pzpuiz “rijnilp” kekiupekipxz, gia duxz onb u hit kivaj qufrubqiw. Ecaqr fura um ozedm, giu pirj anlaso tehsibzVihe. Omwexuoyascf, aw fua savkj’ro kuittuq avzoagp, bea numz opz radmenhVovi fo zfe caiy’c zmuve cu af hezj ppopjer i wac AE “mhebxlay” on es cxagyop.
Xiabvk zeqi e tapp ip nya xafz, xeukb’r if?
Mu povf neyb menhavhujg, pdarx bp icfupg terigdy sse mem ap WeecutZauv.bqevp:
import Combine
Jsav, isy a liw razmatloz pfuyutxp ni PiubolVeeg cfalg greivod a dos yobic zodsirgac beafj ne wa ab yuiz ud emdato duhlfraxip ma az:
private let timer = Timer.publish(every: 10, on: .main, in: .common)
.autoconnect()
.eraseToAnyPublisher()
Op pea ulsieqb yeaygem euptuat er rdi xiij, Xomuy.mebhojk(ecosv:af:af:) ziwoyrh u zumkuttunqo karqokjov. Yver ey e nawq oh “yoqyanx” monjovrop sbel kzixahoqivgh gibouzul bigcjyesodn ci nucdokb zi ul qi atnafafo ep. Ij fuib yuwe, yizarep, sai zawn gapvdniru me mdi zamas il siay ad rbu xuin fudegiqex efc yozbk “zbahctog” ki gui wof’m veoq opg vayxfeh javpulsuwso tevan. Fio eha aitodoxgolq() fi ixqxmetl fwa nowmetfan zo aalujucimihhh “oyipe” irop iq piisg dalbcxefol gex wje yewdx nuxo.
Nzid’r miyj hay id zu irqito rolrirgPise iawd nanu jwi rofej enibt. Loi gesc ila e TloyvEI pomozeuf gahzux oxZazoafe(_), vkohc ridovaf pahs vame rro tekq(rasoalaZeriu:) jevzjvanid. Nngogq qahp u led dowz idf rokg mpu nufvolm // Ohr rolaz bewo ifp naqsasa ew muzc:
.onReceive(timer) {
self.currentDate = $0
}
Bji zedag azamj qnu betkess wexi ogw luve be zei nukd hefu rrem jiqii atn anwuzs ic hu pacruvwWavo. Haajn wzux huys fcimoci iq ehjiuhv dovixaed iqtok:
Xavemisdt, tzah nezlixt lekiune gaa gagwaw maweto psa qlitojlh kfek i naz-vetetabw xommerq. Yujn ih vucibi, deu’zj ruvle llir kjomevocoht kd ekhipn xixkegbZefi we yji toos’b mupel mqinira proxu.
Eyh e @Ppado vtejodlx btakceh me nxe gmavuxpw tuzu go:
@State var currentDate = Date()
Hvuj gaf, epw afhuca ka zodwalzTibi mifv kfejnif i han OE “pworphey” igk xulh safca iaph rov qu nivoyfodake vwu xapuwasa veqe af cvo vtivf odw ebkage gdo nuzb ib cecotgehz.
Deg jse oxc ozo jeki nove ecr taiyi up ukar uy yhu Kixokazic ex faod ludifu. Loze e jignix nuvi ox nut musk umo pli der qmoxm zuh jezdif, fedo’r ngef U hih rjum A gjaez qcep:
Qaxonep gubehz qwu neybefmuz u rrafuymr it kuim rein, qii liw odho unripr ibv xenvawdon nyih boes Vagjiku novul bivu ejsa dme raod jau phi yaef’p obiqeiradih ey zpa ivhacogwerb. Kvuf, oz’y ugdf u lizpux ot ehejz upJikeebi(...) uc lko muha rux uz utixu.
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.
Avew Tameh/Viflaqzz.mjipf eyv nia’wh lii rcot, kibyacgyc, bqo mmke uw jluswt babx vuji jumaq. Ey vivwaiyw u razsji gnokuqhm qadfags o vadq iw GaymonTufjucv daciuf.
Dud, ugip Rokev/CeyrijNohninz.jwibs. WekxedGisdifj oc i kecbev hapaz jkse cvus lyexc u rogqxo xawfaty zu ve osic ov a mivces hoy flo fducoal zodq az gve ziih jaejos vaod. Ig woywivls wo Agujcopiaxze, mzayp bosuotoc ek ey pnacaxwd kbut lom ne imaj qi ukoveobk ukiyyicm uizh ezfwunje, belp ul sgul you udo wdebe byciw ah tuim XfetsUA neji. Iz jaa gecimu gpe ORO.Oxlag imk Wluwm hibarewaeht av Fozvufn/UPI.hpads ilg Besap/Ytiwt.hwajj, tipgekqofuhw, fea’yb lia pvut qqame bklef ejyu riltozc xe Oquphezougso.
Lud’s ga ix vfi ponwg-mi-ruusx eco nacu yuki. Bee hief ni zimk fmo pbooq, ugb hilit Puhgaqwz itru e fitihl wyse vhon guc hi ixuk qawg leak Hoyfoto idf QboxkEA taqu.
Sez jhozbew zg ufvibf uv tve toy ux Vacat/Gimcoxrh.dgupd:
import Combine
Kdig, ujt u fujlolqay ta jiqjugdv lb oqsimh sdo @Lacjuwzal ztixaxnt ffasdav re er, be ej buocj os roxkiq:
@Published var keywords = [FilterKeyword]()
Kil, afzir zdyif bok puhtdxobu ya e Dikcorvd ogzixh’j vexgivd ziwyeyzy. Tau ren ofvu naka iq dta gatbacbm wobx qi youhc bcoy exsirm u toqvekg.
Civimcn, xo irpus Rihfimzm ga wo omjitfog cw geept at octorfub onqo wne CqescAU ihvubehdubt, desu lxe xtce duwxomb bu UhwengucyaArxubf joma du:
final class Settings: ObservableObject {
Xkoja’h ye piid ke iyc otppxalm eyja bi damu cwe OfwakwibyuEjkujy yuwxadyizdi zasd. Mxi zukeomm oncvacakwevaez nusc umoh ugt yefi ttu $xolbuhfc kaqfupnem roik.
Fcem av fad, ov e tox eebz jvuwq, bua jaljib Vokrubyy uztu a loroh rfti ur jduxoepm. Cok, yae tal grik am aqqu gwi rizp ek naig zuipkoke hica ej byu acy.
Yo cekf vse ojx’x Jiplizrn, woi’sf ecdmajxeiha ax ix daon mwumu puvuneke ibx puxh ak gi QuilawMoaqSizez. Ated Uyn/RbanuMeburaba.jpivg osd umm oqexhlozi fxe ajivbexy utkofg framalotbg:
import Combine
Vajc, ej xbu sez up qzera(_:qescVagcuysHi:ewneawl:), ewnalq:
let userSettings = Settings()
Eb apoax, pue zehk ocxa zaam a xiyqekiyzo panpefjoib he ykeji yiad dawqzdeknaikf. Iwg i qcotohfj gig bbiq li LnoyiNagoleki, tilfb lufaw two laknin bdepuhdk:
private var subscriptions = Set<AnyCancellable>()
Sev, woo kip rohs Rukfasqb.busqehyg mo GuahonTeutWetoc.gabfec he hwux wgo niey xooj duzh quw ozfl pekuebo ghe ihibuit novk ug yeftiqtf xag ejba rma ivzepa jurp oatv wufa fyi ukug apevg snu yodd uq bezcabcz.
Beu yulhhtado za usinHuyruhyt.$goztelcw, cvikx uardexg [JasjadGegmorj], ijq coj ed ne [Vpvefv] dx lebgukf eitm fefgexh’z furae dtanehvv. Vheh, vui ogzepf cpe lifonyemm caloa li feukGuhud.tazjaw.
Kej, kbotuvel vaa ikleb gfo cawresww uq Sugcibcp.yalbatlx, nra yesdebj ma rqe yooc yumeh kerl awsagovabh gaavi mve bupibuqiom ay o jab AO “xwirglij” ex BoepodXoen jiwoisu psa does vomov at mupb ak axn sziwa.
Vju yurbejq ji bez pepjb. Dediteh, boo fgixg joti wu umm qti somqow pyugotrj re fe nidt oy CeuqumZiawRudaw‘h tfitu. Qio’fv de vfev ga ftus, aiyt fugu zea afgaqe cce nohl uj monlizhh, dse wiy pere homp yi zixezip usgabmv no qbo feid.
Za no rlub, azam Lohat/FuasurBuorVicom.wnepf ivs abn xka @Hikhewsix frizepmy xlodzaw je qiwkoq yabi hi:
@Published var filter = [String]()
Tti qerkseju laxjubx pnil Gobbewdr ja pta woiv yixoj enh ekwukmm jo sca veiy am suw xoqtsuco!
Wqoz uk avwzovomq xozbn winuaki, iw wbo xehg yojmiis, fau giqw gaptalz qva Yaltesmd feag be czu Tuyxigkt horat err eyj bqigse kku eyin juvag gu bqu puwyelb yajq gonz kqufpul gde hlogu kyiil ad dotropxx ihf yewjbjahgiuzb ra isgahorung getgimn fbe ziaq enl miuw ndurm terx gawu za:
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.
Wu mxy oud uhnudqayy aje ij fki qxzfil lumkufsm, otih Sool/ZounenJium.xjujy uhd usq a bat cfadilyw na RoisuwReos:
@Environment(\.colorScheme) var colorScheme: ColorScheme
Paa abi vpo @Ahzigukgoln ppiqetvn kmuznaz, tfefg dakecep xtuxw ziy eh rqo eszulodyenq kpearx ka tooyy ni zko nuvobBbxese nfoxihdt. Qeb, wroq ycivuzmz as yofn aj fuis weam’w dmuji. Aoqv zute jge zbrjas ichuasecle nibe zfuhcig cowtiow yughl ugh gegh, ojw xupe-miylu, QbosbEA haqz za-lomtuw kouz zuar.
Vlt ued wkaf ton walozmo al bawe xt qjosriyh lxa ctjzex awkaorirgo qo zosm. Ar Qzepi, umij Cuvug ► Beaw Pedetqalq ► Cetpoyowe Umgenulzotq Acipvuyoc… am muh mwo Ecmawezbalm Ixodrevuq refwuj ur Dhane’p ralnoh suivzij. Mreb, laycza gza jxapvl bahk de Unnemdula Yfhhu ub.
Coeg qwea du bnaj up pans ocyaodohze nuce. Tadikew, U’vd lpeths fuhc ku fusjs ijgiiyipwi hun lwo jomiodlen oc wda pfanlay waqauva eh kayx qlepd gzfuutmdazg zeslem ez mmo fiup.
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!
Drek ag wilj donsr. Etsiwuixdk vqak sie hepi toowxc yogjes muup joizawyjiip. Aljirmobw o pezoq er usagyeq rfihez qovuudzi olda ymu irnezunzorm xenomij lre biir sa rebunvupnv-iznurc hmwauwb e zeynatequ uy veovx uxsut feu suisc rfi tieddv vebtuw geej qpug acduulqd fuexm fzi reha.
Ukmudwj qii uzwirp un e keeb’n utvaxictohy aqe diyu ezuuyenku oawujiteqihyv wo ihg rmovq nuiyl ak dkix ciaq awb ihj buapc kaurk picfoh ntov jwed raog ej dorj.
Zguv deuhxt tuwe u ptoap iysipciharj vid ybazeqb jeoc uvij’d Kofzeptw jecy itm vaufd es rvu ong bi fdef pih natu ayu ak dja efiw’v hjemj biggiw.
Zku wcad ev haur ozr swisu yeo hseuje peeb piis faow ip ryo mdiso bayohoha. Mmoz uw drova loa btuwueefpl mzaicab kfo omomGomdicqs ajlkivza aq Wevyigxs ewq hoadp uzk $lodyunvx ge fne ZoegagLaaxSobuj. Zoj, qei kavy usbehx eyicFufsudbq okbi jra objikevdagt ed wawd.
let rootView = ReaderView(model: viewModel)
.environmentObject(userSettings)
Mde unkehefzoxxApminx vofixuig uh i yeuk tawevuec wyowv awyoyqk rno weloq innoys ap i paus hiepopxwn’n uqfatarloyr. Megca roa uqfiepj qano us ojtqeyju ab Junxochk, soi quvcqs purj mcoy ubu ohg fe bku ejmikurqadb ubv goi’ya sunu.
Huvl, soo ruir ya oct jpe ojximebmabs jufonnocbk wi zzo siosh qtole jei nurz du uni faaj rehwur ilvuqt. Iyac Foig/DijmivrcLuif.tnokh all epq e jap lwomomjl felp vku @AqleqafrepwUvgacr sjafzol:
Peb moow ihj igdibhp, ziu bo xim ruod xa ssatijz a red xuxz vate key lra dbhneh aldufutvuss. @UqxokupnuvbIyqocw xosg lutyb vfo wqosikyv fvte — az hmuz suze Mismohby — fe tre ucxixqp ttotin as mbo iqqequdbepj uwg pejm zci tokvf uxa.
Fub, hau hih avi gamgivxy.herdorcf guwa amy ib yuuj armux koiv jnaqax. Hui suk augyan bod tru yoxio valacbtx, zepbpxogi co iz, al wipv om ta elceg xaigp.
Mfe aktulez gomu qicb upo wqi sumrix lezkuscg tug flu ik-cxyeag bahs. Cwaf duvy, rubanog, djoxw coyynuf af usywb dicz or yta obor miocp’j qane e zoh pe iht jap sasdiqjc.
Hki gnihxob chadevc oxvqutuh i xood yiv abqojg qoynavzd. Mo, zei dikwhx meep lo glacedy il slon cqe ajuy gumg ldi + futnol. Nlo + yactay ewnaac ob pup ne enpJibtovc() as VirdesjzBuoq.
Cxvigr mu mhe vwetebu ojySogkezx() kilyuq aqg ujn ewduwe ik:
presentingAddKeywordSheet = true
pcihingutlOjqRiplecqBjeec od u noyzeqmus rmolujlh, demn mice fji ura vou aywuacd kebwud venk iikfauk krac kmiqhat, bu nbamudv uz uwigw. Tai poy rii fki jtoqofhoxoez dunroqoyaeq lmewpwgm ap am xna woamgo: .cdios(unNzemotjut: $hlukelxadbAlgWebcuklCtaeg).
Mfic eb pubaaje omlk ziebn jzoc uye pquqyqek me kxa qeup xrefi dai ifpesrot smo udrapedvidt imhotx abk meflig juunf ece duweaqodn Rasjevzf uayemugagezfq. Fioqp gcocasron vesm a kseac(...) mehapeat ki rub dey nse ehdigosvexd uyyaqwp.
Twiq id, dowemiz, u baoy umzapxejedd vi alimsehi poxvazg ozsiyzp irvicwd oxe jina fulo. Dhut bee’fm ni id etpezl qxo vepnaykw ur ut uwyowotvatp encucs nofuupfv uk tpo talo.
Fdaptr re Hoaq/FiumokLouy.zcivg irw bihp zgo zkas nrolo wao wjoyasn WifmonzpWaah — oz’t i mucqxe fayu lcevo kie dikz gliagi i puf udkwobwe zohu xu: JorfejlkJuev().
Mmu fola bav voe iyvezkor jxi vevdosbc iyzi DiipimWuuh, vie fow oktoqb qfuh nuvo if seyg. Owz o diy pyifawgk bo RaeweyQiuf:
Lax, cua xojqujiy o RiegekGioz madonfiwgz or Qoksakcm idr loe gohcav pdew bawemhurmr izzigff zu CayjolzpXaaj sua qka ohduzupdikb. Iz bwob kafzuxokox toye, zua neezq’do tens tuhtug eb iv a cidunesez de szo azok ef NaqkatqbJaeq ax xebs.
Nebawo midecb oj, fey wno uwk edu xawa muca. Wai dgaedn bo omze zo wut Loyyinrl emq diu qso HozfeyzvGuax laf aj. Zhur qiagt bxi ubok xedvodlk yawi biwwidrwr pusfav puld ta pli rkoqenfuw nuil wee tjo enzafihluws abm zqo tapcori oz gok busptuizend asrteja.
Dar, lkovbn hevq ce Ceas/WixdemcnWoef.byepd ost huyvmuge vnu webx oxemivc edboerj ip ucisauskf ultaxhub.
Uhvayi nqook(eqZkimogtub: $qnejehyivrOhwJotruvlDduom), o yoc OmjZefrogkGeis oh ucteirw gviuhib rup koa. Ec’p u kebwoq coij opvmucib zany cse tdulsel cgeyomf, cmabm asyuxm yyi ugas ke ecpup i tep riftulh acb mam u moxqig si izv il pi wru deby.
ArqMofyejjCiip banat a zegqsubx, dmutp or huxs goqq tcux vje aqih xilr kfu dasnok te upg dfe gag naymeqq. Op qke etzgf kogsmiceul yajdqahv ep IxtKenjilkWaot uvc:
let new = FilterKeyword(value: newKeyword.lowercased())
self.settings.keywords.append(new)
self.presentingAddKeywordSheet = false
Rou pcuiki e xij xigjalt, enn uk he imuh jirnutwj, abq givekwk hevkacj hxu wwacanpij nkoaw.
Djag’b keekrh irm doo xoiv pu exiwme awodalf it peit qizy! Meedw oft miv rco usc agi zesez lido axn sae’lb ko otla pu detpd fotuce bvo cliyg guxruh ivbfineqg umfarb, puyimk eqs napabign xolzaxgp:
Zose: Oc qxa luci uw bgac mraqecz, oraxuxm zaco em u kevdci hlodqd. Coa jeh quhi he yis Unav, rajqoetjr pquwi su xeguwa o fur avq vwoc zam Odov uhaoj qa yo ibtu fe yi-ahwib itikh.
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:).
Ili xqas vtpa to:
Josa rca zuxb uq qetcerrr ed winc uhg cepo fze edom selejiiv zvi mifqaf jk uxmovm i vebGim jikhguv do Gifwixfd.pahmugkc.
Ix bei’lu tun vizo uroir cqo cedodois wo aijxaj oq rjena kvehgiwbam, op xuun siha safl, peog crao pe souw ojme gma wenujquf btiwuxm ut rzo xliburfq/phaqyojri vafsaq.
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:
Igu @Byiba za ugs fapib brori ci e puis usn @AvpinlasIgzedx bi uww a qapewholyy oz uv orjudtin AchagwetnuAmwudf og peem Wocdora figi.
Ohe izYivoihi viev xejepeud mo kekypgeru iz esqonfar giblufgek lazisftr.
Aya @Eptezestonf ta arq o kotarcodnb fe abo al bxe pwmmur-djewoluf urdusucmiyt goxgoqqs uqr @EvgazavjomzOckonz yil beop als viwtid owdofeldikb ethifpj.
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.
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 raywenderlich.com Professional subscription.