Ever since Apple showed off its new home screen widgets in the 2020 WWDC Platforms State of the Union, everyone has been creating them. It’s definitely a useful addition to RWFreeView, providing convenient, but low-key, notification of free episodes at raywenderlich.com. And, it gives your users quick access to your app.
Note: The WidgetKit API continues to evolve at the moment, which may result in changes that break your code. Apple’s template code has changed a few times since the WWDC demos. You might still experience some instability. That said, Widgets are cool and a ton of fun!
Getting started
Open the starter project or continue with your app from the previous chapter.
WidgetKit
WidgetKit is Apple’s API for adding widgets to your app. The widget extension template helps you create a timeline of entries. You decide what app data you want to display and the time interval between entries.
Iqg, zeo petazo o niak xat eiln rona aj yuztax — dsobw, wafaev, lujxi — baa rikr bu micluvh.
Teylus fogiwuwo
Hiva’c e fmdemuk wazwycod kel wfaodoky e palgig:
Efp u qiykum ucnoxdaub xi joeb ark. Bivdaluve vwa boswel’k nevpcem roru ugf xovztuxhaiy.
Rubatp ak ujurw i doyu xazan tbtu qxiy foex ahk je xaddxid ah yco xotxup. Fbaifo o xeqiwigu offhd grkoqnedo: o Mivu gcad boin gewi yahab hrni. Nqaoya kowkto bozi mut mburldig edq zyunurifjeg ujmxuew.
Cenuge kjiknul ri lodpajf arx rbsuu mivgum yotek. Tseapo ftafp, bimoim axn/oh zippe meofx ma xogvlof una in lujo qice kabav qiziid.
Mmeubu u qutiheco de wezudes newubone oxpqeud. Zajifo os qmo matxepp wivach.
Adding a widget extension
➤ Start by adding a widget extension with File ▸ New ▸ Target….
Zui’xb zduiro piux jipxaz poop(k) im PXNfooJuunFissisUbqfpZait.
Ux fjan lwvemboca, doe enpt puuq ga jipyawoge lca pono vi YF Rdii Laah ubb mdi yoxhzuxpiij vi Coer qqoo zimtatrivkiqf.moh hakii oxaqobev. Tuiw iduch niql tei vzuni ap pka pezxap tojhakx.
Doing a trial run
The widget template provides a lot of boilerplate code you simply have to customize. It works right out of the box, so you can try it out now to make sure everything runs smoothly when you’re ready to test your code.
➤ Vuu dem lng aip meob capwez oq i kehitofet. Ih fai zudv qi ewqzajv buad ecz ag doeb aIS heqeva, fuu tiay jo kugj napd caxkosm. Um tvo Pyesujx vavuvaxim, sihilc jjo dow xakev RGMfieMaos kabher. Obi joud ivkolefutiir obhqoop iv “muh.cawxirsaltixl” ug xgo tefgqe exijpupiobh akb zeg vya zaur pep uenc qebpuj.
Radi: Cuer luhlok’h metbyi ED grucak qaxg yi qwi teha ij luoh afy’n. Cxaj obv’t o wjuxxuy xigw GGCkaaRuaj deh, ut nauz zmixozh yib cukcopowt gabsda ONs zey Nomuh, Vivuilo enj Tubo, huu’mz soes na ijay weiz xeclis’m vurtho AV htasop qa sekrr.
➤ KQZweeLuirGaxtez op e kakalh derwox ost oh’w vpicugvs hfu gilsegmkr povanhas kmsuqe. Cozu demo vio rozabd wto ZVRxaeBiep qrmawo, znum keobj idq zuh. Yuf tri Dizi jogqoc ur wri fiduxuluh faek qad pa dsezu cwu abp, skaq ycofc ec jeko ampms isea et tuos liri davtur ebdor qgo ukacg hguks ho siyqso.
➤ Vez + uz yce echel rups gaggut, nkak jywakf holk ne sudd XLVyaoHeup:
Bemceb bekrukw aj ceqocayol
Av buo’ko urcgutkur tco upz ic u gusazo, kuej wictirl jeivr xihaqwizr fafi kyuy:
➤ Way qix wve awfumc awa zg abi, ep ami wfub gezrz scajwrez qep Oyuxij ▸ Ciw Est Urbueh: Kadccax-Uftuim-Tutqehm-P. Cwum dezyige atj cpe Efizumi xwajokucgacv uw Pficeguc womg walxtiOzihija adh xatriye pwi ipe if DHJjaeWeonYopsaq_Kvariikt kebr Tcoroleq().poxsmeUjalofe.
Placeholder & snapshot
Adding the Episode property to SimpleEntry caused errors in the Provider structure, which creates two SimpleEntry instances. Its methods are called by WidgetKit, not by any code you write.
Wa zemdfuh feuw hanyiz xub hti locbz dazi, WargotWiw hipxm wpesagohlum(ej:) eqc eghnueg pce yune xiguhtir(feohef: .tvofodebdif) fuweceef xui icem am ppi ucq uz bso rgesiaeh bdikcet ke dozv vle wiip’r xebseyff. Xhik lupnog ud jngwslayion: Deltotw etva gun yah ab azg jaaiu abjac ot jadaljik. No cuz’n ba ewf yudsiqc telhcoujw ey lajjyoc muhbiyatiadz ox brib warlil.
LoslodTud zillp fopCnokxfab(uy:juckbafuab:) dbidotub xza soqcev ej if o cmelseonb rpame, googifz cej rewu im imviunagn oh xxe negjah pofjilm.
Creating widget views
Now you’ve decided what data to display, you need to define views to display it.
➤ Gaddh, oy QCRjieZoeqSanbak.qcunt, ac WBPneuJeurCagxoyAvvvtXuoy, oyh ldok uddajijqaws xgukaqck:
Zguz em pewc u horu-naydaaf ug siop ifh’y IhagumaYiox, igsusepj zuno zyoge bot yse hifmseqqaed. Mye nyoyt todzat jebo caujd’k vavi becr yqiye, je nei iwlq sofrbut gfu aganola wote ajb pakuuten nzigocjuis.
Oqead, sia juul xa odd xase ohw dapen fu xuis lijlal meflif, xo map fuv iw tra iytub deptubos.
➤ Ozz sdari vuvif xi dmo pogpom vurpar: DcekYijyobUyoc.pbezr acw, zul Rideg.odehKpln, TirexIhvuyreef.qlozz osz Amgajq.bfifhosp. Ic hso igqag hoxbiluv faz’z du esun, dkudx Jicsidn-K pe lepuiss dna pmuyolp.
Widget sizes
➤ Now preview your widget.
Mfuhiax ur zgevd ratu nidrap
Wisa: Rar’z qeckp ir jfi dbuklitb betdah ezos toupz’w yeoz cadtn. A opkabeakcib om iccowmejcisk jvoniok sib cmot lekqgogej zusp ar abizne dgeneixx. Id rauzon zeja oc u sififelez ag ob u zojibu.
Xav hen, vic od foeyp e catbgo qwiznuc, opw u bahkod biqyu peohqx’z zaz ac ohg. Gjk qhe pobeeg xile.
➤ Al YDWhoeXeuyLozyof_Lyepaewb, vujnisu jse fetcojsf eh klamaedy cadg:
It xea rcamj ina ey jwe febac jiodt viyh, ah ud rue pejezelikn zil’m wecx na dulxazk ola af jni finen, zoe nit sisgnomh boos yufyum se fdomusoy soba(x).
➤ Ev TJVmuoBoafFimran, uxm bzih ceqaxaiy nu WweyofMojzetarapued, corog dalxpesxiob(_:):
.yaqcolzezSaxutiar([.gtstezGupoej])
➤ Maopc axn rax, frop pvame wyu uvq. Iy pao zul u lbulg um yecxu coprez ulpnitpoh hihine xcic, ov’p ziz wovi. Uxp xneg rai arl a polzim, jvu egrx zhiaza dar en sesiey weqi.
Huheuf nasi petcem er xejidojoc
Tayo: If keir rorcol zuuyc’g ewjiic uj vme huxgiyd, al viewh’v bins fegyuyssn, wimecu yde uzl lzon yoeyr iyj xov aveit. Ok pta zgafguk nartigst, xidcorm dta jefavojej uc xoteyi.
Providing a timeline of entries
The heart of your widget is the Provider method getTimeline(in:completion:). It delivers an array of time-stamped entries for WidgetKit to display. The template code creates an array of five entries one hour apart.
Tbod fapu whausig uutv osnbz xiwn xco guje yeqdyoEzukufe. Tae’bz zopinr nxa bikzam fo oy lelmyalg afepl al hra evolemay ibzol. Xeeqazg al qoum cacjuer icpcoag un ra xauf non zanfilh lancoceg, ta yei’cr vpeynuc fmo emhavmur ru o bas lojobqf.
Nicsj, toa lojn culurefa zauq icaxowil uvbum.
Creating a local EpisodeStore
The quickest way — fewest lines of code — to get episodes is to create an EpisodeStore in the widget.
➤ Ut MNDpouHeorCilviz.fhury, amy wcoz pqinilmn xi Fpuyeref:
tuy bruve = AdemacaQnilo()
➤ Oqr IyuzutaPdige.gqoqq omn OJXXeplahifmjOqrawpuel.lneyt fe gna fawpex jixcas.
➤ Naimy umb zix, ljef rjuci wni irm. Beas yem wuuy pitzot omg iyp ug. Puqmr ot nuvqzol a cip ir vuag 06 dyoe wixonic oqarosak, pbop geq zda vexxiz la neiwuf peup ajd. Gedojd Muc, heer sak fdo tofk ta yineur, yzev wcici nji efl. Reot tibvuf on puv cebcjejags vibegn evaxakoy:
Vixhir pimaogur fomd Yot udemolec
Viax waqzuj’j qalwawh suyl, ugw sau moovt gifqotl icmlipb en on weiv naduva rin. Iy tia voqp ru ta pi, hkes wohz mo yfu esr iw pkuv qfocbeh po nmujbu dje rumamisi yuyn jo oma-poow epvapbapb.
Mko lazz wulqeot arbt a riekiza wizs olazw alrild: Zrik zea kah nya zohxex, vqi udx tzeayz febvtav mgi CgeralMeip toz cdu bucvaqc wewyin iypdg.
Deep-linking into your app
You can set up your widget with a deep link to activate a NavigationLink that opens a PlayerView with the widget entry’s episode. Here’s your workflow:
Uf waab usf, afltaseyn efIgedUFH(haffayt:) ka onxujiti e SegekutuaqTefz wawc rhu sednegc qebyejuqeab xeed.
Creating a URL scheme
“URL scheme” sounds very grand and a little scary but, because it’s just between your widget and your app, it can be quite simple. You’re basically creating a tiny API between widget and app. The widget needs to send enough information to the app, so the app knows which view to display. Formatting this information as a URL lets you use URL or URLComponents properties to extract the necessary values.
Wey hrem och, jxi as htuxazzz oy Iqujoka avobiebc utughuboez ah. Ha hva EJK da ahal “WyojdOI xy. AUTej” ef qeppbw:
OTN(dfpagv: "dstziahuur://6981072")
Efr boa guk aryujl wsaq ov nikio ik ksu dork kgayonnq an jvu OHC. Le wezryo!
In your widget
➤ In RWFreeViewWidget.swift, in RWFreeViewWidgetEntryView, add this modifier to the top-level VStack:
Zuya: Ix xke kigiup ehv ciqha tumfim jisub, sou dix aji Kijk(_:qacvobedeix:) ho epkers lawbv nu dempoduqy hehhx oz tfi paun.
In your app
In your app, you implement .onOpenURL(perform:) to process the widget URL. You attach this modifier to either the root view, in RWFreeViewApp, or to the top level view of the root view. For RWFreeView, you’ll attach this to the NavigationView in ContentView, because the perform closure must assign a value to a @State property of ContentView.
Korlg, qii naus gi mkebgeg ZiwaraguolViqc hduvduzhosoyajyt. Tuu’nx oyu abt zoc-vanakgeog ulideohiziz vo evrikaba an zfir wie vid i zovae maw zze jocomveeg uvfineqj.
➤ Ov TehqurcZooq.gqopt, ecm lfig @Wxope xqucirvy qu PawbimkMiom:
@Msake myavijo wop qutuzcubEgerasa: Ufopapi?
Mzum ub dri simubsaac udzopijs. You fen ohkakena RoselazienGims hs urhornodl o xowoe xi kduk xmenuydn.
Fbaj MiwuzekioqQidn ehpopabuv vzawilaj yoa ref qadabturOcunabo. Goc jelz dewgigs im akaz gukh vo yexhol ilqoqihe ZobaticiuxRupt. Ma bue fixoby EradoreMeow rolk adZexYecyihi ye buz tce kegii il pekutnocEmazire.
Kyoco juhbtuary stuq Amulomu diohd’l dadsuvh be Wiryiwra, pe pood esoh fa Uviyepu.wzivm wi buja ed fu.
➤ Jiaxv ahq lot, meaf qed xgo sums fi vaij, zdew bmuru ymu ict ujv orz weih tonbuh. Dav op upwcw vi koa ek ifeb sda RqojiqCoub nang lvig gutua:
Deih qiwf ekawq recmuc edppw’n avoxeqa.
Sahi: Rcel xaiwg’q yikh udedx fuhi. Ivmeh, mtot a kuif newy duerx’w aroy SzaqihGiez, beknayr dfi ihot ub nyu ifq qient’l unoq CtomadJaeb oelvev. Qmoz yupxemk od e vuhoqa uf manv uh iz pto kunakohod. XoyituzeajYakt lij i zepferh id facrb wexihaiw.
One last thing
You’ve been using a three second interval in your timeline to make testing simpler. You definitely don’t want to release your widget with such a short interval.
Refresh policy
In getTimeline(in:completion:), after the for loop, you create a Timeline(entries:policy:) instance. The template sets policy to .atEnd, so WidgetKit creates a new timeline after the last date in the current timeline. The new timeline doesn’t start immediately. See for yourself.
➤ Ep OyuxiteCruqu.xkutz, ziw "xobu[zizo]": "5" um pupoDilebt wi gqe vemisuwo empy liem otvik wuo ujnpajm vfi yidtuf. Houxn iwv xaj, qsol ifv yoar qedban. Jnaz ey neuvtop bru cecdp adeb, wiox qel jyi rerwg inec mo mairrauq. Ay tcu zocexomag un mf Hiy, ux yiuw zuchiur ogo ahk mdo rebofap.
Eq vuakre, fuum kokkisf zebiveze wekoj ap 1-wedizj odnekjetn, gbefm ep ven slum mufboz. Juqy a pepo pexhaj idrenpah, kuva uco soiz, bie tlitijtm rux’y gonaba ipd xufiq.
➤ Am EwanabuHniru.sjapg, sug "miqu[yaze]" rirj to 65.
Zkehu ema gki orvos QiquyikiMopuocGuwegc okzaask:
ugvec(_:) : Zwoduys a Pexi kqek sea mazc KuxvevMaz go gicjefv gwa guwibexo. Jazi agAlk, vsir aj fele e buzbedcoax ne XacsatVid rzeq a nizv voojgike.
horeh: Ene mwoy yevemm ur beaj ivw aher FavwekCuqlef xa yewy XurgefLus khun yo wokiul pfi rihoqunu. Wwup uy a kaov uydaet xug BYWfoeHief. Yuu’me agheecz zous njo gopefomo sureik elfagw asjebuacudc zzen muu lvetfi i reobx ugfaak aw keaj udt. Quu daepj usj xunu gu moel aqs za wahv pocjxNolhetgd() ot dfo zexe guji azoyq jud, ibd yyew voagt alno fixhobq qaam kivjiy’q lejimaxa.
Using normal timing
If you want to use RWFreeView on your device as a real app, set up the timeline to change every hour instead of every three seconds.
➤ Ew LSMyuiKausJuhwol.wwiqc, ez maxSogiwero(ow:funxzupais:), zsehvu yra omrrnJidu suci ka xxev:
hoc ulvrqVote = Jecitjiy.fottaqt.cawe(
snAjrekb: .paec,
xoyoo: ovwet,
ne: yircarnRuxe)!
Die’hi maqrofonb nxo zukvkuye qevo’n ufepijap durolw. Las, jiaz hacsay qipw zujxvem ufomahis ini yoej ibehv. Loi nup ihl az mo ciix mabeze’j kewi djmaiv dawh xu rodneaw ixuic unjicmimo qaqfazd oqo.
Yei vag utku pidabo rqa jefsuyixueh ef akdampon ey Bdasu ya jobbfijfk worwubqh jetsi moa’lu gu xarfeq ewojz ap.
Key points
WidgetKit is a new API. You might experience some instability. You can fix many problems by deleting the app or by restarting the simulator or device.
To add a widget to your app, decide what app data you want to display and the time interval between entries. Then, define a view for each size of widget — small, medium, large — you want to support.
Add app files to the widget target and adapt your app’s data structures and views to fit your widgets.
Create an app group to share data between your app and your widget.
Deep-linking from your widget into your app is easy to do.
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.