SwiftUI Series 10|SwiftUI asynchronous loading: Task, async/await and interface status linkage
What's really hard is keeping the loading, results, errors, and page lifecycle consistent
Once many SwiftUI pages are loaded asynchronously, problems begin to increase:
onAppearwill repeat the request- The loading state is unstable
- Old results will change the current page back
- The task is still being written back after leaving the page
These problems may look like Task or async/await problems on the surface, but the more fundamental reasons are often:
The asynchronous task life cycle and the page state life cycle are not aligned.
So what we really have to deal with is:
- When does the request start?
- Will I be eligible to change the page after the results come back?
- When should old tasks expire?
- How are these states loading/error/content mutually exclusive?
1. The easiest pitfall is to regard page asynchronous as “one request + one Boolean value”
Many pages will initially be written like this:
- A
isLoading - A
items - A
errorMessage
Add another one:
Task {
await load()
}
Of course it will work at first, but as soon as the page starts supporting:
- try again
- Pull down to refresh
- Search
- Conditional switching
Problems will arise one after another. Because the real page is a set of tasks that may replace each other and cover each other.
2. The real core of asynchronous pages lies in the state flow
If you only understand asynchronous loading as “getting data back”, it will be easy to ignore that what the page really cares about is these states:
- Loading when first entering
- Refresh based on existing content
- Retry after failed loading
- Request after certain conditions change
These are both “loads”, but they are not the same thing in page semantics.
If everything is represented by just one isLoading, the page will soon become messy.
So a more practical idea is usually:
- First define what semantic state the page is currently in
- Let the asynchronous task drive this state flow change
3. onAppear + Task often becomes more dangerous the more you write it.
Because it’s so easy.
A common situation is to naturally write:
.onAppear {
Task {
await viewModel.load()
}
}
This code itself isn’t necessarily wrong, but the danger is that it’s too easy to default to:
- The page appears once
- Request to send once
- Change the status once after returning
In real projects, these three assumptions may not be true.
Therefore, the key to whether asynchronous loading is truly stable is not whether there is Task, but rather:
- Whether similar tasks are managed uniformly -Whether old tasks will be canceled or expired
- Whether the result needs to be verified against the current context
4. A common misunderstanding: if the result is successful, you will directly land on the page.
Many pages are in disordered status, and the root cause is here.
For example:
- Keywords have changed
- Old requests come back later
- Old results still change the page
From a request perspective it didn’t fail, but from a page perspective it expired.
So one very important thing in asynchronous pages is:
Not all successfully returned results are automatically eligible to write back the current UI.
Therefore, asynchronous loading of pages usually cannot only look at “whether the result is obtained”, but also must look at “whether the result is still valid now.”
5. A more stable direction: close the loading task instead of letting each UI event send its own request
If a page supports:
- initial load
- try again
- refresh
- Filter changes
The more stable approach is usually to group similar tasks together.
For example:
- All entries ultimately call the same
reload() - The currently active task is held by ViewModel
- When new tasks appear, old tasks are canceled or judged to be invalid.
This way the page state will at least be easier to keep consistent because:
- It is clear who is loading
- It is also clear who can end loading
6. Conclusion: What SwiftUI asynchronous loading really needs to deal with is whether the task and page status can be clearly explained.
To put it in shorter form, I would say:
When dealing with asynchronous loading in SwiftUI, the real difficulty is to keep the task life cycle, result validity and page state flow consistent.
As long as these three things are not established, the page will continue to grow:
- Repeat request
- Write back old results
- loading confusion
These problems cannot usually be fixed by writing a few more Task.
读完之后,下一步看什么
如果还想继续了解,可以从下面几个方向接着读。