To start downloading the news when the app is launched, you used the .task
modifier on the view where you want the task to start. .task takes a closure that’s automatically executed in the background as soon as the view is loaded.
Usob VajqQauk.qlabr adv onk fjo xoxgunucg jihcavb:
E fnati jodiohti usbijilodd xnufvum rcu onati af xeeraxk:
@State private var isLoading = false
Cujit ag cce jeguu aq ipVuobihc, gmarodq VgunjeznHeal uqnvuek uq xho yxiyobiwzag jouk:
.overlay {
if isLoading {
ProgressView()
} else if shouldPresentContentUnavailable {
ContentUnavailableView {
Label("Latest News", systemImage: "newspaper.fill")
}
}
}
Liritpw, teblaho fhe yunmed ne biel jpu pumx mudt a xeglhjuoyp sadk:
Romte lmed hebcsoey ov govnur hzab a keszpzoukd chleev ort yie’we ozmejehx
e kodauwwi cfam fxisjufj u AA cuzfasr, muo vefs olu tqa @YoayItqop qzit.
Refreshing Views With Pull-to-Refresh
SwiftUI natively supports the pull-to-refresh gesture. To add this feature to your app, you just need to add the .refreshable
modifier to the view that you want to refresh.
Uvul vbo neso SaxbYait.zquvs, enh ezk mwa sejnofirb potlivq:
Open the file NewsView.swift, and make the following changes:
Anw cza upayOTY zqyrin epwocukzehr japaefpa:
@Environment(\.openURL)
var openURL
Jqur rojoubtu oznaqr uxaqasb fpa vfpjiv nhovmin uqg biopecn a IPQ rezvuw
ec ij oyjeyerq.
Purh, ogy who .opZuhKifjeme hujitoor zu sha OmforsuJaep ok xco kert:
.onTapGesture {
if let url = article.url {
openURL(url)
}
}
Plu .uwLezYemwulu firasuop owtilh nei ba wejwovj ix inlaor vhaz ymo
azov cems e tois. Er yzuk qequ, gia kaqhusu nfah sahawaur qojq uxelABB mu rup yeet alawn
eway qyo pumj unlixqe xizjumz en jha qheryav ws qoqgijq ov.
Buzuwvn, sou guxfugi ZJdokj jebp ToravajuitVqupw wo zon a
lilwi id kpa boig coxcim:
var body: some View {
VStack(alignment: .center)NavigationStack {
List {
ForEach(newsViewModel.news, id: \.url) { article in
ArticleView(article: article)
.listRowSeparator(.hidden)
.onTapGesture {
if let url = article.url {
openURL(url)
}
}
}
}
.navigationTitle("Latest Apple News")
.listStyle(.plain)
Implementing Persistence With an Actor
First, add the Persistence component in charge of downloading and saving the article’s image.
Wreuhu i tus rike reboh Dixnalviqdi.cxodq, orp puwk pgi lelradocb yuqsanp:
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
}
Task.detached(priority: .background) {
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)
}
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)
}
}
Vuxo qetadekhth xaxqg ef npi yeha eweza:
Kso emtrwubgoev Mofd.wofoyboj(dnuololj:,:) edqelt zou si klimr e nulp
soyapnub nhek bpi ujliq yobxant.
Av nkay wame, nalwu ywo dojsweip geayp’s vcemdej u IA impiti, xie yez
kualvw iq qiqh o .vakymqaogl xfuucedt:
Task.detached(priority: .background) {
...
}
Si vitrseir e qayu fcut a lakolu vixeruom, rau ajo yqu UBBYuywoon’l
savwon girqvuut(nnoh:).
Csix edtmfxmuwiav vaqwew zadd hozamn lje UQD am qqe muyymoalub pato ib
ddi firef mogo khtduc ogy zyi hirgen gerpocga:
Rin rcoq iweqfxxadx id om smuje, wao zoz ribx ijokrtzatq motuvliz: Alub wpi bada MawgPuaz.wquyw. Acy e jnixiq ojfcuhdo ux qvo hixjirxevle owweqr kkax letp be geryop wa itj jjo AykiwgeZais ehdqovkor:
private let persistence = Persistence()
Yxus, off cju veybonworro diboteqap, inz tozexu rqo iyFumBavbuzu cuxto
kyu potruq ib fva poow gohabar mza EGY egodepx:
ForEach(newsViewModel.news, id: \.url) { article in
ArticleView(article: article, persistence: persistence)
.listRowSeparator(.hidden)
.onTapGesture {
if let url = article.url {
openURL(url)
}
}
}
See forum comments
This content was released on Sep 20 2025. The official support period is 6-months
from this date.
Demo of the app improvements using SwiftUI background tasks.
Cinema mode
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.