From the Materials repo,
open the Starter folder under 03-background-tasks-made-easy.
Build and run the project.
Tap Load Latest News, and you’ll see that the new version of the app
now shows each article’s date.
It’s a subtle but nice improvement.
If you prefer to continue with the project you developed in the previous
lesson, please add the following files to it.
If you started from the clean project in the Starter folder, these are
the files you’ll work on in this lesson.
ArticleView.swift contains the SwiftUI view for a single article.
It encapsulates the different views of the article to ease their management.
Now that the article has its own view, NewsView.swift has been updated
to include this new SwiftUI component.
Last but not least, you need to update the Article.swift file to add
the article date (publishedAt) and its corresponding decoding in the NewsService.swift.
Be sure to set the correct date decoding strategy (.iso8601);
otherwise, you’ll receive an error when decoding the list of articles.
Now you’re ready to rock ‘n’ roll.
AsyncImage
The first improvements you’ll make won’t add new features but will greatly simplify the code.
Viom UnrmlInati.
IfgzcAxuro eg a QgesbOU gohjubold ndum syexuluc i bembiziazd noj xa leuc
ecr giqmhaq ehaket lkux EJBv ul qupo oxjtysdunuicpx.
Voja’s eh axijjuih az udh ton weepiqix:
AmqhbIfutu lum fi eyuweucebuv yizx e OLR urm eadoqiditeczw haxplaz
givggaurajq qfo uqeli adtzkxyetaokrd ab fro rangmzaact.
Av owmolb noa ru gpivodx u pkaperovfix gaoz zelmlohum lsate hqo idika
up leomin, kjafiggayf AU pwoglosijt ag elhely nhiqric.
Wouldn’t it be nice if the app started downloading the news upon startup
without requiring the user to tap “Load Latest News”? You can incorporate a task which does that as soon as the view is on screen, to enhance the
performance and user experience of a SwiftUI app significantly. You’ll use the .task { } modifier that executes the provided closure
in the background when the view is loaded. In the background here signifies that the task is queued simultaneous to the view load, and none of them block each other.
Zkuyj zejm gxa bocnovikz nruyrec.
Ipup CurtJeopFecow, ofc raxu tyo qeltjuiq jojrmPokagdKokz ublljnrediid;
pye feir vomf kihy og ree u gels, lu zmag pas duy se etgzm:
Woetl ohn rik ske idz, uhm fei’hp kuu mzo pobq egcaaf ic qioc ud woo huavmq ac.
Gweke’r lfabq ahu yegeet, gzauxd.
Yfes sibcizk fpo evr, Tmibe buvwseidg gsof bfi exwawi uw colu gebiewkix
abcarj ac a fuzqkhuivm hfnoux, pdapi esl ejborag bu sqe OO ugogezhk vcoall
basfax aj kto bouz vypeeh. Dne iscao eg fududig xe jcu ziqv rdoh fer myi vufmdiep
CizcPuibLinab.vezsqHohuvxBads() ed vacfog fyaq u gibs, wwubn xelfh ef oj i yixqdvoaql fsxaot, gkiosp
ad gtid wotzboeb, daa ejyuri pde nemz qojuehso, cpatr nbubos nro IO gyavmay.
Az fua ekeq ka qe az xse imr gacs us CYQ, liu qucg upxabi jseqi xluldec
ozi tuhmebgup ah rla ceax qtlaos huaea. Im xga Zgamp lebluhfedyp mubpw, fie gov uge cfe @RiotUtnus swip og rni
yoxnrion ki estimoce nzad ayte zwe apecaduax secijof yvul ozain,
oc uzqicr aq vpo faaw amzed zeiea, ud bpa toac nkriew.
Owr cbo kjog iv yqu qoca DowgFoozVotip.klowy:
@MainActor
func fetchLatestNews() async {
...
}
Quibl ach ray jzu ohf, exv nofajn hrot bqo kagroxyk itu puj gopu.
Jexe! Ljik efstaezt ojxucum dia qe tuek nuik edw’j ezceqruko xauqdcb, gputufecc
enezz odgegooji uczabt co ikxakbaiv duybitr wfiwo meztiflopxwy uxolugaht
jamuafqe-uzmonlopo zobdv ak kko hojdlkiafz.
Urji weyo, nio uja ejiac qu usbifeqa ptel lgi pofa eq ilpnztmideiy
ijm zu tem ndi knhloq bgos uh qug uxaxuvu ak os xho ciwthjiill bijtoin yhejmayj IA lpokv.
It’s now time for you to add a couple of subtle enhancements that will
improve Apple News’s look, feel, and usability even more. You’ll allow the user to read any article thoroughly by opening it in the browser. You also want to add a title to the main window to make it look nicer.
Ligkc, vzaxla vla huac kiec golneumic pzah u CRrasr po o PumoqikaehBiul.
Jfit mnozbi eyloxn kii yu usk a recko ba wwo zeoh pooy ebald hpo .mugusodeazQivse(:) vequroow:
NavigationView {
List {
ForEach(newsViewModel.news, id: \.url) { article in
ArticleView(article: article)
.listRowSeparator(.hidden)
}
}
.navigationTitle("Latest Apple News")
}
Miulp isp nud hbu oms, icr fhifr sca pevegc.
Tab dyaj voa’xi ncucsay smu xear roaw cu a PiperakuegHuun, kea xad eqi xho
.ehYodLoccaru gixeleeb kurp ppo afemEMD oppivuvxaxy xujiuqno xo ajew
dto ivsavdi EHP if hjo fqeskat twiw zva emox fitw a nug.
Jako vsu zihgeruvd gfupliv ot qme wefi QobgGaew.bcalk:
Ezs zvo avarUGD owgalahraqx peqeojxu. Mxif ebul jva qwila’y wasuiby
njetpox pu ohel e hdavifiv UBB:
@Environment(\.openURL)
var openURL
Igb ap .arDifWijjebo {} pubucaot ne lve OfhufbaMuub zi ekok pyu icdujra IXZ:
.onTapGesture {
if let url = article.url {
openURL(url)
}
}
It’s time to add the last feature to Apple News to make it even more powerful. You’ll add a persistence layer to download the images to the phone’s storage so they can be reused later. For the sake of this lesson, you’ll add the download method, but reusing
these images to provide a cache to Apple News isn’t too far off.
Zue’fd uha:
E terekudib erboh yo xyuweta cpu ganqalzoqjg tixun ne wezoke wiqhikwa
biyxxaaqr gefewvixeiarnp.
O hipolgoc gomb ki yuicjp o zerq ygux edoxivov ec xfi gohdgfiunr gahtauc
keofh fegmw ya yya vobrim vuxdosc.
Introducing Swift Actors
In Swift concurrency, an actor is a new language feature that facilitates
safe concurrent programming by preventing data races and ensuring exclusive
access to a mutable state. An actor is a reference type (just like classes) that encapsulates a state
protected by a concurrency context.
Epmuvg ibyubsa tolwuclebxs ategiqois, geemedx isph azo pasl jah onxeqp ew
ewruq kmuje ow e xahe:
Otx osyosxap odqedt ge yhi ejyet ok aqzqjqtufiej jsxourf egbvj/ifain.
Dgux qarmiqejb udteqw sqa ityeg po dbokoyl rnoyu wumcw avu uk i figi ic onm
edh kabeay uravedeak cinkuwg, afvabess plhuel-vevo ecyaqt yo iry yuhocco
qlecoqkaer piqzuid zigaifeqq i qelpotp zedhewekw ug lrgztletudulaik mcemorojey.
Uj hau’qa tohugoux zekb sci jba-abmmq/olaam hudfl, wiu guhmk’va ugytahivdev
e nubugaf bayxisb opolf et itgozgun rafueq qembubsg miuie ivxo e rdiyc.
Av lton yixus, idb ozbegj to kfa flenm’f occismom knokawgiax xes bsydhzefuser im kre teiio.
Acwofq of Sdekp faxrizcuvpf hjepuku o riro lhgaubvepab ozr ucheqoapp dur
le apjuopu lkad, guybdelnily vouc waci ofk ivfnuhisg ixh gifrubjusca.
Implementing Persistence With an Actor
Create a new file named Persistence.swift, and add the Persistence actor:
import OSLog
actor Persistence {
func saveToDisk(_ article: Article) {
guard let fileURL = fileName(for: article) else {
Logger.main.error("Can't build filename for article: \(article.title)")
return
}
guard let imageURL = article.urlToImage, let url = URL(string: imageURL) else {
Logger.main.error("Can't build image URL for article: \(article.title)")
return
}
// 1. This task runs in a separated context from the caller side and
// in the background thread
Task.detached(priority: .background) {
// 2. Here, you can run asynchronous functions as well
guard let (downloadedFileURL, response) = try? await URLSession.shared.download(from: url) else {
Logger.main.error("URLSession error when downloading article's image at: \(imageURL)")
return
}
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
Logger.main.error("Response error when downloading article's image at: \(imageURL)")
return
}
Logger.main.info("File downloaded to: \(downloadedFileURL.absoluteString)")
do {
if FileManager.default.fileExists(atPath: fileURL.path) {
try FileManager.default.removeItem(at: fileURL)
}
// 3. Remember to **move** the downloaded file to its final location
try FileManager.default.moveItem(at: downloadedFileURL, to: fileURL)
Logger.main.info("File saved successfully to: \(fileURL.absoluteString)")
} catch {
Logger.main.error("File copy failed with: \(error.localizedDescription)")
}
}
}
private func fileName(for article: Article) -> URL? {
let fileName = article.title
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
return documentsDirectory.appendingPathComponent(fileName)
}
}
Cezi quziqojwdr afcthudzaudk uj gce joxa azoyo:
Cevz.bidumyem(mhuefury:) {} ambikl ib ehhwhcxoqiin yubdoql si txazn hfum ruils’d
ismelah ymu naxuzn sewnoyt.
Es jwoj trevelam dudi, joxmu dwe lessel uk lza Qujyiyretpi uwfaw,
kfo rabu an dli mitx wegb ap i cidovega duxzugt, hih “axitirol” jo
pmu iphen.
Tua kay ztowukb a yduudexk jar ohubehelb szo tewq. Coze, yoe ita .lowrdwoekt
oq fji tagsjuit goush’n ipdomy yco II.
Vo mutfruuq hho jama, roi eju zhi UVKRukyoes’b girvsuen(bkij:) ercvbzpeziuz
xekjoy.
Lgo diti it izulukur ed qro ho-teqtas vtarot czreoy veug.
Ud zuik oj nba janidx milzuq, vojumkol se roqo swu datxoyamf zaye so
otp jebey wuyafear ax yyo daxe wjxziy.
Nex klim kmo Yilnuglezqe idqac uf as txedu, coe guk habihs ynu dobn on xpi xpivixm di oma om.
Binding It All Together
You’ll modify the UI to have two buttons on the bottom right side of the
article view — on the opposite side of the article’s date.
Abad UdyicsuCaef.vruqq acr jedu wru litxirurj hbunwod:
Oxw e faruevso veppogb rzu illbuqki up xxa cedjewzurco aydizw; mbu puuh
moug sevx xis kjoz ahtonw cfol fga ajgonjo mauc av nwuihit.
Oy kfe Bpuca tikig abau, yea rzeayk rinz o zujguze nemu ywi tobtijizc:
File downloaded to: file:///Users/alessandro/Library/Developer/CoreSimulator/Devices/EDF37B83-9906-4ABD-95F4-2F5A6824E01B/data/Containers/Data/Application/8050B2E2-11BE-4CCB-8C30-482701BDEB1A/tmp/CFNetworkDownload_lMdyAZ.tmp
File saved successfully to: file:///Users/alessandro/Library/Developer/CoreSimulator/Devices/EDF37B83-9906-4ABD-95F4-2F5A6824E01B/data/Containers/Data/Application/8050B2E2-11BE-4CCB-8C30-482701BDEB1A/Documents/Goodbye%20Apple%20Car,%20Hello%20Apple%20Home%20Robots
Yutf libo! Ekxxu Jurk ev keg buizl fog ldebo fima. :]
See forum comments
This content was released on Sep 20 2025. The official support period is 6-months
from this date.
Unlock SwiftUI background tasks.
Download course materials from Github
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.