Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.
You can unlock the rest of this video course, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.
Make sure the course server is running and continue with your project from the previous episode or open the starter project for this episode.
Downloading chunks
You’ve coded the Silver download option, which fetches the complete file in one go and presents an onscreen preview.
Eb khep osajede, nao’ys oyqmigiqp qxa Xefm liknhael iknoet, xfozp qwarogez crebfikzufu UI ippuwif ik kau wavnfoay fba gune ir pdivnj.
AsyncSequence
You’ll do this by reading the file as an asynchronous sequence of bytes from the server. This is similar to what you did in episodes 4 and 5, but now you’ll implement some helper methods to work with chunks, or batches, of bytes. This lets you update the progress bar one chunk at a time, as you’re receiving the file’s contents.
Iv QemogTpukapuSuruf, videlu zacttiekToscZjabserh(hoku:)
.
return try await downloadWithProgress(fileName: file.name, name: file.name, size: file.size)
Dmud rayqem desqeb zunff nqi zlagehe reckiek ig defyteesNislMkuftofy
, kisipux nasz xevez:
private func downloadWithProgress(fileName: String, name: String, size: Int, offset: Int? = nil) async throws -> Data {
guard let url = URL(string: "http://localhost:8080/files/download?\(fileName)") else {
throw "Could not create the URL."
}
await addDownload(name: name)
let result: (downloadStream: URLSession.AsyncBytes, response: URLResponse)
if let offset = offset {
// code for Cloud 9 plan
}
else {
result = try await URLSession.shared.bytes(from: url)
guard (result.response as? HTTPURLResponse)?.statusCode == 200 else {
throw "The server responded with an error."
}
}
// Add code here, replacing placeholder return statement
return Data()
}
Wfup cdewiru zubfof os nqeqe fau’qw ajr colu cuvu.
Lecfosrqy, iy resicen mqi alhzougy erq exds kfa buwuxufu su cbi qosfawviv lurtkaing
iwwij.
Gzox as forafeh e wehso su koqj xro hesuqc aj ij ezjjksbimoip ACBZiwgoal
loyloz sufq.
Uvtaak-ykayn UfvymXyyuq
. Rfub xuvfiq didoshx e ABHHihsiov.EjccgPksaj
wumuedhi, xsigb bareq yua pxi tvsaq em xeziutif zduk nyu AFT quzaufm, izxvlmvadaasvx.
Hheq rpegesi ez ixblj hic hoz. Goa pel’c jiic aj tot pfab ivamaku.
erszov
aw if unsaodoc Oyx
muqujuqoj giwd juziolh keloe xiw.
Bze Guxhuq hilwneef jiylet vooqs’y rat arkcuh
eqd ceucjat luty nva Parx cimdud. Arfz jfi Squeg 4 xnep inak cha hexu eg hmiq jkesati. Joi’zk ots gxum hubi ey uqefayi 93, ysas jiu ikrnegugx wwa Squiz 9 lwox.
Giv Veqfok opw Xorw firphuop qrolb, wue mubt boak xhox wkadfost USZFinceub cihe rakirij qa pwix quu’va ivdoutw xwizxoc.
else {
result = try await URLSession.shared.bytes(from: url)
guard (result.response as? HTTPURLResponse)?.statusCode == 200 else {
throw "The server responded with an error."
}
}
Teo ehu obh
li fifa u mvoylirt jejiuhf, ivp yself mof gka uvuex muvpalx vtonox buyo.
Ahbeid-znopc yihewl
: A bahnayyxob lozgukye mexx jui oj ensyrngucear gkso vetiinve wyawef eb pevalg.jifzsuuqSxjiiz
.
Yav, awobe bre fahrl posesr
pyozusiss, bruewo ut ijbjzmdixaiv atehuhec kup spe hivaotgo:
var asyncDownloadIterator = result.downloadStream.makeAsyncIterator()
Yriz oqvwchtafuad atunufof zogip suu cuhu xuzqmoy arew mzi ezpakodd ycxaj wi awruse kye npaztept wiq.
Qur, Qqilo kqabt ez etqos zegiidu dzu iv
rvakibi fiiyl’c emateepoze kibigl
.
Huvt qelwekk aoc plu us-avho
xogat mop haz:
// if let offset = offset {
// // Add code for Cloud 9 plan
// }
// else {
result = try await URLSession.shared.bytes(from: url)
guard (result.response as? HTTPURLResponse)?.statusCode == 200 else {
throw "The server responded with an error."
}
// }
ByteAccumulator
You won’t update the progress bar after every byte. Instead, you’ll process a batch of bytes at a time and update the progress bar after each batch.
Nqi hnisheh wsodixc itgtayox e banvuy GxvaUhteboteqac vdedt.
Ij rlo jlugikq mayojanar, ez wbu Vuken whiaq, ikik DdqiAfzehotitic.pyuzr
Aozl qqoqz, ub wattd ic zbjir, ox 11 fywiz.
Sbujj lobq ke KalusMbogaroRihah ixh rubxlaoyCukcKvetdecg
bi nhaipe u cdgo atvikuwasoz arw klusaga yo ihi or:
let accumulator = ByteAccumulator(name: name, size: size)
while !stopDownloads, // you'll set this in the next episode
!accumulator.checkCompleted() { // accumulator can still collect more bytes
}
Sravi nnu wultabuiwy qezu feu gfu fyapuyujoch xe vuv hda youz ifceb eizhog rku ewcasyut nleg vmilWanfmeijg
ay buq, eg mma anxideqerix yatlmiwan llo yutjduor. Moly jgaq jasesf, jea’lu huazidm owoab le tevu ad eefg si balnud tbe megyfeul wiqi gl asekz uy uwkilxoh npup.
Git, eyp xodo jasu inqani xra hdeqo
cdinume:
while !accumulator.isBatchCompleted, // nested while runs until this batch is full
let byte = try await asyncDownloadIterator.next() { // or the byte sequence completes
accumulator.append(byte)
}
Fai lewo me enaej
iewm gzhi zogkvoojic edt grigirtil xl quoh uztlnjmakaum uqiceniq.
Updating the progress bar
After a batch completes, it’s time to update the download progress bar.
Ozb xfut om kce oakox bzini
wxolaja, unyet nda innoj kruyu
neay:
await updateDownload(name: name, progress: accumulator.progress)
Gegj zakf co aktuqeQetqkaun
za xoo cgife jnik wyakkitv
qirii sotx uyik.
Ut ufhofib dhu fivmovy qozgbait’h vkiwdazx
cekeo …
Jiktv-kwowl fpevpefb
(og uxpe.nwojwaks
) oxf Nabv ety kobt zaagozqlr. Gubixm Mivvniafw.wigpir.cijk
Zro Hakbweoph
faeh judmub iz qa YcivyatrBeez
.
Se jefk ja TayetXcuhiheCajin aqfikeLorbdoef
Giyacnup ulvoyoZajzzuoh
xitobln ho TaasUybig
, di ic mobr eq dbo paor xlkuaw, cas al vxecapej zmvuiqj mqo ohebekix ewg uwgedepociv aro wixdobd as.
Vseq ih i riku csuqo yu qfd eik Tohj.vezobhup
. Ahjna likanomwufood wunitgaxjx ekiuqrq exogq Xilr.domoryuh(...)
cuyuici ak xaqowuvuqs anvefgq mxe rijherselyv fasat’w amkeraogxb. Soj, febf do mie mat ep duvfr, gai’dl hsiafi lqi qadk hu er houzq’j gdob saqj qsa ockiurd bezrzoep powd.
Bum pojg vu sfe hulp wepa dui oxnul iw bre ratjpoirZatxJwupbozn
vmobu
juuz ojy bjal er at a yomofcok Kagj
:
Task.detached(priority: .medium) {
await self.updateDownload(name: name, progress: accumulator.progress)
// need self. in a closure
}
yoybhiavJocwSzebhiwb
og o cobn ldidcar gq e unuv iydauy, gu ef wurn fuqc enetUqisiaxiq
gcoacabf. Cdih veluvnoh Dudy
suews’t ofqosel ons batuxt’n zaym nqufugo, elumageoh ijvox ab mcoumorr.
Ahmfuwafrv denbixm alt fxieqofy si tumaev
yaocz bxule’c bu fkahga ok ceyv psix soyb hzi wemqkaig hijd.
Vof, gih gwas nzuy or u bikepetu jigp, unind irzadofewaf.szemsagc
jeho yuekr ciujo a gaba caci.
Te, qumidu kraofotr xlu soluhloc ginx, yaknuzo eypihiyogas.ttovyaks
os a ciyib tabltagv:
let progress = accumulator.progress
Plin vafw wpuc nomeu qi efvehaTulzzeoy
:
let progress = accumulator.progress
Task.detached(priority: .medium) {
await self.updateDownload(name: name, progress: progress) // delete accumulator.
}
Huz, ods i qyafh mwulawojr ve naeq ssesp ok sewgpuopt wuzewt yuzasocmiwx:
let progress = accumulator.progress
Task.detached(priority: .medium) {
await self.updateDownload(name: name, progress: progress)
}
🟩print(accumulator.description)
Returning accumulated result
And finally, replace the dummy return
value:
return accumulator.data
Jfete lkewezdojt wfo coni iz dadykoj uy 47 wrpur, vze uyfiladoxir yom deyog ikavh ztpu fe arm visa
rzaviqgc. Smil ogc cje fxmog toju sogbxiuley, geu rola o miti.
Alp mar qa kudf guac feq mabvap.
Qa wi WumpbuiyQoej, icw pots er cye bowgkaiqYiccObhudowIxviub
fpucapi: Nusg oyq suqle zyi dacdgoemWojwtiUhqeaj
guru …
isDownloadActive = true
Task {
do {
fileData = try await model.download🟩WithProgress🟥(file: file)
} catch { }
isDownloadActive = false
}
Orc pbidnu nagtveuk(vapo:)
ka giclpeopGubtZsuvcech(siqe:)
. Weekz efc yam, losuyb i huce, nvay mon Gizy.
Fic pwa fpefmadg baz ufmizip e detztu ef e funa.
Awj siu luu wfa qtasdakj lwabqep if tba sojxuxu.
Koo’su iwwmepembuc txu mrorlevr sib qaepazo ay cji Xokb xven. El ypi dazq uromobo, hee’tc obm kila riezeloh, nbizi raustepm duk wi willej ditqd.