Now that your complications are available to place on the watch face, you need to address one last consideration. How do you ensure that the displayed data is up to date?
You learned how to reload the timeline when new data becomes available. Now it’s time to learn different ways to retrieve that data. There are four options available to you:
Update based on changes while the app is active.
Schedule background tasks to make changes.
Schedule background URLSession downloads.
Send notifications via PushKit.
Since you’ve already learned how to reload a timeline, this chapter will address the latter three techniques.
Scheduled background tasks
There will be times when you know an update should take place in the future, but the watch likely won’t be running your app during that time. Calling scheduleBackgroundRefresh(withPreferredDate:userInfo:scheduledCompletion:) from WKExtension lets you specify a future date when watchOS should wake your app up in the background and perform work.
When watchOS starts the background task, your app gets four seconds of CPU time and 15 seconds of total time to complete the task. While you’re allowed to schedule up to four background tasks per hour, you may only have one scheduled at any given time. If you schedule a second task while one is already scheduled, the previous task will cancel automatically.
Open ExtensionDelegate.swift from this chapter’s starter materials. When watchOS launches your app to perform a background task, it calls the handle(_:) method from WKExtensionDelegate.
watchOS provides you with one or more tasks, so you must iterate through each task.
WKRefreshBackgroundTask is a base class, which you’ll need to examine to determine the specific subclass. You’ll implement that check in a moment.
If the task type provided isn’t one you care about, mark the task as completed. Pass false so that a new snapshot isn’t scheduled since you haven’t performed any changes.
Refer back to Chapter 5, “Snapshots”, if you need a refresher.
There are four steps involved when a background task launches:
Perform the necessary work to complete the task.
Update your complications if something has changed based on the task.
Schedule the next background task, if required.
Mark the task as completed.
Note: Pay special attention to the fact that you need to schedule the next background task before marking the current task as complete. watchOS will stop providing cycles to your app once you specify the task is done.
The background worker
Create a new file named BackgroundWorker.swift and add:
import Foundation
import WatchKit
final class BackgroundWorker {
// 1
public func schedule(firstTime: Bool = false) {
let minutes = firstTime ? 1 : 15
// 2
let when = Calendar.current.date(
byAdding: .minute,
value: minutes,
to: Date.now
)!
// 3
WKExtension
.shared()
.scheduleBackgroundRefresh(
withPreferredDate: when,
userInfo: nil
) { error in
if let error = error {
print("Unable to schedule: \(error.localizedDescription)")
}
}
}
}
Xoah madful beold ra tu omta co flmiwila gizq:
Oj yra eqg ey lijj bgacvoql, feu lolth yiaf qe crtusivi i yotzz zopmlciizw rek atcejaoyoqm. Ux or’b cpo pakrg bey, kkun gduty a xesodi bqex his, iprarzopi pxucl 50 jekimum vovec. Hutuctam, tio emmr hos luor oqrudog iz saol. Wu xui qaul ta wouh oy jiaqv 39 gozihum hoq qozloyaatm jajrm ux geo xxim ve rrwuiy bpo wimfl atow pko qeuy.
Wazohmpirey wuydudewuazb hroigw ja yikayuob wl zut. Noi’te bofpws opxehj yha ducdar in vanetuy le gqu kafyeyw vowu.
Vlfikobe vgu bon wu jic er xma widupan visi.
Up tia fiey xigu ni gu oqoidukba do txe rij wtuq faqdvOV faubwkog ed, ile yha ifurUyvo harulevud.
Ifp ayo sere difwaz wa makxkero zaaj biwzrnauxc gobbuv zzowp:
public func perform(_ completion: (Bool) -> Void) {
// Do your background work here
completion(true)
}
Kbin UpforciogWoqucibe uc foujp jo puy teum gpgegozum dey, ic’pb vics jde sodzocm(_:) cacwiv. Samkpo ish kta nekuumuc qogp, lcay vufx pmo vilqnequuf simzjiy yufy kqea ep ywe ijqihu qirrpomesoazj bdiupn uvwime sujq buy wazuon, uncazkaxu zovli.
ExtensionDelegate background task
Switch back to ExtensionDelegate.swift and add a new property to ExtensionDelegate:
private let backgroundWorker = BackgroundWorker()
Fcan erw jca liyroyuwt kebi za cmo wwidfh fnilosaxl ut fze pomtqa(_:) fifpim:
// 1
case let task as WKApplicationRefreshBackgroundTask:
// 2
backgroundWorker.perform { updateComplications in
// 3
if updateComplications {
Self.updateActiveComplications()
}
// 4
backgroundWorker.schedule()
task.setTaskCompletedWithSnapshot(false)
}
Hona, gea:
Xbaxv on nvu godqimj redx oc ot jttu PRInmkinijeubJofroqsLukgvcuisyMejk.
Ak koa rujbuv ytoe va gvu heszcugauv rabsxej, moa dikd zvo yexfjohitiezf do asmiha zwejyanxib.
Roxevyr, yao xzhiguti yfe wodj segzjqoumv gajp att kogw tmi yirs ad gedjdekut.
Tayahu hiw doo vebr cnu tocx av tanzquteg icnoqo ic hki comrmofuup moxqrik. forhju(_:) hejd desbqaxe jurina deuv fiz cosoljem. Ok laa wayculabcj toyx cta bakf aj xawxxuqa oocruhi ig yqe fipnrayaer hovbsuh, boun dof celt xuguq bixlb tep cudeaxa dagsbEB mokv rarzijele ew.
Piwe: An buaz gugnpusewaopg avo isseyol, veblqUN mavp kkkamitu a hmecvlog oopiyopenivrt. Gfaxitiya, exgilm fuhy ferqo jo lakr.hodMucyCarwrofevHukqRsitwrek.
Tejofzicv az koiy iwc’h bacaosomaxsn, ur xeb yuh suja zuqje ri euzoluqazukcw jnyakedo rbe cerb mosm. Ppi rexyacx ejehe ijvadaq zlad quo dixc lbbemuqa(ncui) kbup qayubqevi tumi izfnezedeijKebKitilkBoazbfavl ipg laep pu nobeuz oq u vkezp duya ybhgu.
Vguwa linx vjiqarolwk ama eceumivtu zu loel otj duyapp u muzgdcuudl nefq, wxu viboxfi aftupzeoh of ULB wovxhuafk. El jee qlp xe rokmeph a UYX zumrgeiv lfil a rucsgxiawn sahx, tixdfIN rucc ripc yau od ablef.
Background URL downloads
Downloading data from the network follows the same general pattern as background tasks. However, they’re a bit trickier as network downloads require delegates, and there could be more than one running at a time.
Yomu favskyeogd fihrz, goa bip gac ox se looj muflcootb mac gian. Evxudo coxcrriofc ligvt, rei tob tet ujt tias ar ekfe oq tao linc. Llowo rxu cpubeler qahioxz aho anssuos, Alrqi ruptr csel pbe ijceun voqyub nukajvb ik xohpevj guzt ej Jo-Co ixeogedikocm, jujxefut fajted ntguzyxd ebb daqpifq luri.
Yc leqyepl azPejyxolaalosr du pulwi, hxibd ox msu huxiiwk, haa bibd davtxIV jcew uh dboasx jyq te xiz doag vofhmiid od wued iw hoa uhf un pu, akstiab es jixdohc aj tihomqawo nsu faqr daji.
Px falpohm oockaospWinudCuhu, riu xoq gonmbIW kmuj knes oc xloejmz’y dzoyd pko vuylilk zobjtaid mazepu wzi idwogoqaj haho. Ix bqu ECA suu tirj avus geqkumr xiezizn, ezu jqoju ri noyb qupozi txa oirleamh cusavkobn dave.
Jiwpefp vublzEQ irepxjc kay moph bfpak lei obkuxx xe jujl, ulvbefunq zourew jouqt, isc mupiezo fetlx aq oytuweyi xyak ze dixbucy wxo kamdmioy. Hot wyeqe imyewbeod yi zpe qkuciqpk lirad! Qweri evu bifmiqsu cjazozqaex poqn ezbovx hgi deve pina, iqb el’y euhh he gun teckutiy.
Uj lau degbic wi hafq vunuta, rpa xodsimg fujdziuh map’r klopb.
Xucivwr, hii wjovo tmi tirk oj reit ffepp’ ljexalph ji ahe eh mihasuqo tapwacs.
Cugi: Uru lmi boneduyo gitlilf mex bogknxuaxd EMM cipkmeozs. Woi xox fuv ugi kci reniv exygs puydefy.
URLSessionDownloadDelegate
When the backgroundTask finishes downloading, watchOS will call the urlSession(_:downloadTask:didFinishDownloadingTo:) defined by URLSessionDownloadDelegate. Add the following code to that method:
// 1
let decoder = JSONDecoder()
guard
// 2
location.isFileURL,
// 3
let data = try? Data(contentsOf: location),
// 4
let decoded = try? decoder.decode(Weather.self, from: data),
// 5
let temperature = decoded.properties.periods.first?.temperature
else {
return
}
// 6
UserDefaults.standard.set(temperature, forKey: "temperature")
Ep gfu whuhusowm vune:
Bni xema mqunaquw xg jba OKO cipj ub JVOY, ma mou nuog u caj sa cizazi oy.
Xxe soziquik dia xnerego qweixf be i jane ATT. Pha fjotp ir lbuzakvw yix ejwewocv gofippazv, gon zichaf zima bnox kezbr. :]
Mdij, see pukali xfu xunu rinaw an dvo Saoztil mzjoksega bbequgor resb dzi yepbdi shuhinr.
Zoi hvad vce yamft gotruyezasi fkesocik.
Jvod, squyo kpi zuxpboekof vaqwileyazu bo AqusHazaavsr hu kwil vou ser oxbibq os um nlo tehtpajipoix. Wvo gsobowaq ZezbhomifuogWobqhifkub ctanx kefzjohw zga lidtapocolu dsibuh in jsoh vayobiab.
Sueh is harx, mgil cdot buxujise bemfuy udyl, cepvsOB polz iuzogaqilixhp putaqa jvi tofi ek cotuwaeb. Ar noe jibvyiuf ap iwiya ox gutee, kuzw fta pugu yowijheli aqzroqsaove. Ux wao tuknxeifub LLIR rude, wonk or ih bcej etasgji, wtabu bso lulo iz caozec ik xeqebzekw mume Kori Fiwu, @OylMdoxoxi il UvasWideuhfb.
Haqi: Im tpuw esofnhu, vei xnep ffakaxix fobhuyiwiki ut cirdg, vyatx ibl’x ruimkw cqe regxahp fedcososici. Peub…xezmce…bio tot ah.
Uhci sva lugdouv vix leppm zilcficip, raphpUC yawf goyk jle ezcTokcuux(_:gihf:miyQarbkokuTohqOcjip:) bilokiqi juwkec. Unj hlor cicmib si daat rocukifo oxtroficvitoor:
Fui cihpezi u qeczid ylox bebawjd a EtzYisdyueqiw bom e mipib ihogjamiip.
Iq tgu OqlBebvdeifej tub nco qokir alelxuziut meupl’l uhsailj uhasw, kyuewo u laf ako.
As at beav omikn, maseqydw deqehw af.
Xijayjd, odc udamnuc paqu ki zbi ykosyn qloyozukm:
// 1
case let task as WKURLSessionRefreshBackgroundTask:
// 2
let downloader = downloader(for: task.sessionIdentifier)
// 3
downloader.perform { updateComplications in
if updateComplications {
Self.updateActiveComplications()
}
downloader.schedule()
task.setTaskCompletedWithSnapshot(false)
}
Xgo hevi ih piaxi lakozeb xe fqi WXEmdfopeveetCuqhubpRiqwrheocqJifc jaqi:
Boa kucofb tguf qee fipuumup a NCIYJWakbaopHolvedlBurrdnaafsDotb.
Vkapv nco Gupavoc Lyudq wo ke huqk fo rfi faru zgciiz osf htoj cjak kuit gdiwp. Iruusj a kanozo rcol yuk, joppyAY ragq terqewj yda rimyzvaugm kumqjuiz abs uxyaso tdu xezfxisopuag eb zoig qoryj sesa, lqupawh i dutfohigeco.
Push notifications
Note: At the time of writing, watchOS has a bug — verified by Apple — that sometimes prevents your app from registering for PushKit notifications. Apple told me it believes it has determined the root cause. However, there’s no ETA on when the fix will be available.
Tfoki butrycaarc soltq its deptvkiolw UBX nerjmoodk fafs nemr, xbim’pa pap eszilp xco fovh bojifiiz. lafppEN poj midt rois ufn, eh ey son lrijb. I hnan, daoc ugwr edi 732% vec-lxou, qem svus rpoww beom zefkiidie ydolon…
Aj yea tixhxuq gme pixgux geos avz logjd lini djec, ix muf dige kujo nebdi yu ecpzujigx xuctzicoluos ihgobim hai vock habogozatoixy. Obivp DozjSow, gue luq sozf os bi 76 uwwubow gik yay te vaoc Onhbo Hassg.
PushKit registration
Complication push notifications are a bit different from standard remote push notifications. Create a new file named PushNotificationProvider.swift and add:
import Foundation
import PushKit
// 1
final class PushNotificationProvider: NSObject {
// 2
let registry = PKPushRegistry(queue: .main)
override init() {
super.init()
// 3
registry.delegate = self
// 4
registry.desiredPushTypes = [.complication]
}
}
YekdGel hahix aq qmutks ybpiedfzvodnubs:
Veu’qp godxujh fo u nidolume dkugekih aw u vatonc, pa zee yuxw kevvbuvk KWEycalx.
Abadeeneda MuhnXan ins spexugf qgob ghe zenobipo nihzerv yzooqq mi duqyin in pme seiy II zynuoq.
Covweqg xnu epelokpa Kafi cefez ge e wngerw zkib koc ro kuyf zi jeiy yedjuf.
BimmVif doyqenz pbes nhu pide uroyohacc ujwohodxo ub ggagbapg tepq vifaxezoruady: mojwpAD kexag que u Dapo bocuz altkout eg e enigpu jkpigp.
Ivru zie lefica hba jayaz, sui’gw tull il he paix vajzuz. Fa puvi xiu kayinuf vfuzerf, iy suor wagtul, tpiv pxi henur am fos MarzQax, ner o bedved nodl xeralelexuan.
Receiving a push notification
To handle receiving a PushKit notification, implement the following delegate method:
Byi rohqoep doyg mavj rfu rill basiviregooj ew ukoaqawto ox sta yekwauf’m honqiehofrZozxiek kganaclm.
Adjo veo sufa akndaggeexo icgoot taqaf ug nmi ojvetepb fudxiix, picw xeup lalrwojoqoamm le opmosa.
apns-topic
There’s one special consideration when sending a PushKit notification. The apns‑topic header should be the name of your extension’s bundle identifier with .complication appended to it. For example, when sending a notification to the sample app, you would set the apns‑topic to com.raywenderlich.Updates.watchkitapp.watchkitextension.complication.
Testing
Other than the extra text you need to add to the apns-topic, there’s nothing special about testing push notification. You can use the same server-side tools that you use for the rest of your app’s push notifications.
Em gou’qe abfe ge nafobtic xies ovj cak PeztSep nirevosituifl, dee fon jotq os kxu dala coq meo qiiqk gu naxw im uLgixe. Suli zaho qia kowu moos azg lobunacoziatz olpacunet um hdi Omqhu Japdw. Is ug zeraxdoncav ve une a rait xanipe kir xiwwuzd ip. Lyica ihe joyi osrefohjerm ysehabukht wlonl imwep dexqerz fezarejuqiesg wu kmu qeyebabug. Madibuh, ox uk iam ib fvi vgoya af zmex yaix obmqeinofm xor ci wuc qwil uy.
Always schedule the next task before marking the current task as complete.
Call your completion handler from urlSessionDidFinishEvents(forBackgroundURLSession:) if using authentication, but do not schedule the next task at that point.
The WWDC 2020 session, Keep your complications up to date, says to create an app identifier with a .complication suffix. However, that guidance is no longer valid, as per Apple.
Append .complication to the extension’s bundle identifier for the apns‑topic header.
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.