# Long Updates and Frequent Updates

Based on Apple's article "Understanding and improving SwiftUI performance".

## Two Different Problems

| Kind | What happens | Usual sources |
|------|--------------|---------------|
| Long updates | One update takes too long | An expensive `body`, layout, or hosted platform views |
| Frequent updates | Many small updates add up | Dependency fan-out, noisy observable state, geometry or timer signals invalidating too much |

Both hurt, and they need different fixes. Treat them as separate bugs.

## Instruments Workflow

1. Build Release and run on a real device.
2. Profile with the SwiftUI template.
3. Repeat one interaction several times.
4. Read the SwiftUI track first, then line it up with Time Profiler.
5. Change one thing, record again.

A trace from an interaction you cannot reproduce on demand is much harder to
interpret.

## The Lanes

- **Update Groups**: batches of SwiftUI work.
- **Long View Body Updates**: slow `body` evaluations.
- **Long Platform View Updates**: slow hosted views. Check representables,
  `List` rows that embed platform views, and media, map or web components.
- **Other Long Updates**: layout, geometry and update coordination.
- **Hitches**: missed frames. A hitch is a symptom; its cause may be elsewhere.

## Diagnosing Long Updates

Begin with the red or orange update, not with total runtime. Set the
inspection range to it and look at the hottest frames in Time Profiler.

Typical findings:
- formatting in `body`,
- array transforms in `body`,
- image decoding while scrolling,
- representables doing too much on every update pass.

```swift
// Formats on every evaluation.
Text(order.total, format: .currency(code: order.currency))

// Reads a value the model prepared when the total changed.
Text(checkout.totalText)
```

The lasting fix is usually architectural: decide where derived values live.

## Diagnosing Frequent Updates

This looks like a screen that keeps waking up. Watch Update Groups together
with the Cause and Effect Graph.

Noisy dependencies to look for:
- a timer injected high in the tree,
- geometry stored in a shared model,
- one broad `@Observable` object at the root,
- environment values that change often.

Reduce the fan-out:
- split models by domain,
- move fast-changing state further down,
- hand children narrow derived values,
- apply a threshold to continuous values before storing them.

## Remediation

- Precompute display values such as strings and sorted arrays.
- Rows depend on row state, not on screen state.
- Representables update idempotently.
- Avoid feedback loops where geometry updates state that changes layout that
  changes geometry.
- Keep lists identity-friendly: stable ids, data filtered beforehand, a row
  count that does not fluctuate.

## Verification

Record the same interaction again and compare update counts, how often hitches
occur, the lane you were targeting, and the behaviour of the product. Fixes can
shift animation timing, placeholders or navigation, so check the experience as
well as the numbers.

## Guardrails

- Keep work out of `body`.
- Keep dependency surfaces small.
- Cache display data close to the model.
- No hidden allocations per row.
- Profile Release builds.
- Long updates and frequent updates are distinct bugs.

If the screen is still slow after `body` is clean, look at dependency fan-out
next.
