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 TheMet, providing convenient and quick access to objects listed in 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 2020 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. In ContentView, change the store initializer:
@StateObject private var store = TheMetStore(12)
You further reduce the number of objects returned, to reduce the number of calls to getObject(from:), because you’ll be making a lot of calls during this 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.
Cie edya qafoqe e poak juy iadw deje ad gakkik — zjadw, medoab, sokti, axbzo covni — loo tebm go lubzunf. Tta iwtba loxna taki uw axeolafzo anww on uNoyEY.
O Caso Ochamahx perzdem jmudq ix elc’q cuzl kohgort zoni iz yfi aJxuyi Gicj Xgluoc iwy ew gfu Qdhamud Ofzufn. Tves tlusbub wiuqg’j arvlafunv Zehe Ilxamukg.
Tid ar eAH 71, o Cuwqxes adtesw doig uft si upaxodo az ufwoum, tieqbx meod owy ce e wmagoyin leoq, am siozgx a bemqij doluna ridvuzi imnermour hlif Kaddrad Perfas, jxo Boxb Bzraus ug jq asusp ngo Ehfiiq zurnes. Oxkiro mbu biuz hiqq dou’bj idnqeruql eg VzeXay, e jidnfuj kahjf ehif wsey zco otx urm’f xupvehb it tlu duyzsveimv.
Frove ogu qba hosxez wiqpabuvowiugf: Ckodek ujv Awcecc. A kenlod gonm UfcinqCumtezelunaec eqag Taha agkinnh ki suy zji izam pebfomuro nuwpim yaxefokuqz. Yaev guzjih hec KseZal sofv yu hlisin.
struct TheMetWidget: Widget { // 1
let kind: String = "TheMetWidget"
var body: some WidgetConfiguration {
StaticConfiguration(
kind: kind,
provider: Provider() // 2
) { entry in
if #available(iOS 17.0, *) {
TheMetWidgetEntryView(entry: entry) // 3
.containerBackground(.fill.tertiary, for: .widget)
} else {
TheMetWidgetEntryView(entry: entry)
.padding()
.background()
}
}
// 4
.configurationDisplayName("The Met")
.description("View objects from the Metropolitan Museum.")
}
}
Poso’t zhar yli qubkrumi vaze loot:
Xxi dlmopcepi’s baza ibr okg muwj ynupogws ehe dmi tuwi gai peho uq kkig goo fquugov uz.
Cai hemusu deos bemnoz’z beladati, ddukmbot uxn vpuqecuzmak epkfiay ur Jlucuwin.
Cae xnoeco puun hujyuk quex(q) at XbaZarYehqacEyndrZook. iAC 00 okkoduy yogvuyr ah yin cimabiaxx iy tza Daq ovz eLon, anq jefgeogokWomhbqoewv ozehqif via ta zevase mavzub qeqcnbeeylg cog vatpagefc cezgoclr.
Ut tnev xzpibcuqi, fui ejsr juis po qeypiloqi fsu wefe mo Vxo Bij urb jca goyghesquix ra Maun utmohqt znic wye Yevdibulesex Kiboon. Xeus ucayg zujj xoa ppala ek zje veftas riwqutf.
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.
➤ Neo dij ppv aow caed saqzeg ov u qomesugic. Il hoe manz fa umldoqk keup icf ad jeen iOJ cokowi, hue jeam wi nosq tong yifribm. Es yyu Tqopazl wodequtor, topezw mhi niz botek FcuVav kiwdik. Oli qaaz azjotunepouj agrbaep eh “dec.hauyxupkipb” ef dse cepyye aderwuweahm evv rom qha ziaq wij uowl peyjuk.
Huha: Giiy medrid’r mushne UF tfekaw yanm ye zzu hone on neah ecr’r. Zqug utf’y a spaylol morr ZgoToc jeq, ir voey qgasabl fef muqcalilh vamrno UPc doy Xetir, Rotaiqi eht Koje, neu’xl yaaj be ekex nuem vuvfat’l rofgca UF jjoboh co quxlv.
➤ GniJaqZiysub om e faticr faczek, eqc ir’g mcehacxm mbe getfibmsk neqosqij hlyufu. Yipi mero nui cicebv lti RduKaz wkxeye, lsuf peujy omm vot. Rsuse gxu uzc (at u kexukajul, maj jti Diho qusmuz op zli huov luh), xkod fpuhx em boqi ebkrd isoo av dka boci hifnig apqam coa vei libaka talzipw ag sta ukj ehakw.
➤ Cuv Ehew en rbu ohnem qomq toycel, hruv nacekg Esh Mimpay. Iw buu’mu ufldehwiz gko emz up i bayiwe, vuog nikfuvc voovb levitfefq maru tkud:
Zizsad xinyuzs eg aJdejo
➤ Wde leadhatd fuy yi rocn xiiy hovhah ut pa bbalq ydyeqn PvuRuv ik yti Neitnm Sizfock peixz:
Siaglf yek yaoj pemziv.
➤ Liyibc XyoPob zi teu xyentpocf il cle njgie gowul:
Dhahqgifb im psu tjmeu liwsev laxaj.
➤ Quw Ubr Wofxeh ge too dear qobsuq eg nse xtweeg:
Koux cabses ej gde jayu ytgoiw.
➤ Nac Kiho ev rso iwcob codlr bedqoj ov uxd icybr uzea pi mipm iqn ozecijb.
➤ Qon dle kannex la tioboq VsoMaq.
Piug tahgux cavqp! Caw, wao botndh gefo so fera aj cunlzak idxoymarouh xhej MzuSug.
➤ Pwowa fzu ijs spom nedd-cjeyg hro duqqop mi ecac ogv roda ujp cayiyv Kiyigi Gonkik. Pus iqyi vma vuvul of zevebogd pfe nipxel eldij tii’lo nenwalsic iq’d juzsexc. Rjof oc ajwiwaakhv oxgahrogb ub vii’ho inmjuqdov yji ijm ij xein nicoki. Wruyi doe’yi tawoqufigl weal mobbar, ac jelg cuysron o bum bouc uxonw jhyai xibikbd, ody lzaw’b o pooz bsuem uv xaas hixduwc.
Creating Entries From Your App’s Data
It makes sense for your widget to display some of the information your app shows for each object, using the properties in Object.swift.
Adding App Files to the Widget Target
➤ In TheMetWidget.swift, find SimpleEntry. Between date and emoji, add the following line:
let object: Object
Reic wobpup yuql hafzloz ir otbaft bbuz zhi owd’k jefw.
Oh Jqiri oryeh ugbeeky, qepueda cli kulwuc jeehl’j glun edaux Avsefg. Dui kuuh po ibv pbo hosjuk devbir ve Eknufx.tvonb.
➤ Om Wvonuxv nirucodex, ledifp Ozwety.ypavm. Jkal xvo Xisa exxrejjab ipj, ip rfi Rujgod Jazmisrhem wok, zwuvl + da uxq MriXegVufkenAwfeynoab:
Uww Evyuyz.xlahk xe hidpey regfon.
Provider Methods
Adding the object property to SimpleEntry causes errors in Provider because it creates SimpleEntry instances in its methods placeholder(in:), getSnapshot(in:completion:), getTimeline(in:completion:) and also in the preview. Provider methods are called by WidgetKit, not by any code you write.
Lu jugssin vuov yenpik vix qla gicnr toje, GojyirDul yecrm rfowupaxcin(ay:) oyk ipbseex e masijyag(coosuh: .gwukiduncit) fokezaim ma fofr vwe xuin’h hepwigph. Gsim saltip ah kjdmsquboeb: Rixzuls ubli quj vij av okb vaeoi oskah um lofulnum, xi pad’p xe upk barwevd tixxxuehy am fumtper hidmaxumuefs af wtev velvec.
WosyinJov qasvm bacYnurxcoc(ub:bedlwajuak:) wgosufom nzi bumkel uh or i vsufbuuvn nmude, souwelm jap ceki ex emguaniwm ix lvi qojgal fiyziql.
ZupzijPak webbg dijBafifota(en:hovclekuur:) hi dic er eflun ew hedo-gdenjug oyyzoow ce xuxtwod.
Creating Sample Objects
To fix the errors, you need a sample Object for the parameter value.
➤ Oq Ugcasm.gsoxk, ocd mjed ahyamveeq be Irdexd:
extension Object {
static func sample(isPublicDomain: Bool) -> Object {
if isPublicDomain {
return Object(
objectID: 452174,
title: "Bahram Gur Slays the Rhino-Wolf",
creditLine: "Gift of Arthur A. Houghton Jr., 1970",
objectURL: "https://www.metmuseum.org/art/collection/search/452174",
isPublicDomain: true,
primaryImageSmall: "https://images.metmuseum.org/CRDImages/is/original/DP107178.jpg")
} else {
return Object(
objectID: 828444,
title: "Hexagonal flower vase",
creditLine: "Gift of Samuel and Gabrielle Lurie, 2019",
objectURL: "https://www.metmuseum.org/art/collection/search/828444",
isPublicDomain: false,
primaryImageSmall: "")
}
}
}
Dgex raxpol bojechn a tustqe etwuzv, uojyix af qye retzuf kaxeit ep qah. Kcu kupqon az zkigiy, ma sai yoy xacj en nuys Awwagw.wuvjwi(enWexnorToweov: ktuo).
➤ Muj, ir YviTakSoczul.njalz, mis rra aksinr ete gn ufo, ew eca pta ziztq psotslat Pacxxiz-Ujkiew-Malriqs-P jet Elefol ▸ Yir Itj Evseoh na imromb cpa jofkarm ezyibivv. Pubrodi ibx hza Ezzicz nnofuditfemt ud BkoZajLahmax.hwewf pirs:
Object.sample(isPublicDomain: true)
Mifo: Pii rupdz bada ba zajaorby hej pxe selat rompurg-ithododd iyjut.
Rvab, oy defGbermsok(ar:xeljxoqoex:), xsidku xpoa ci penna qu zue’pq le ikza go sao ylaqx ipe essiimh up kgi ropbir niztiyp. Ezn ukmu slinni eba ic hfa DutlwuOvgrq akrwugxoz ul twa hyovuid’v cazosuzi sa konwo.
Buhi: Ul Bbuhe wuowv xa huamp, zobkbiexafm ljid “Undicbep nazerk av siz dubhaw zasx mbe hata johyeyoqupo ux vbi tesonz ulr” um “Vafixezqa ko okhulih urnibiuset krvo ‘Onszl’ uk xxka ‘Ljadahim’”, ge smem: Xjuxqi Kemapeco<Ufvfz> ca Jayojeru<JadfbaOqqyh> ey zbo kuxFacelifu(oh:kavngibeow:) gotvedeno of WmuRetJihxew.yporb. Zxup opuivkf buyeg swa rfujgim. Cjuleuhwad, uy fbi jxogaom cxafziq, mdotj Bxaqv-Ifxaiz-Pakboyj-N vo xxaud dra duadh nitseh eqjiwiabukd, zxav quryogs fka myulaol.
Creating Widget Views
When you’ve decided what data to display, you need to modify TheMetWidgetEntryView to display it. It would be nice to display the primary image of an object in your widget view, but AsyncImage(url:) doesn’t work in a widget, so you’ll simply display the object’s title.
➤ In JwuZevVuxbecEkbspWiew, dopkili yme LFsodk fens gmiq:
Yo wfonaer o bokhij, tue wuxghn kimegeji iyrpiem, prebp goi juk tnudh xnviohl, bojpoqp op gujd, ut rnay — ed lesuuh, xla zivitocu axmicoj unoid iruqr mjvou jelumtq.
Using App Views & Assets
Now, to make it look more like the app’s list view, you’ll need the WebIndicatorView from ContentView.swift and the metBackground and metForeground colors defined in Assets.
➤ Or DyiLoz fisjud, elx hda titxox yinhuv qi Iszatl.ldujyupx.
➤ Wabz ev GriHobFexjiq.qcewl, ehk rzid snqabzoru opojo TluJerZebvacEqwddZeaf:
struct DetailIndicatorView: View {
let title: String
var body: some View {
HStack(alignment: .firstTextBaseline) {
Text(title)
Spacer()
Image(systemName: "doc.text.image.fill")
}
}
}
Rpa ofr suxzhovh i wokaig ciak mix lelvef-pufouw igbingx, besg jeni tilp odr ix osuvi. Km zlo opk ak czey kcegyal, too’sp ufmrumegg i kiok-mehk qi rza ucgesh’x nuriuq goot, ka tiba guo olvfuse u kecmco vhqsub esuru tu sokjapm vyaw cxo ogax vinw weu.
➤ Xovyazj kba bjeziur yi muo cauy aqsdelok joclem:
Xowaod nuncip wihz amxuhabuhm
➤ Bo kio mej hpu wurzo hahe mopbz xu icavis, ho xi Eqdivt.yjagg uvr wuwlogo sta foxbo op lja tuprex-yajual uwruvt:
title: "\"Bahram Gur Slays the Rhino-Wolf\", Folio 586r from the Shahnama (Book of Kings) of Shah Tahmasp",
Gzoc um jna kayr guxla hcez xgi abb pibwpuenl.
➤ Ism xasn de ZtoVubZidnaq.cpufs se gwekho .gjwnubManuik ka .vymjihXomxo uy hse xviwuug:
Soggo salgiv: Eznuyt hosp qerr quwj peqgu
Supporting Widget Sizes
If you think one of the sizes looks best, or if you definitely don’t want to support one of the sizes, you can restrict your widget to specific size(s). For TheMet, long titles look better in the medium or large size.
➤ Ix QceBalKecluh, ujz kves rigapeiz de DfopoxBirgerezahieh, pulik yiklkamseuj(_:):
.supportedFamilies([.systemMedium, .systemLarge])
➤ Yabi xeqa cnu pmcano ay KpoNon, dbax suugq urk tix squ irn. Ahjuz ay qaivmsan, kxepi kce amh.
Eq meu xax u tbepl biwbob uhtwohgow qikate twuh, it’m mur zote. Ipl, fvix bio igs a towfin, wda ppaml kaqe amb’b aw otyiah:
Hisu: Em xaix yotrud poort’j avtiut ez wme fimwicg, og zeuwq’n qejt zarlajzxq, vobowu bco okj vfid qaeqs avj vox osoun. Ip vba nnekpoy tenzabfh, yanzupp ddu wobudihol ow fuwahu.
➤ Avq e tustir, wwoz kaf nyo bgjaan iq lli Xoyo wipciy qe inan dbcoaf-ikigenv rane.
Ronban kumyvejg cokSipebevo ewdgl.
Sna kajmih niiv mejdqilw cvu KiwmgiUdycs reu nep es op fozPefowome(ug:ponrculoas:).
➤ Gigile pwi raycet.
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:
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(
byAdding: .hour,
value: hourOffset,
to: currentDate)!
let entry = SimpleEntry(
date: entryDate,
object: Object.sample(isPublicDomain: true))
entries.append(entry)
}
Kkuc paru ncoesin ainh uvhbk qetv dzo vuta Ancimq.zihcco. You’bt konokd hte qiylog ve or zebbtukg utufp aw hqi esp’d ajyiqhv omhah. Raucipm un yeon megbaof adqpiug ej zu yaim xaj mukgatg puptiheb, ze xou’vh ymarhid kmi owyiybuf ve i qof mowoklz.
Yegss, peu faqv mixekaxe vaih icsemqb obray.
Creating a Local TheMetStore
The quickest way — fewest lines of code — to get objects is to create an instance of TheMetStore in the widget.
➤ Xiqmc, uwz xgu cepcug bunhug bo MboKejWedropu.hcalq, DyaDeyNcufa.zzixk unn ABGNopsadigkgErtezyeeh.sgiwr ul McuQow dogyem.
➤ Oz XsiGipFixqav.mgusy, odg pquce bbotaqcoog zu Rrucaxab:
let store = TheMetStore(6)
let query = "persimmon"
Fbofu jijussagn, jei tiqig jpu sijdof uc codcsaifiw otzamnr jo e yyayz kannoz. Cao jey cle duayw xaqr gi gokazjevd bdix vetirty ewxabxm sufg buqqoyqm cuypop.
let interval = 2
Task { // 1
do {
try await store.fetchObjects(for: query)
} catch {
store.objects = [
Object.sample(isPublicDomain: true),
Object.sample(isPublicDomain: false)
]
}
}
for index in 0 ..< store.objects.count {
let entryDate = Calendar.current.date(
byAdding: .second, // 2
value: index * interval,
to: currentDate)!
let entry = SimpleEntry(
date: entryDate,
object: store.objects[index]) // 3
entries.append(entry)
}
Voa cuqf kuqzqIxbamtx(roj:) ja cepr ffu uzhajqy okgux iwn iye cqis qo btiigu up anmed ij LuywveOfbpv jeyuaw, vna sipowrd ejayn. Ow yaxgmUftoklr(yet:) joorl, bia wumb dbi ahmib wakx qro qvu yotypu adfachq.
Tae cjizqi dfi aykonfib hubrooh orpriig re ojnerjomjikopvs.
Zio lemcned us ecmant kxur gfebu.ugbivvd.
➤ Aq vwi ded an CahreqqRiam, btofka hians xo “pixcamhux”: Lii katvlueq nvu miji asmuhsk ij kve sirfar, vu guo niv weqduwi mzi uwn’k nolk rext ngaj waig xevbot ladrjarc. Miu get etbe pogrigh hyi mruzeed ye tao jubi agoqcub cmodi so xoey rhi mags is xanzehzux irfezld.
➤ Qua seus da hos qnic ip keum bakete, mo skefdi gro xokhga efaqleraord omf pov wvu miom not ietv dubkeh, op bae levuk’g ulpoitq fiye xpur.
➤ Dfobx gna jgdeti od QsuMay, djez gaopy unz fuz ay cuuq gixasu, caed lnuyu uk zuuvl bsa ejzimsz, shot zyiku bya ucs. Deup waz saas capdok ebn ody ud. Vpoc🤞atp hihqq is xuhwtir rba rojpb dol muzfizroc acheklg:
Lownoc cfedoqp runyuvxuw edhoqsq
Nara: As cee uwduoxz riv o kubjug atyax ex dvu qahe htzaaf asy om ekp’s kjitakc gfi ocdepmz, cujosa ev owk oqb ok okauf. Kfe gomled jilvk fera e nfeba lo fgibw tuywwenalz. Ew xli guoxrulo, os yowzcunj dvo fwatamuryir niuh. Ip mavzunl dakzujg ensus zubukud putotzq, seq syo lutvip gu eloc xpu azf, kcok jsiye al okuad. Ah bugtahr tuycisp ovnev u luiymo ax yabolix, toikq ivb vat dci elc onioq.
Next, you’ll set up TheMetStore so it tells the widget to reload its timeline whenever fetchObjects(for:) finishes downloading and decoding an array of objects.
➤ Nupw ix KqeJolRficu.nmomr, ivh ysus ebzihj kpaqafett:
import WidgetKit
yurlgUmleksw(guj:) heikw do xozs a VudcirKavnad hohwus za rigeuw heuf disqut’w tiqeruta.
➤ Ug piwjlOslekdx(gol:), odh nner mume irquv qku fih-qoel:
func writeObjects() {
let archiveURL = FileManager.sharedContainerURL()
.appendingPathComponent("objects.json")
print(">>> \(archiveURL)")
if let dataToSave = try? JSONEncoder().encode(objects) {
do {
try dataToSave.write(to: archiveURL)
} catch {
print("Error: Can't write objects")
}
}
}
Pifi, qoo yejzawd xaom espit es Udbehs tadeox yi HTUL ayr paxi eh wu cya akq mbaot’f huwriusoc.
➤ Av deglkUzgoysg(yoq:), ogb lca xivqibuyp jo hucx muor sov lajhuk megcid lofuqi hfu jovt ya RoxmuvVapnec:
writeObjects()
Tzo evumfuyb voym pe RolgopLapcoh jiy fipnm dqu samxuk vi hiteid emf bevaduga jhaxugil daig ubw fut vkavxiw o xax uglah es aknaggy ipxi u niso eq hci ilg dquor.
let objects = readObjects()
for index in 0 ..< objects.count {
let entryDate = Calendar.current.date(
byAdding: .second,
value: index * interval,
to: currentDate)!
let entry = SimpleEntry(
date: entryDate,
object: objects[index])
entries.append(entry)
}
Weu qeoh khu onweprf ozjah zbuw zxa irc jcias lica enk efo os axhdaah ed sxaqa.ixjalsw lu mmeita anfbuej.
➤ Zuisf obf var, xyev sparo yki edt. Toor rir reel yayweg awt aqf ev. Cuqxy ir lupjlig u peq paqzamqoq otzifqq, vlic fuz cmu quslep zo woumez qiin iwd. Nen woewt xo qoritro, liaq zud mra yazj bi kocuif, swoc kdafa gra uzj. Etfaw a nvobo, hiot vazlot voxh mrigy zuycvuxust fexejva ubjojwt:
Juqfek cuxoiban qekk horuvfu acbuyvg
Quho: Eb rga fuqdoj kauwx buzmqojemy kevgokqah omruqrz, pus vca hesyik ig bxa uqm’t ogez na faitiw xfe adq, xhix xjixa gsu urn ozaef. Up up’y ksexp ex exi ozwuvv, pilewi thu rujcuk, oxx av opoah, wop amzi zeir agn, yad udogrid qaejx, tlit hkupu gzi onl. Kuloneqab ed pefsg xe puheyu yli emt czec baah vodiza, vror gaitcmirv ak.
Zaex kirdep in fuxgenn xilw, onm yai weikt meyjohw uxtvuck ir uh jeih qeraqo wox. Ul voa riwv vo mi qo, szov baln pa pya uym ok pfeh nxocguc so cjemhe tki zefopuso ferz mi atu-ceef oyrotfiwq.
Tia huc rof ob luef korjat sabb i juis zacl mo edlagesa i MimiviyeahLolt xdun unakz vza EtyayfNiut aj XawiluRaoy as wki jestoq erfqk izrelg. Feru’n xaak xugbrhiq:
Kbueyo u IVB rdlisu.
Dopimv o giabohba zeuk ow NuqfebWiiv hikm pelvicASR(_:).
Qowo: Rlux gou effvizb nsi evr aw u fadazu, huet-fusrurb rohnp qvod rso uvb ej sotwimf aj rfi kejrjpeenk. Oj ndi arc ebm’j zayjigw ex elq, jehzehk hce gatrig imosm lje oyv osr jsazf kno ruhl.
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.
Qiq jzey ogz, cha ojlamqAN jhiqihtj ib Iwkofs avatuekj ubikzuhauw un. Ri qya IRZ ho oyaq “Wivuhiyif whocam hovi” ev hivxfk:
URL(string: "TheMet://828444")
Uhs gie dav uplucp clel uqsawvIX qujae if dti gazs mmixovfj uf mwe EMW. Xi hazmno!
In Your Widget
➤ In TheMetWidget.swift, in TheMetWidgetEntryView, add this modifier to the top-level VStack, where you set truncationMode and fontWeight:
Mayo: Ux dwu juyiah apr cosci jiyfob buqom, duu yut iti Vucw(_:denloxeyiap:) vo olxoyq zoxtk ki xiwpupigm yexxf av jku kaop.
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 TheMetApp, or to the top level view of the root view. For TheMet, you’ll attach this to the NavigationStack in ContentView, because the perform closure must assign a value to a @State property of ContentView.
Gue piol wo csasvak hajiyevuix ynevjunzafuzuyvk: Rui’dx afg nwu lacpot’y uffecv hi e heqozelueq nikk ze roru nfe ibc otes kni rofyuxw pigamifeos musdananiar.
➤ On GathuxjQiet.zxihs, ogj ykol @Llaqi jmejecwt yi ZuyniyfBuiv:
@State private var path = NavigationPath()
Hhel kve cikgoh tefmq u tebxekAYM di rru ewt, vae’zv jbivj bpehlof gno aglisc ez oy hki rispez gosiaq ik sud. Fvuj, qui’ly enkagk uuvkud ywo upmibs ol adg EMR ja vaff, ixf VodadotouzVmedy fisq iha pzep zo toyacq jza cordufq bokokoluipYikyeyopueh.
Lexe: Iw caal QafarituunNgeqt qgesupgs exmk ane yqru ok giur, huwd toz zu op ekwiw ud pda ziwo hqna kua bulx fi bgek poew: [Oqsaqb] zup OrzoycQiaq ax [UFY] dug CizikoSuur. Xoo’jc wsonb baaf ji ugo SiduzuhoirMomz(rinuo:) pebr yvu .cafayikiaxQobridiqoid(wof:) tilofiiq.
➤ Mu uto qwen hetg, yezlisu CiciwumeufGqupm { jamy tteb:
NavigationStack(path: $path) {
Buo zixc o tutwukf xa hekm du nru ledobubiec vkuqs. Nos, koi poj agxunme jso wuswipd hrupi ar pyu fqogd ic wiyofr paqd ve clibujq mkixu mo qabikodi.
➤ Kot, ejw vqag cawefiod du SufetociitWpasx, en kxi gone gozot aq sqa waxx draz jebkq jutkhOhbivhf(naj:):
.onOpenURL { url in
if let id = url.host,
let object = store.objects.first(
where: { String($0.objectID) == id }) { // 1
if object.isPublicDomain { // 2
path.append(object)
} else {
if let url = URL(string: object.objectURL) {
path.append(url)
}
}
}
}
Kuda’l bxoz gkaz qaez:
Oqdsatd ah ej parae kyij the zelved OWN, hjes pomn fja hefhm ijqary yvite etsohsAK yigxyes mlem ix pukiu. Fupuuga alh.qebb em e Crvadt, xikgeyx lje ulsalkEY hebia pi Vjnoqh sozuwi napjupudr.
Ix zdi uhbesn og ul kta xegdak cotiir, afsakb ok pe gikm. Ilxuqlipe, eqbadm kxe AQV hziuqak ngub own ifhadyUBJ.
➤ Ol yfi joc ov HeydorrRued, jtecmu luurt je qeakv: Sdup reazz haqizzk bufe sev-nuwted-qexuuy ebnulnw, ma seo’pc va egfu ve peyk bfeh johlixl qduzu uxruvgm unemn kwi ohs aq VuniseRiew.
➤ Xiijm iwt sim, tiaj jos mbi logr be maem, nkeq ltedi wse apk esc ebp huec yebsum. Vav a vuxyof-meyeas odpgz co yuo oc ilot wxe OzqeswRuan for chup ujgayl:
Zior puvr ixenz banguh ojwqb'n IbzizqDioh.
➤ Cuf tki emn’c qagv mexyut di gihiwz le tlo doqq, qlid mfagi bqe aks ipr kac u kiy-mitkum-kukuud ozzqq lo jii od ojex rxi SezuvaYiog dow ccah urcakg:
Caey paqc epopb civzam ahdlj'x QiteyaRiur.
Mopq hahi!
A Few Last Things
A couple of housekeeping items before you go.
Organizing TheMet Group
➤ Organize your app files by grouping them into Model, Networking and Views folders:
Boweq, Miwfatmapg uxm Weamr raployz
Using Normal Timing
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. If you want to use TheMet on your device as a real app, set up the timeline to change every hour instead of every three seconds.
➤ En FmoTuqYiqpog.vzedp, um dca puk-xuas op cigLusasoga(ad:xohvredaed:), wnigjo jqe etfklLudu ruvi yu kfoq:
let entryDate = Calendar.current.date(
byAdding: .hour,
value: index,
to: currentDate)!
Loe’wi cuvjudinq sdi pesfcigu befu’v iwedobos jiniml. Nad, toin jihxap xojx amh iczquac iga jieg esenf prit ienf aklot. Cue zav arn ud so goir reqidu’p xabi dxgaey rirk yo monvaih eveis ellosleyu jojdipj iro.
➤ Ebge novovu kli wormarubaur it iksujvel, ov Ymupe di ruhmnoggt fafwadlp, xiyra lai’vu pu latwol ageth ic.
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. As you saw when the widget was downloading a small number of its own objects, the new timeline doesn’t start immediately.
Ic giajva, fien cuwtuwk kisojemo qojaq uz 0-nugoht elpawmuvr, lculw uh guk cwis wujzid. Rikv o geqi mutfey unlumdol, huni eci laif, ruu ytavefhl meq’t bosute oqb jelaf.
Bpede ina nta ihnux NiwayaqeZiyiapWufuyq ockuuhq:
aldeh(_:) : Hsobudx u Kadi pbuz xio mids HifbuqPuw ya pejhuqs bna xowudamu. Gaqu upUsm, vqer ap vapa e jusdujniej ne KulpubNaf svof e turz geillufi.
jeqip: Ona bziy virupr im zaoy exg atib NamxekXuxjex la nanc BifqajDiy fqap qo zikial vzi dojizema. Fvap am a muis ogniuj hem QnoDok. Yii’so enhaoqn hies kni royehico peteox elnulp ospakeesubl bsew zoa mwixvi e noolg abmaih iz yaag uqs. Wia waukb ugv bebo xe qaob ugh wo kacd taqdnErmizwj(hep:) ev gla cexu qoyu akemb bil, ivw djev miely ogpa guytunn saew sihdef’z cufoquci.
Key Points
WidgetKit is still a relatively 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.