mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-07 12:58:39 +00:00
Add Swift Concurrency Pro skill to the project. (#5662)
Installed for Copilot with `gh skill install` and manually created a symlink for Claude to access all Copilot skills.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# Unstructured concurrency
|
||||
|
||||
## Task vs `Task.detached`
|
||||
|
||||
You should already know that `Task {}` inherits the caller's actor isolation, whereas `Task.detached {}` does not.
|
||||
|
||||
```swift
|
||||
@MainActor
|
||||
func example() {
|
||||
Task {
|
||||
// Still on MainActor; safe to update UI here.
|
||||
label.text = "Done"
|
||||
}
|
||||
|
||||
Task.detached {
|
||||
// Not on MainActor; updating UI here is a bug.
|
||||
// Use this for genuinely independent background work.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
However, what you are less likely to know is this: `Task.detached` is rarely the right choice.
|
||||
|
||||
Prefer `Task {}` with explicit isolation changes, or structured concurrency. Only use `Task.detached` when you specifically need to shed the caller's actor context and priority, and even then only if there are no better choices.
|
||||
|
||||
|
||||
## Cancellation is cooperative
|
||||
|
||||
Always remember that cancelling a task does not stop its code – the task's body must check for cancellation explicitly.
|
||||
|
||||
```swift
|
||||
func processItems(_ items: [Item]) async throws {
|
||||
for item in items {
|
||||
// Check before expensive work
|
||||
try Task.checkCancellation()
|
||||
await process(item)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `Task.checkCancellation()` throws `CancellationError` if cancelled.
|
||||
- `Task.isCancelled` returns a Bool for non-throwing contexts.
|
||||
- `task.cancel()` only sets the flag – it does not interrupt execution.
|
||||
|
||||
This means it’s important to ensure complex tasks regularly check for cancellation at safe intervals.
|
||||
|
||||
For legacy APIs that offer their own cancel mechanism, use `withTaskCancellationHandler` to bridge Swift's cooperative cancellation to the underlying API. See `cancellation.md` for details and examples.
|
||||
|
||||
|
||||
## `Task.immediate` (Swift 6.2)
|
||||
|
||||
For `Task.immediate` details, see `new-features.md`. For most cases, regular `Task {}` is still the right choice.
|
||||
|
||||
|
||||
## When `Task {}` is a code smell
|
||||
|
||||
Creating a `Task {}` to call an async function from a synchronous context is sometimes necessary (e.g., in a button action). But watch for these anti-patterns:
|
||||
|
||||
- **Task inside `onAppear()`**: Never create a `Task` inside a SwiftUI `onAppear()`. Use the `.task()` modifier instead, because it handles cancellation on disappear automatically.
|
||||
- **Task to bridge sync → async in a function that could itself be async**: If the caller can be made async, do that instead of wrapping in `Task {}`.
|
||||
- **Ignoring the return value of a throwing task**: The error is silently lost. At minimum, handle errors inside the task closure.
|
||||
Reference in New Issue
Block a user