> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apsio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload dSYMs

> Upload an iOS app's dSYMs with the apsio CLI so its crashes show function names, files and lines.

A crash report holds addresses. Apsio turns them into function names, files and lines with
the dSYMs of the build that crashed. See [Symbols and dSYMs](/concepts/symbols) for how
that works.

## Before you start

* Install the [`apsio` CLI](/cli) and store your project's
  [upload token](#upload-tokens) with `apsio login --with-token`, or set `APSIO_TOKEN`.
* Make sure the build produces dSYMs: in the target's Build Settings, Debug Information
  Format is "DWARF with dSYM File" (`dwarf-with-dsym`). New Xcode projects use it for
  Release.

## Upload

Point the CLI at an archive, a dSYM bundle or a folder:

```sh theme={null}
apsio upload dsyms Shop.xcarchive
apsio upload dsyms build/Shop.app.dSYM
apsio upload dsyms build/
```

The CLI:

1. finds every dSYM under the paths you give: in an `.xcarchive` it reads the `dSYMs`
   folder, and folders are searched recursively;
2. reads each one and keeps the Mach-O files that carry debug information;
3. splits a file with several architectures into one upload per architecture, since each
   has its own UUID, the one crash reports carry;
4. asks Apsio which UUIDs it already has, and uploads only the others, compressed.

It prints one line per file and a summary:

```text theme={null}
uploaded Shop (arm64, 6f0f5e2c-…)
1 uploaded, 3 already on the server, 0 failed
```

Uploading the same dSYMs again is cheap: files Apsio already has are skipped. `--force`
uploads them anyway.

## Check without uploading

```sh theme={null}
apsio upload dsyms build/ --dry-run
```

`--dry-run` finds and validates the dSYMs and lists their UUID, architecture, name and path,
without a token and without uploading.

## Output for scripts

```sh theme={null}
apsio upload dsyms Shop.xcarchive --json
```

`--json` prints one JSON document on standard output and nothing else there:

```json theme={null}
{
  "uploaded": [
    { "debug_id": "6f0f5e2c-3a1b-4c8e-9d2f-0a1b2c3d4e5f", "arch": "arm64", "name": "Shop", "path": "Shop.xcarchive/dSYMs/Shop.app.dSYM/Contents/Resources/DWARF/Shop" }
  ],
  "present": [],
  "found": [],
  "skipped": [],
  "failed": [],
  "dry_run": false
}
```

`found` is filled by `--dry-run`. `skipped` lists files that are not usable dSYMs, with the
reason. `failed` lists uploads that failed, with the error. See [exit codes](/cli#exit-codes).

## Upload tokens

Uploads authenticate with an upload token, which uploads symbols for one project and reads
nothing. It starts with `apsio_ut_v0.` and is separate from the [read API](/read-api)'s project
tokens: an upload token cannot read, and a project token cannot upload. Until the console
issues them, ask Apsio for one per project.

* **Lifetime:** 90 days by default, a year at most. Store it as a CI secret (`APSIO_TOKEN`) and
  replace it before it expires; an expired token is refused (exit code 3).
* **Scope:** the project it names. Every upload made with it belongs to that project.
* **Revocation:** a token that leaks is revoked by its id, and the next upload with it is
  refused.

<Note>
  When you run the Apsio service locally from its repository,
  `pnpm --filter @apsio/api token:upload --org <uuid> --project <uuid>` mints an upload token
  with the local `SYMBOL_TOKEN_SECRET` (`.env.example`).
</Note>

## Where to find dSYMs

| Build | dSYMs |
| - | - |
| An archive from Xcode | `~/Library/Developer/Xcode/Archives/<date>/<name>.xcarchive` |
| `xcodebuild archive -archivePath Shop.xcarchive` | `Shop.xcarchive` |
| A build in Xcode | The build phase finds them for you: see [Xcode build phase](/symbols/xcode) |

Upload the dSYMs of every build you ship: the App Store, TestFlight and ad hoc builds.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.