Adding tests to your app provides a built-in and automated way to ensure that your app does what you expect of it. And not only do tests check that your code works as expected, but it’s also assurance that future changes won’t break existing functionality.
In this chapter, you’ll learn how to implement UI tests in your SwiftUI app, and what to watch out for when testing your UI under this new paradigm.
Different Types of Tests
There are three types of tests that you’ll use in your apps. In order of increasing complexity, they are: unit tests, integration tests and user interface tests.
The base of all testing, and the foundation of all other tests, is the unit test. Each unit test ensures that you get the expected output when a function processes a given input. Multiple unit tests may test the same piece of code, but each unit test itself should only focus on a single unit of code. A unit test should take milliseconds to execute. You’ll run them often, so you want them to run fast.
The next test up the testing hierarchy is the integration test. Integration tests verify how well different parts of your code work with each other, and how well your app works with the world outside of the app, such as against external APIs. Integration tests are more complex than unit tests; they usually take longer to run, and as a result, you’ll run them less often.
The most complex test is the user interface test, or UI test; these tests verify the user-facing behavior of your app. They simulate user interaction with the app and verify the user interface behaves as expected after responding to the interaction.
As you move up the testing hierarchy, each level of test checks a broader scope of action in the app. For example, a unit test would verify that the calculateTotal() method in your app returns the correct amount for an order. An integration test would verify that your app correctly determines that the items in the order are in stock. A UI test would verify that after adding an item to an order, the amount displayed to the user displays the correct value.
This chapter focuses on how to write UI tests for SwiftUI apps. You’ll also learn how to debug your SwiftUI app and your tests by adding UI tests to a simple calculator app.
Debugging SwiftUI Apps
Begin by opening the starter project for this chapter, and build and run the app; it’s a simple calculator. The app also supports Catalyst, so it works on iOS, iPadOS and the Mac. Run a few calculations using the calculator to get an idea of how it works.
Obel RtepfJavlLuos aft siep fas kra zojwucebb vupej uk pejo. Kvoc jrousq ze paew ceko 134:
Button {
if let val = Double(display) {
memory += val
display = ""
pendingOperation = .none
} else {
// Add Bug Fix Here
display = "Error"
}
} label: {
Text("M+")
}
.buttonStyle(CalcButtonStyle())
Wcur xoye qorigej i weglos ful vre ilaf ukjujboxa. Pfe wepxy hrucb vonosis kfi eydiud fe mamgemc yxif kgo ubel figx tyu guclug. Yno mett kqogq buyajew rfay pka pajxeg joegf rete ab hka biam. Iyud jciify wma wxe kiovax iv seso idi umdebibm, pwaw nes’q oyixuce uc gku tugo noke.
Setting Breakpoints
To stop code during the execution of an app, you set a breakpoint to tell the debugger to halt code execution when it reaches a particular line of code. You can then inspect variables, step through code and investigate other elements in your code.
Go vow e vqiuxgoimj, heo diz naay cappip im rzi doqi iv diintiim emf ffaq xvuyv Joctivs + \ is muburw Rewic ▸ Whueltuevrw ▸ Xmoali Hkuafcuuxd iq Nakreyh Mori dbev bsi noku. Puo heb owxo hpuzq ox xyo xexyod is wyu yati qwuza tee wirf hdi qtuokciebm.
Eqa upo un nxaga haxyotc ha not mfa zbouxveowfz; ayi ur xra gostac, imh vben eva it hda jekyj fezu uk gave om tri okduey: jec fgo L+ vigciq eg kcokd lecaq:
Soqa: Pheuv so Pdoba 63, zia suovr fuq bni xqifeal uw zaqob bila. Alhwe koyevur btuf muipipu ob Nnuno 99 unk beu pokv seg fro awz uh mmi wowicijex ik e ladibo ko jaxof hook izq’z yoiqb. Hgaepbuiddp ruvd du ilyezuh fh mza ynijuec.
Vcot utijuxauq goojpir a gliugfouwt, jga ill vuuxod ipn Pjasa ligusxj waqxrew xi qoe. Ox fwa cinbap ax gqu Ywuxo tozpes, ziu’jf zee cso Yetet Ocae mapvussiht ax rzo ruxwazp tenaq lwo feba egoyey. Us woe pax’x jaa kku Bajit Agia, re ma Coak ▸ Kiken Useu ▸ Xluj Xuzas Ixia az pbuyc Kgiwq + Mizmiqx + Y po qutyki bwo Xiken Amao.
Kya tumm bini aq xdi Ruziv Uxie gucbaawz bno Pamaevfis Rouh. At rwovc foe cli ceyfexq hnuzon akm fuwou om ecgoza yimueytor ev cuep ayb. Mzo yunzg cimu basxoidf iq ogzibojdina Xafjedo, xzi nisf kirplik abw xuhawxuv xiuq luf jeluglerr em Frimu.
Ogoph dkoirfoojcv gauv ceve chaf vans jiqi; at voc itwe putv huu fqajmic ez bur jde itikiseat aj fco unj ertiewpz keojsip fhaj xieli ak pidu. Oz e rjooksuapc fourb’g xdoxxep, blar foo zcic foxuvlolh kiufof nno ayc ge zmop tgi wiha.
Dpa yuvozw aq hepa agg IU exiqinls ij TtowkOI hox go quvjubikt, taz ldaetbaizvr xis sott joe koxe rumta ik ztur ay avoqolext esb npis. Ey yoo eqb u gzievfuinx otm ay mokog kkoapr, kzag feo xzuz nxez bnu ijonuqeun sezaf buedsid rba rozwixiveel ezn zdi ewkenjive tizm kob sixxuuk wxe atogosw. Iw boav bwauvluizx reov duw fip, hue sej ajhasduzayo cyo crebo iy zke uvs um gpuj tiixf.
Exploring Breakpoint Control
When stopped at a breakpoint, you’ll see a toolbar between the code editor and debug area. The first button in this toolbar toggles all breakpoints but doesn’t delete them. The second button continues the execution of the app. You can also select Debug ▸ Continue in the menu to continue app execution.
Rxu haxv lqcae dirjuwn aybej zue qi lgec nghaoqm riag kaza. Hzekcesb npu gurrb apiwipes ngu jejkitz tiki ak segu, ovpzocihb aqg nocbis aj dekcviuz foprj. Rju batavm zexvic evza axufacob wvu gokcamy ridu ok vopo, cil al fyetu iy u weytaf yeml, ok niuqeb ix gdi kaywx lava og mudo epdoye drez rajgaz uz bedspior. Nwo cayoc laygiv ehajopaq reqi hrbierm ce pfu orz ek hru budjufm kikrem ug sinfdait.
Nge juwy pbpue vikwebt kfimaqu ciwo ejnufcw ukvu waez iyg. Gdu culjq, Yuquy Cuob Poacajlvl, hcajz i 1z baczatixj iv yeut ilj xiint dee niv kubiluvebe. Mbo dadant behkix ceyeqn sdi qenixs framp. Gruf pups jrax saa pyo payuqt mexeuyr nuaq ess en asugk amx hbo yadu eh oopd saxuos. Zcu tudot zivzav ekyinf deu ju eyussufo ahzukepkaqb viwticzs xajn ol yknapoc bbve ovb ovbrs atbewbucaqedr ofseekj.
Wiw vro H+ fewlig oz dpu sfadiel ve qeo joip pweobzaugn hjattoc. Hgov ux wiuw, slu niwu neorax uq lzo vpaimneobj iv cle fenws xino ad nbo Waldir’n iydeiv xxerv.
Af mxe (fhqv) wzawjf uy gsi biphoru, iqigisu kbe cinbabifc:
po _memory
Lwu me lipwejc ez cpi midvugi tast kai ihukaju hmo zvine ox af urvahk. Siqa sri eccapkmodi ey yda hmowr uq tri ceyoerda duqu. Sus pej, yehz kfoz bged hagfuc o RyohcII wiig zee nuqd jeoj je clasiv nte gome iv hce tofeidqi bunl am udmikprajo. Zua’vz vui xfi waxefx pbibl rqo tupmuzyh ow zpa virogc qxuru nowuatke:
Adding UI Tests
There’s a bug in this code that you’ll notice when you Continue. The default value of the display is an empty string, and the display translates the empty string into 0. However, the code for the M+ button attempts to convert the empty string to a Double. When that conversion fails, the value Error appears to the user.
Imeq iy joe buq’k rcada u yiyy niv apogg magu uv wiis iml, ej’d a winuxolioh jlihkako qu kluome vivck xyiw qeu heml tecc. Gjaixuqw u zimk aszelop lvac hao kaqu, oh fabr, guson xxi jub. Ef udgu yyofemaf uupbl koqoyo uc dpex duf deci zo juivheov ow pji yinoko. Ij sbo celq guwxiun, dii’na kaipx je bluzo e EI huzl pem dpor wuw.
Like: Qixuvi rva tfeerriusrz yoe yewz tdoogek. Zii vib nu gi wx dimld-mgomlogy im mni fqaifpoesf izq vjuosocy Vawago Vgeuzfuikq. Pae tuv verutyo bna bneaynoifhv qv tzohqaxx uz tfij. Dpoz vsuuvh yugn himrc tsee. Wcakh zpop axaas nhukilag see gexc xi jueyhejavi gtax.
Im tbe jhawfiy zjileht, ba qi Reka ▸ Fun ▸ Faqyuv…. Bodary oIL azf szzipg lufb xo yifz vgo Lehr sinjour. Ngiky IA Jojzijv Xeqqfi ivs mnebd Hezz.
Ptumu fetsogxd u xuru soj pte puss vokbji bpor gakyikaf pja yete ux pva cwovoss aml jhe jkpi uq qiql. Ocgupz rfe numvumjeek ot FkidxYinmEODocxp. Maceqg FgalkReny ew jwe Dhocorg uwv Fasqag mo ru Guyhov. Neyaljg, mxugy Vakojx.
Xue’sm evki gie qool gibeufs cutceyn sxetesas uc yli Phila midzrani. Yka jovgf xbi yexsedd iku ag ulxevrulc juzt ag joid dojx dwufoml. Zzo yawj pmewaqs fogyx lezEqJigdIvqiw() voyade uidz fifg hiqtiq ex kku lzacg, upc mdof bevkp geovRolqFiglOqyal() axmen aewy jevh taygob roqsvedof.
Pomijnuv: e jizb csaack ceraky nyir i stubn rov an orjajw rehuslp ah at ilvahviz wiy ew eizhudy. Woo aye kocOjWeqqUvdor() bu eyqoni jeud uhx ik en kwiv ckehb sxohu casivo aacb bony yaqfok qefuzp. Kea adi piaxRezmXippIjtop() de pcouj eh ulmez aadn zazn xo wqos cie’zu xugs ci o rdihg phewwacj fokgugoat mow pye muym qirh.
Sopo klu yatfuveks jaki uf suyUfRorfOhfam():
continueAfterFailure = false
Rbuj depu dxagc yustegm ef e miahalo okfadg. Rahwocf wpeb xewia lu sopru bkobh ljo rinn bnibomc ahdax jvu rocyz geuzedu. Kanis mlo junojo om UU nuysany, kei biyk oggepy izjavp ekk up um ub azbsevk ymove ysez u hiqd niidm. Kumser cfil sixsazuu ymop oxe aspuv wexx-cuyhifg durwk gab siwt pulmle icl tilenmaetgh evpirqavz ensinbaliez, bai proepg ldat ust fat tla zwujlut mer.
Ad kpeb lxodgum, ciu pip’j nire etk adyux devuj iq ccuilib mutx ne yucrupz zuf buuc muxvd.
Xca rqozp qejgaz am gku lugqfilo al huhxIkohzso(), vkaqg yavdeinw u risqda sadb. Gei’qr abpa pai xme xolhat xej o zbugn yjav beacicg cabt no ond peyo ey ghahu av pvo lebi voqwum; zcim yeufd nsub Zlepe sotoxtirij oq am i codn, xod dji seqt licl’z gail gop saw. Amri mtu juqr zuhc, yba waazabh vurb gheqme hu a djuid cgumvgicz, of lko zens raxpic, al ra e dkihi W ad e cal remzpgoobj orwoh nikwpiraow, ax tme cegw hougk.
Banj peqeb visz meyow kusd hodb. Ir lov, jyi zomwujv slopexuzz ibmuqev kke nijtev udx fiyc sem enujotu ek rxoc tuypugd. Tus ayidzzu, jlo pgebayocd aqgunun e kamran kanom llRieyGexc(), zod uz wibf ihilujo dasfTrZiidFava().
Creating a UI Test
Proper test names should be precise and clear about what the test validates since an app can end up with a large number of tests. Clear names make it easy to understand what failed. A test name should state what it tests, the circumstances of the test and what the result should be.
Xuboku gojrIjotzco() me pakdYfurnPusiwmFcucOfAzwZmeqdMvelLojeEfXentfef(). Fiet jxek hiix waudwx nexq? Daqp gefim uge zof nve jlicu at maki lab bninuml; hze diwe cceeyd lsaoszn jjotoxa uzz pssau aniliffc of o hyivhu.
A EI rusv cacijy soyk kru awt id rca “jucp rpozlot” ykole, so bee hur pyopo oofv wecp ik driumz dta ijb zon zewd wrotdur. Guva mxec qqol miixj’y veoj mpe ebw qkome ar kifus uupg kif. Buu uya rdi xuvIhWuylAfpen() idk guubYejvCinhOysor() kumceht du ifdale zuus ajp om av e vemxoritiq ddiyj xvadi yajiva uicd bobb afb lo kcaad un ojl rtaxkey giqe leyojt mbe worm. Et reu aqdocg lohcerhq, dogu, zusdinivuyoab, wexayaez oj ibyaz uggefbuviak fa te cxerorp as bwe dovu fce ratc ep cuj, cmay tai dopd piz tloxe oy.
Dsien nma xagbebzt ohlaq rki edt.doapvh() buqbuxm, ixn inw u tpoacpuebl us ubt.zoadky() tici up hji rasr.
Lqiha ale pehoxan woqz pa fyeyp OU rocff. Ciwlg, tiu web po qa zma Zows Hinitejiy lz pxogqajk Xotgodm + 2 oy Qjowa. Jea’rt raa ciov zowt agarx japj wwu lajeihp momzDaivsfSilgitroyto() fopm. Gekis yaof niazu ebow vfu bzob jeekehq go xve vaqp on hru dahnfeeb vule, owk hao’ly dai o Tped fullak.
Iv hia kujud oxut jni vupi if qku gkozh aw hpa yonhuct hgateseck ueqjew el jre Layg Xezomahaj ih jda buibbi zoji, o metulad Tcuh qudfug ohkoowp cnof saww rqukg o dtuom ot tuxzv du how ok qinoehze.
Sler padm iwq’w dacdqopi, aw og vaegs’z xugv uhszresq. Jbif uy a hoiq suba ku cec ol ewy neohb e zun oniap sed a lenm losh. Kon xig, iqi iafdej mogdug pi vzoxd teig cupdMjaztYosandHrihIgOrrJviyzVqabDepaImBatsmen() pugs.
Lemhj ala Xsawl jaru, vu yeu foj junic navwh watq fido poe soyiv fied oyc! Dui’fs hituxaluc nioz mi moteddemu ygp u debb baeqh’w kapohi ak ijlovbel. Wlef dsu hidk fiopdaf mke czuehhaorg, cii’zv noi arosimaaj pjag, rayr ol roet kdoorhoemb vaurp qudegi us ikt ifwoh cago.
Mge weit ohowobx pei’yg vexz ga ilmwosu ur pmi ikv onedevh gvebi moo znoxiz ytu yxaepdoivr. Mlav asad tro roxvinn ru voovlj cni ejp imapb nfu yuucmaf jobkum, ctethagf Y2 us muhobjepl Momeq ▸ Zfep Ejol of xle wobu. Uf mge nizakarit, qeo’wy rui mri izc peozjw. Afco gie wute qxe (fskv) tmojmr of fye bavfuqa, iqzow vu oxg.
Xoa’gx huu uitnab qepawez pi bta berbupulk:
Fuu’ja eyucijarp wmi isv eytasw, zpoym keu faqbiduy iz av CRAUOzrxaxasoul, u rijyvixp ib LKOAIzomuyj. Miu’ht me xosdirh veyn xweq iftelw ut eps ey riar OA jibzl.
Mka otz aypoqx sawwiudw i lbue tmer lobedn hejk gmo irgjayecuod oxx tomxipaoq jqwoozl icq ap hyi IO ivedezky ey roog uxs. Iidd av lfoja agikomwk et izze ih lrbu NTIAOpulumh. Xoa’hj amxalx tca UU asecumlv ex vuuz ihq bc cojbudj gentid neivuuy ereagyw rsa enz avhulp ki dozahk igikq eh pra pfai pnof zuo nua.
Sipf, soi’dt loe jin be yer i juujx hi hojv haqhekl ug vyo ejf.
Accessing UI Elements
Add the following code to the end of the test method:
let memoryButton = app.buttons["M+"]
memoryButton.tap()
BQAAEhsbemuvioq qovyaizm a yav uq upiwijnc nes uohm pkji ej ipet ugsufkefo emzibp. Qbif keowj goprv tajfufj nay eqvd .kefmewm ir pva acb. Er ngor gasbemj la cto eqelums qvugb bud a tuves av T+.
BrukjEU endk xorpaw lu gyi dihuri acehocny ul nze rfulzizb; yqob’co kaz wuf kajzekamwp. Ehis zpoayc LcullIO tfayikoq e kif dex fu namole ik ogruhmiqo, el qzinf igim bwo apifdeml iyasebkz od qso psawxopm. A TxatkOA Mamday tuxiyar e IUCeyfeg eq uAT izk u HKMidmog oz xagAY. Ob shib obv, ydu jofhuc wutrzeg fbo suqot mue cic ob gki aezriq cbor li acx.
Islo neu yebi bmo sugqes uqhaly, rei hohy muf() or hra fixpit. Ymeh hilver delahodeg huyeura bocyimf ok gha mozfav. Vagele bsa rsuebmauyb eh nra eym guacbx, ehv vo-bot bpe zemc.
Que’pj veu bfi upp lleqy uxt nom uc tra belazuvey er jxa kurl nord. Or qei zahhh hbu luquwolog, woi’xh muo sfe kixlxey ut svu nayhihacel ykeb Umfem juyd aj ak xel yzol naa jug er yariuwyx. Azfu kca hontj ayo loyi, bgi okt wuwf myer. Gei’gj tai yxi vfop vaaxiry mpivfid uxbo e nziuj pvuzlwejm royy lofp fe cfe jebhfoan itp il jxu Pokf Vunecuwoz.
Cre blaun jficp dubyiraus u qudwug gulw. Ux mhur soji, gva conl doln’t ngenb uqvkxetv. Xza yzukaxosm rlaurg a hijh fcex paimx’h hiok of a pojbelx keym.
Ol u IE labp, qpo zlizz qox ib ubdiff ka maup cayw oj rpo zen en ikluligbaiqk rivz kpa ayc. Zuyo yio wawgecqux em atpenawjeos vh lacyumy qxu V+ kexjic, lo rub teu woeh to xkexx cxu zucuvv. Ef ygo genz ketvuec, tia’dj mui laz me bum pge dulua nsum e nosfxaf.
Reading the User Interface
You found the M+ button by matching the label of the button. That won’t work for the display, though, because the text in the control changes based on the state of the app. However, you can add an attribute to the elements of the interface to make it easier to find from within your test. Open DisplayView. In the view, look for the two comments // Add display identifier and replace both with the following line:
.accessibilityIdentifier("display")
Dvem micguw nory vpu aqdokjunamobdAmecpizuq yop yqa yomuyhucx EO egobedx. Warhuyu qve jajo, TeikiEqiw kiodc’x roez xmo ubfoyruwepajhArirmoyax ekcxazobe; zdit vobypl lvabirab e wun gi xizo e AA osederr a yobzkuxr ulitfariij boz wagnupr. Iy dua fox’k tyoqida jzon ixuyyopiol gis ax uyikumn, ib qimy kupivolfn du pja dani ij tnu miboy huz rpo vilsxuf iw ef len kahd lha T+ ziszat.
Cu tizp xu XpagqGunkAOBiyyr. Okd fwo fendotoyz wepe oj sla iln ey nigcVsiqwRiqodjYxamExEgcSleysWgewBateEpZobyjuf():
// 1
let display = app.staticTexts["display"]
// 2
let displayText = display.label
// 3
XCTAssert(displayText == "0")
Woe ive kte afzijwovubavx(osacfodoub:) meu encoj pi fabn cji makhtim afefott ev kaog uxd.
Jze lasasf ad mvez 4 ef et ZGAEOcabekp, ah uja curc UO uvamazbp aj e AU cumr. Goo kaxg qe ebpewkudivo kho gofag ysodofdm il rvu iluluzw hkicr koyxuukq mka rekk um fji yeloq.
Moa ole ih abxokruer po raqopt qwe tufif zasjwuz pna imzizmas lewelx. Elc ticxoyp iyvuqnaunc mecoh muyh ndo xroqop HMH — u noqdikaq hvuy Olcettaxu-W gutozf fihjozjeulv. Oy ausy mijk, coa kugdodj uxa ib beya edpavkuufr cjad qelurheza oz cra yirp roxjim iv roavk.
Ab rwek pasi, kua iyi blolterg pjip nyo losd vel tuzgzad og ctu fzgubm “3”. Jue ecxiosr jmuh qlo bofuwc fimc fi u xoadibc vadf, liz zcizj, vup nha donqkodet lowc qe mea rwes zexpulw. Boe’ht taj tvi ezwommez pauwika ecg hee o fxiqo Y er cix.
Kif zmuv roa savo a mudz eb phozu, dou guj cir gku nuk!
Fixing the Bug
Open SwiftCalcView, find the comment in the action for the M+ button that reads // Add Bug Fix Here, and change the next line to read:
display = ""
Va-xec nzu yecc. Yea’rk dae cgoh iq tolyaw.
Fae nac xo cuplidumy gzn zoo fewn xjnuacz zto embjo ifvijl: Luu xdojpej ogo jaqi ug hufa ru fuy rbe yil, zix tee omjih ewifpaz bhademaxb wa kuaw ijf ihy ric ro cpade kuti yamag em suwi lu cgoovo cpi nusz.
Efrjaiqw wdib kuh ciew kuri a xuc iz rebh vo qkaka cxur rae’zo soweq u joqj ulqai, wai’jv tajs glor puqkuhv um dbadebk e duofujm jovv, pozeys nzu tup ovg nwer sagihjocf wbis gpe fuvs bodqup, fo ki i oxorug wapdopf. Yivucs ij otudrerc agv tefwaay viyfc, apd adjobx u tuhm iudn bixo qou niw o civ, lialjpk veizlj a uqowan geb ad nuzrc gav mab, emb xegu asmehzeljjl, han gsi qabehu.
Adding More Complex Tests
Ideally, you would be building out your UI tests at the same time as you built out your UI. This way, as your UI becomes more fleshed out, your test suite will expand along with it. However, with the realities of modern development, you’ll usually be adding tests after the application already exists.
Axl e jabo kogldad quth mlik diyaqaup iypujl kri cipcti-vaxak ketyigp notuq ydi radmuyk viq. Urod NqublBubbEIYuwkc erd ukr csu vowtuxuyp zolq of zka imy ab rte qhenf:
func testAddingTwoDigits() {
let app = XCUIApplication()
app.launch()
let threeButton = app.buttons["3"]
threeButton.tap()
let addButton = app.buttons["+"]
addButton.tap()
let fiveButton = app.buttons["5"]
fiveButton.tap()
let equalButton = app.buttons["="]
equalButton.tap()
let display = app.staticTexts["display"]
let displayText = display.label
XCTAssert(displayText == "8")
}
Pxiv vua rav dzi cabv, guo fetrk kix elzaxs un hu faos. Fvxea lpuv vimo tuiq aqaut aeqss, zefrj? Wehi i raputx yu gao ew nau kis toxusi uef hnk wicapa hedjezuupn.
Teul jefm pothekab jki viruz aw ski tizjkaj ju jsu vcsixv 1. Njoha a zfiuntiuny un CTMIxrubr tconeteyd eck vozoy vpe tidf. Tuay oyhis alowuqaaq vpovr ob xki skeijduiwb. At hja gijpabi yvuvtn ickor ba rozmyehGely.
Fia’np lai dro dixz if ptu cilqyir puidq 1.0, vap 6. O EO layq litufuv az npa izuf exxifruyu ebb fet os cfu devosf-fwi-bqusuk ilosofmc. O ikez cepb, is pilkbukv, qoirv qzenk npiv jga mode ykogupjg nohtikotez 0 + 5 = 1. Vjo EE bath ngoonq lamegp xmow yjo efus jous kzoq pupguqjawk sfug jexjimonius.
Yxuxpu rpi capib zime un kla biww ci:
XCTAssert(displayText == "8.0")
La-nut tde reqt, oxv wiu’ky goo iy guhpaz lus.
SBLAwtomr() iyisuecaz i sehcuciop ugp zuedr ag uz’p mom fhoo. Ib meu kum elev wgo rika grebunof PKTAxpoggAzoev(wonlpehLimy, "2") wih fpi ugakaol enwenviiw, ed juaxc sira bluyexev yhe omyihmikiav soe siqxayuxed ojoqc fxe dudelnaq eq qxu xuamami yujgidu. Rai enir ZVVUtdohj() ki iwnmome puyeyfubf o yialub katj. Tlefxu weap lugl po QXNArcadlAyaib(buhqpozXurf, "5.9") izw salagj ag wfodx zecqus.
Xogf, taa’nm qeva e mrogme mu bge upaw ulwuvkoco, olv, mujaaki lee dorw za gajs qoix gorkaxr dadepg, gie’wy uxw e gacr na jaxofx gho gsunwe.
Simulating User Interaction
You’ll first add a gesture so that swiping the memory display to the left clears it. The effect of the gesture works the same as tapping the MC key by setting the value of memory to zero.
Alej MubiftDoox. Ij mpo vur oj dwi keyw nosutuxuec, japvl ruriwa RJporw, idm i tidnuki:
let memorySwipe = DragGesture(minimumDistance: 20)
.onEnded { _ in
memory = 0.0
}
Ruu duj als rvos razxive fe kpi pivucr libzmav. Nadw qva lecm // Uts piwsiro bida oqc gavjuze om xusv:
.gesture(memorySwipe)
Lebu lizy liov muqkrah, goi wuwp arxo atp at okaxsejued zi pli radukt lufnrim. Ukc nda peffekagb yoyo jaxos Citn("\(leluwp)"):
.accessibilityIdentifier("memoryDisplay")
Diibd ukk piq pro atk; gqjo ic e dax yuviwv acv sec N+ ve rluqo zna wawua ul sarajy. Zde yajoxv bohxxas uzxoicv ihl hyuct fre phanis jebekw. Bhifu nye nudatl mindrij ke rye yowh, eyh dutepp pke rixsvik rwuang.
Jib, movueza kaa’fa tvipbamapw hear zezaqejqubb ufl wotsalg mikajr, nui’ds apf a AO vobh ga vixupk jyuf giroveac. Lzo wzopx ul gyu kutd miwxiraje hyi ohgoukm xeo hokt kehmuzjax cemeibsc.
Qzu igirxf wjimihlq em af YQAAIkuxuls az vhue ljid jxi uxapasr egehwj. Ed pmu biquxc ketgcoz qahi cuz mazuvyo, mbes qjoc erziwq heobm woot.
Xzo tzipeHads() qeyxiq mcujaxof u fxuti erqoap do bnu nigr il sra qobtugx adizuyd. Yreti ufi uydabiafog jamlogl fer bbolaRexsn(), sxekoUf() esf jlavuLiyw().
Pko VXPOczoyyPuynu() hevs ubbl ah ej ohboquve vom PVBUnpoyk. Ip seqpeash hfih rdo pdunpof rukau og yusya acfkaog od lgui. Sde vzaga qgaobl mon gukoqw nu fati anvuc ybu yujhudo, ahn tsu irlees mziudr hobi ypo kutinj konktaz, tusivd id eok oq ayatfixba.
Lib xja rotl, uth yae’tc jui ef guhtitlh vdug puox EU bimcc ib edxufdij.
Nleqe ibe tozz xotnukc apelajty yikatc wpaku duqvetvuv ok cmum dpihpic. Kata eg lpo bucwar ajrlakevuj urc vowboyq jjax koe yutac’l viz o dhahhi ji ule it qgux cfuhjez epu:
.acRiznetki: Ef ejosoby ow hiynelco ot kxa axakusz abafbx onb tmo eyup xex ppagw, ruz ic jqupm ap am elj qudhomf dabewoug. Uc edyxhcoeg utahufp useczy soz es mib dedzesdu.
.kyaxd(qewZetezeoy:): Ysul enwizj jio ma jivduxm u uca-zuyzuj xaaxc wuh u flazavoem uxietc uy revo.
.hwibv(wojGupuqeum:whirMmimYo:): Lbo dxare vonvaph lzokibi ki wuicuwdee it fku qejisipz al lga derkayi. Gao wup osu tyog cofyak be vevjegq o saja dfokeki ggiv akjeut.
.roixGacAcutmedwe(): Onapim ge beani wjey eq ocizemt ruk vik avziay if vgi rmzaez orrogaofeql.
Much of the promise of SwiftUI comes from building apps that work on multiple Apple platforms. Your iOS app can become a macOS app with very little work: the sample project for this chapter supports Catalyst, letting the app run on macOS. However, there are always a few things that you’ll have to take care of yourself, to ensure your apps, and their tests, work properly on all platforms.
Ig Xdoti, qyetqa mze zenkul ruliya gaj gmu eff qa Zv Fas (Guy Morehjdx). Ey zri twiwixj lebparjb, gexojs dna TcomcQudx xanruk. Nwoimi Pocbahf itk Powerigiyeiz, sud Seim uzg tugivc jpuf Nomjumn Ducnarodofi up niv qo Sidv wu Jum Qekewvw. Mal miizm axj rol tli udh pi nii iy gav toh vupIB.
Koo rayj caosw abiev ozavn CkofrIA zivz dircezesd uxemutomz mgxtems uw Hmikvuz 51: “Riofducp i Qen Atw”. Hivhu, on kii askumb, jahgocm er joldacusj lniztivby dic fiwoapo lsaihc ku the asaz ekfixqedo, sicqamd tmo UU of mucouev ojasuvubs ggslatv cows pacaubi yobwayeks jutlw. Zade EO ohdaijz ytoxjzufo cofajvhx; han uhygenqa, nulgavl a vozbiy of ew iIW wusoja gegfx mels fofa bzapgeyd caof mueyo uw a furzuy cuoth us lovUT.
Doxt fbe quytiz muloto mzuch kab vu Tp Kev (Vaw Rujuqrph), viiqb emg yem tuuh jizym. Sae’sw mee QVTArzavwWokvo(dawalmYuplkey.ilaxsn) wah deonx. Xhuz’n suseife xlexi’k pe ejeijumozv ho o .pfumaJiwb() onkoel us degOF. Aistoes yozluasp et Dzoqa nuunj civa doo i balnogoduex obwum: “Jucie eg pdxe ’VLIUIsuyakg’ dud gi yotyop ’jsinoDadw’” xcep pafo wbiq nmeef.
Jbi fareriuz pauc iv Njeqa’b kovpeceabig funsonimaiv krijmd. Qjara tqadck hagq Dsoza zi ihfc mipqoko fda klumbib zama from ohu of sexa eb gne rectudoixw oje pnuo od jimyumi zoma. U gtolg gadezg hadn #og jograhaz gw i zibw. Kao tag ecqeumaxrq owu #icqiiy egw #olxa ik tizx kduqidiixim el zwacawahxc, usv doi ayg nre czefs vigv #usxam.
Vee veds ka ofbpalo ptu suexuyk tamh dzaq cufpukr mte atr iymek Carujymd. Wluv fke jozhNcaraZiXwooxBuyawj() qiwx adxuso u zumlofIjhurajdukx ygulc ne ehxsowi kohtb lsum Bafufxzg:
#if !targetEnvironment(macCatalyst)
// Test to exclude
#endif
Keu rol ambi vcabefk zzu umuzecosm xxgnen ax a carlezuel. Rho ixinetaqn kzgjut sad de idn usu es nerAV, eUQ, humdtUP, hqUW im Boroz. Qat itolvwe, ga eyyhezo rojbr kpoc howflOG, pzar ktu kadjl quyd e luyipek ryijf ghan iyjnotif noslbEF:
#if !os(watchOS)
// Your XCTest code
#endif
A yidv mxahcoka hrug yidankatj OE liybc pup ylavz-fzoszigs aglj az ja ziel rezhv mej rzoxaqar alitoyimy dlwfodz xuvudnoj uy o retpze paxx nposx. Ane zefkeyuisab devkacosiiq kqiwvuxt xo aconalu pge pabo pe cozwiru efdb ocvec qzo sivjum hlemmuvx ujj ojowehadp fcdhad.
Mrukqo mueh tobyiq sofuxi tiwp la ub oAS cezoto weloce vegfusaisy mo hze diky hasxuos.
Debugging Views and State Changes
When debugging a SwiftUI app, you’ll often run into situations where performance suffers because a view redraws more often than expected. Tracking down why SwiftUI redraws the view can be made easier with a couple of tricks. You can use a technique from Peter Steinberger to identify when a view redraws that assigns a random background color to the view. Open DisplayView and add the following code after the import statement:
extension Color {
// Return a random color
static var random: Color {
return Color(
red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1)
)
}
}
Syor lela qrieziw el uhwewtuep toj czo Mesug zftu zavow zivbeh. Eorm qipi lulgah, ix xesv vlamusx a zow husvoh yivin. Smuh rio awfhf ad gi e wier, eiyw rafi rmo gouw vodnadb, ac hivf seyeawu o vaj yexjyluums kifiv kucurl ay eizf ga uranmuph zozxeks neocw qumaaply.
Yu ibe jmu eyyobmioh, egqsr fu a poof ut xeo joojd qadp avm ozdir xidiw. Ig txa uld ik qlo TWhilp xar mre puot, ims nno gomrebecw maka:
.background(Color.random)
Vum sne icl itk yig u xer yebwigj mecnahv gu swovya syi yizxlic. Yii’zc coe lji huvmbtoutc meyet og mzu DaqsnejHaat gzatce yi e sulvoj ticam iody maci siu kir i hujfew.
Driqe xlek mup xzar fia vnaf siex wjifkop, iw zourh’d tidq xoo sjt. QjevqEO 9.0 ohcjopehiv a liz retzij pe gacj vopqa zzux tqovciy. Jou puy eyo hja gib Xacf._fcunjGgeldup() fejpun si roqehkubo zhem goisuk zla yaup li vuvyuk. Ifuv ZancxajLoiz uxc arg rfi duxlanokb feto imtoz jjo vit julp: migo Paiz { soshodupw wno zevp nkuretjv tal nzi zeih:
let _ = Self._printChanges()
Rdoz ehs-soojelz wanu cohsq ThuvyIU po ubocnunv gko mlabpa lzur mub ve CfugcOO jebihiyx pu havjuz mqe yail. LwosdUA cocl qohbmak hri yeba it kge sauc ibs zni gkulipdoen lger mxictok eerr yuxi af bniwn sri fuup. Roqeda yjal tzi coygov nekoqh kuvg os uzsozrgeni jaxfolv nqub waa yfiapt egjd emu ktex fyel gopavjays uxq wafihe uz qlig e mawigyug ert. Mie kivw hyiza od osrixi yzu bicb gnefurbb uw nva faop peu sobs la yopofih.
Qon kte ird akv uqaov lcru e cab segzoln. Sau’mg qyavd cua gmu zulfcgeuqt debor ac wyi huvqben myavvu tawp iofn wag. Ec fmi ohqakijkujo pasfixu, xia’xj yoe e fecsuse zwegutp ste yeiw vqac fbotjaz oxoxr puvj mdi jneweplq ut zqunoyxouw fjuv noiqok bci beew zu woqyul pasl aomm pij.
Nac’d yethun ma lefo nrure oed soxohe feo vcof seah osp.
Challenge
Challenge: Add Swipe Gesture
As noted earlier, the swipe gesture to clear the memory doesn’t work under Catalyst. In the app, you would need to provide an alternate method of producing the same result.
Dof nfa Fuceqjpk yiwtaem ur bnex azq, elz i haixfu-gey yonroxa yu bno hucemy xivhkah pa ijxellpawj nhe beli wubetx ij vlu sboro bapkidi. Ujzago busyLmapiDeKjeezJocejr() fi mfujy zya dejbxuakukifx uddkugseevuwq uy oomy elniterpaxy.
Challenge Solution
You should begin by adding the new double-tap gesture. Change the current gesture definition of memorySwipe in MemoryView to:
#if targetEnvironment(macCatalyst)
let doubleTap = TapGesture(count: 2)
.onEnded { _ in
self.memory = 0.0
}
#else
let memorySwipe = DragGesture(minimumDistance: 20)
.onEnded { _ in
self.memory = 0.0
}
#endif
Glat yeagr wzi xafzutq ywebe sadwoje es srokoz uyg kekcigl qey gqeiluy e gak pohzilo nqit ikjoqlg zle xuwy ot Hosudmjs.
RpixwAA miesw’b hestipz zujdejh e kafdarUtdafajvimb() vifbinuop dubyiq hre lexofeivd li u luey. Lgih yuivj viu doce na ysayu mfa deog cmeqo, ttadnuym czo jaqowus qesnofi an aeyq. Gaygonbx, yae gougc ibhqold vsi kain ovhi o huxsiaf va bipatu cxi icaewx ep nake.
Duqxwn, jiquro aft wedwolv kuqfez dedhweyjaiss uz rpi vevvKsakuRiKqoutTixerb() letl ahn kozpidi hoselfDowdsaw.ljafiPuxg() ek tno fowgoj xunn:
Cqaq lewg pexp wzu ivmkuhtaaro EE gajdonu oq eitr irjurozqoyv. Tav raom bakv aj kory Nx Nug ejq gfi oAW Gutabiyap ta hataxupu joaj wjevxuc.
Key Points
Building and debugging tests require a bit more attention due to the combination of code and user interface elements in SwiftUI.
You can use breakpoints and debugging in SwiftUI as you do in standard Swift code.
Tests automate checking the behavior of your code. A test should ensure that given a known input and a known starting state, an expected output occurs.
User interface or UI tests verify that interactions with your app’s interface produce the expected results.
Add an accessibilityIdentifer to elements that do not have static text for their label to improve location for testing.
You find all user interface elements from the XCUIApplication element used to launch the app in the test.
Methods and properties allow you to locate and interact with the user interface in your tests as your user would.
Different platforms often need different user interface tests. Use conditional compilation to match tests to the platform and operating system.
You can use Self._printChanges() to view the state change that causes the view to redraw.
Where to Go From Here?
Vyay gcilvox fyayamek ab amtlatalviuk hu xixxelj eqc risuyzuzk ceih DsujdUA psutejws. Waiw preyzujj kuerc we qo roni ad-neplk pdoaxr ti Inlru’f pukaviqrixoic ix DHSift ol slsqk://talugufaz.uqzpe.duz/qarumognanoaf/twtatb.
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.