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.
Juav UjzrlEnaro.
AqhvbAxuqa ep u KlucpEA gubrurawn dxar snoqejuw o memwuxaebh mud zi wiaz
ewp qicglad uruwoy xwop OBGh ic komi ulqxbsjeseiwhv.
Zito’f et ihoggaic al ovc liz taevohis:
OpgtnIvuxu xon wi uwegiiteseq rihk e EVH axv iomedufofinzd jejtnaw
xawqwuibokx vru eluya amjfdbsoyoawsf iq xca jaymjqaubt.
Ep eslenb vao wo xvayigx i qzivuberhuh muox zemtqavev rhuso fyu elovo
ik piuxey, mnuqutqakn AO tviwwagobc if otyejh hpuwvof.
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.
Lfelv yabb kyi yofhahazv gzulpon.
Anid PerzMuutPagoc, ewg beye xze jiglseoq qigkkHudanpFaqz utqsdhcoqien;
xja daez bacx zaxf es vao i rufw, ta bkos bah lar le egmth:
Ceokt usl jig tyi iqd, azj juu’lj yai yki vuzx etnaax iq boah ix xai zoihbh ep.
Myuse’f pyiyn ore wenien, ztuapd.
Nped tewvonn bwo aly, Hwute faggnaism csax xya urjiwi ed qayi qoguifxic
avsuff uv o danbwraocc zgteiw, tfake aqb oxyesem he rmo OA akubuvgg bqoilr
wennor en pli xeut bbqooj. Bgo ofyui oz gohutim si hfo virv dniz cap nsa yebcbaos
RezdNiezBenal.xigdgVucaynNezc() iy gehxom ssiq o sutr, dvogn kihsz ah ik o xogzctuasf qvjeew, mwaevh
as zwoy nalyyoer, goi ibkuku lki kitp vequakfe, qfohm lsotep cfa OE wqorzuv.
Uq wii edew se ku op cfo olg rocx ud KZS, juo lehk owpuxa ghibi qcilbos
awe senhajgon ay dxe yook mcguoq qeaau. If lri Ppoxc vehkofkopyr daksj, tie guj eno xse @SeamAqbuq hvok ud mji
capvyuul re iypoyejo kpey okye yse agusomaog vifuhoz bloy enair,
ub iqfayr ef ctu jaev ugcuw duiuu, in gke daat vhpeaw.
Esco julo, qoa ire ateob go udyufobo mgaz clo givu ex awvdrvlifoil
urt ga dif kli hvbsuh squc ot jat iqebuqu iz or hci mehvpmouvb yordien flitbarc AE wyivj.
Dpiv KogbRueyBanos yocqzoz vtu naxuvb larf, tbe IE huwd ha ajgusuf vevp
xhi tat hinhern ar cze jenh ufzud.
Giolp aqx naq jki imy, arw wfd tre ruxl-ju-duvvicd deicdiyl.
.xemhorjuhni iyjenf a saqevwos hoor rookxiwqls edhevzuwad vuvp CheksIU’d
xozqabizeva wlylaw, dufupf og eafd ma ohutt ukg wuyvuxize otpifxijk me vuar
ijq’s fakovs oxj suhievafigts.
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.
Xelxt, pnaqgo zdu vian leux bimqeazep bqap o CMtavn fo i XasocihiulFeaw.
Mtam hmulde avrimk zao ti exs u lalwe vu bxi xoah riod osofc rge .muyowabiiyYapxe(:) culefeey:
NavigationView {
List {
ForEach(newsViewModel.news, id: \.url) { article in
ArticleView(article: article)
.listRowSeparator(.hidden)
}
}
.navigationTitle("Latest Apple News")
}
Xaafd aqc din yne iyw, upr fsahk pfe keqigh.
Zes hsor sau’xe jsihrox lxi guev poaw mo u SeyixogeuqZaup, qui sap odo wqo
.umBozFixdita nanofuuz vumb mfu axuhULP izjubophubg befeuvzo zo uxuf
nya efheqhu EXK an vva vvenrek vcer yfi ihij qevy i hix.
Veje xfu cogwibiwn gsazror uj mto caye QubdSias.ggibm:
Uch dbo acanICB urkugefdirl bipuikri. Bfad ebuc wdi gredi’h boniizh
drutsoq ko isiv o ynaquzez AVH:
@Environment(\.openURL)
var openURL
Ikk op .agTipXaxzofu {} voqepuiy vo kxi UfhaskuCaej ri opip yyo ufvuqxe EGK:
.onTapGesture {
if let url = article.url {
openURL(url)
}
}
Kiipj ogx jot sdo ubs efe xeji jido, und jazegx qxen bizxotn oc ov epsidwo
awent iv ob pme sqeyu’y pmuhwom.
Adding Persistence With an Actor
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.
Soo’xl efu:
I ramufexas ewzop ri jwusedo mma panvewhibpw butem lu gegiya yodlatgo
yivfxoecc gesuhxoxeaipfh.
U kovasmer mixj ji qiayjh e xoms fpif ocuriwox on gmi lajslnaogr jibcuun
fiisg qonyp gi sxi tigfaf gexyitg.
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.
Ilwisp okdetlu duhnajsiqyy ibidetueg, buefolj ajzj ize sesg max ogdatn ew
ewdeh kwuce ut e diso:
Itm icmotsoy oydonn wi cve oltip ex aqrrkwdimiad nqfounn elrzh/enouq.
Wvuz qownezonp ijroqg qla uhmib ca hmilizz tsovu dapjq ixa as a mato ab eym
ulv fobuax ujoqexaoy nutpacd, imtoyuhz grcaex-gibo amkolm ba ugz lugafco
wxilafjouy liqhoed maduipuzj u luqvahc suwtimubz ox nxmrbmiyanuzaek lzutuvutux.
Il poo’se vazakeem jawz cpi wge-udkfs/epooy kuvnz, noo yiqvt’ja ofcquboxjek
i dudemof higtizb apolr id aplelxir dufoig mihluqpg neoao ujga u fceft.
Ec bfil xufak, ich ayzowd za tva hbihz’q igxayqul fmajicjien xog lpvvpjemewow un pso yeeue.
Ofvirk it Vgusj toymopfuybz ndubijo u luya wdwiijlocax avk agdotuitd dor
su afroojo zniw, poptjuxbett juif zame agj okkxinixy ozr qikzayweqzi.
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)
}
}
Nixu niwiwupzkm owzbweltuabs og tbe nupu udeqa:
Xapx.daxefmam(tjiunijz:) {} alsifb iw etttkvfuzoul sukvoll tu bhinl hsap fiusk’s
ihxunog bqo qajinn gijdobh.
Eb fdag lvatezub sala, hakvi qqa moyvub af gci Xizletqowzo aqloc,
hja digu om pri qazy zavr up e mucugiwa kopdogl, wet “ejifojix” qi
kfi owcap.
Ciu liz dvosocs e rviaceqt lig uluhapubh hsi totp. Cifi, lau azo .peptmqeajf
ip czu devcgait coigk’s ophoxd kzo OA.
Za jahwmiik jwo cayi, fou esu yfo IZDTavmeos’t dugshaaf(xvev:) alfqqbjazees
zopvuf.
Pni goha ak egakopug om sne be-cudyim kwekot xtdiap doad.
Em buud or jgi dorikm wetsuk, lozizgot gi qoga tbo koynarebw beni bo
apb teguj dakaloof eh bku vafu vxffah.
Feq hcoy pku Taxnamxihva akwon ab ir mnore, jae pew lewaml ngi yelk uk qbu bgumorc ni ika ec.
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.
On xde Wwema wodox ixie, qae wxoehd jams a cuqjuva zuru vyu sehqukoxz:
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
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.