# Crash reporting

> How Union collects crashes and hangs from your iOS app, groups them into issues, and computes a crash-free rate that never looks better than it is.

Source: https://useunion.dev/docs/crash-reporting · Updated 2026-10-23

Crash and hang reporting is on by default once `Union.configure` runs. A crash is written to disk when it happens and uploaded on the **next launch** — so it appears later, not immediately.

## Errors and breadcrumbs

```swift
Union.recordError("PaymentFailed", reason: "card_declined")
Union.leaveBreadcrumb("retrying_upload")
Union.setCrashKey("plan", "pro")
```

Record errors that matter, not expected control flow, and use a short code as the reason rather than an exception message that may contain user data. A breadcrumb is a name only, never a value. Up to 8 crash keys label every later report; they are refused in strict anonymous projects.

## Issues

Crashes are grouped by where they happened — the binary and offset of your own code’s frames — so uploading symbols later never splits or merges an issue. Without symbols an issue is named by its address, and grouped per build because addresses move with every rebuild. [Upload dSYMs](/docs/dsyms) from CI and Union names the frames.

An issue you close needs the build that fixed it. A crash from a newer build reopens it; one from an older build does not. An ignored issue never reopens.

## Crash-free rate

Because a crash arrives on the next launch, today’s count is almost always incomplete. Until late reports for a day have had time to arrive, Union shows the rate as **at most** a figure, without a change badge — a fresh release never reads as perfect by accident. The waiting time is learned from your own project’s upload delays.

## Turning it off

Crash reporting has two switches: the project setting in Union, and `Options.crashReporting` in the SDK. Turning off the project setting does not stop uploads — they are refused and recorded, so an empty Crashes view never reads as a stable app.
