---
title: "Build Distribution"
description: "Upload iOS builds to Sentry for distribution to internal teams and beta testers."
url: https://docs.sentry.io/platforms/apple/guides/ios/build-distribution/
---

# Build Distribution | Sentry for iOS

This feature is available only if you're in the [Early Adopter program](https://docs.sentry.io/organization/early-adopter-features.md). Features available to Early Adopters are still in-progress and may have bugs. We recognize the irony.

[Build Distribution](https://docs.sentry.io/product/build-distribution.md) helps you securely distribute iOS builds to your internal teams and beta testers. Streamline your distribution workflow with automated uploads from CI.

## [Getting Started](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#getting-started)

**Accepted Formats**: XCArchive

**Upload Mechanisms**: [Fastlane Plugin](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#uploading-with-fastlane) (version 1.35.0 or higher) *or* [Sentry CLI](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#uploading-with-the-sentry-cli)

### [Uploading With Fastlane](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#uploading-with-fastlane)

The Fastlane plugin can be used to upload XCArchive or IPA builds to Sentry. On GitHub Actions, Fastlane will automatically detect your [build's metadata](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#upload-metadata) and include it in the upload. In other Continuous Integration (CI) environments, you may need to manually set metadata values.

Uploading IPA files requires Sentry Fastlane plugin version `2.0.0-rc.3` or later. XCArchive uploads are supported in earlier versions.

1. Configure the [Sentry Fastlane plugin](https://docs.sentry.io/platforms/apple/guides/ios/dsym.md#sentry-fastlane-plugin) (version `2.5.1`):

   ```ruby
   bundle exec fastlane add_plugin fastlane-plugin-sentry
   ```

2. Set up `SENTRY_AUTH_TOKEN` in your environment (you can generate a token [here](https://sentry.sentry.io/settings/auth-tokens/))

3. In `FastFile`, add a call to `sentry_upload_build` after your build step.

   **Uploading an XCArchive (preferred):**

   When using `build_ios_app`, the XCArchive path is automatically detected from `SharedValues::XCODEBUILD_ARCHIVE`:

   `Fastfile`

   ```ruby
   lane :upload_to_sentry do
     build_ios_app(
       scheme: 'YourScheme',
       configuration: 'Release'
     )
     sentry_upload_build(
       org_slug: 'your-org',
       project_slug: 'your-project',
       build_configuration: 'Release'
     )
   end
   ```

   You can also explicitly specify the `xcarchive_path`:

   `Fastfile`

   ```ruby
   lane :upload_to_sentry do
     sentry_upload_build(
       org_slug: 'your-org',
       project_slug: 'your-project',
       xcarchive_path: 'path/to/YourApp.xcarchive',
       build_configuration: 'Release'
     )
   end
   ```

   **Uploading an IPA:**

   When using `build_ios_app`, the IPA path is automatically detected from `SharedValues::IPA_OUTPUT_PATH`.

   `Fastfile`

   ```ruby
   lane :upload_to_sentry do
     build_ios_app(
       scheme: 'YourScheme',
       configuration: 'Release'
     )
     sentry_upload_build(
       org_slug: 'your-org',
       project_slug: 'your-project',
       build_configuration: 'Release'
     )
   end
   ```

   To upload an IPA instead of the XCArchive, explicitly specify `ipa_path`:

   `Fastfile`

   ```ruby
   lane :upload_to_sentry do
     build_ios_app(
       scheme: 'YourScheme',
       configuration: 'Release'
     )
     sentry_upload_build(
       org_slug: 'your-org',
       project_slug: 'your-project',
       ipa_path: 'path/to/YourApp.ipa',
       build_configuration: 'Release'
     )
   end
   ```

4. After an upload has successfully processed, confirm the metadata is correct in the Sentry UI

The Fastlane plugin automatically detects the following build metadata. If needed, the metadata values can be overridden by passing parameters to `sentry_upload_build`:

`Fastfile`

```ruby
sentry_upload_build(
  org_slug: 'your-org',
  project_slug: 'your-project',
  xcarchive_path: 'path/to/YourApp.xcarchive', # or ipa_path: 'path/to/YourApp.ipa'
  build_configuration: 'Release',
  # Optional metadata overrides:
  head_sha: 'abc123',
  base_sha: 'def456',
  vcs_provider: 'github',
  head_repo_name: 'organization/repository',
  base_repo_name: 'organization/repository',
  head_ref: 'feature-branch',
  base_ref: 'main',
  pr_number: '42'
)
```

See the [Fastlane repo](https://github.com/getsentry/sentry-fastlane-plugin) for more information.

### [Uploading with the Sentry CLI](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#uploading-with-the-sentry-cli)

1. Install the [sentry-cli](https://docs.sentry.io/cli.md) (version `3.3.5`)

   We recommend using the latest version for the best possible experience, but at a minimum version `2.58.2` is required.

2. Authenticate the Sentry CLI by [following these steps](https://docs.sentry.io/cli/configuration.md#to-authenticate-manually)

3. Build your app to create an XCArchive (preferred) or IPA

4. Invoke the following CLI command to trigger the upload:

   ```bash
   sentry-cli build upload app.xcarchive \
     --org your-org \
     --project your-project \
     --build-configuration Release
   ```

5. After an upload has successfully processed, confirm the metadata is correct in the Sentry UI

## [Downloading Builds](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#downloading-builds)

Once builds are uploaded to Sentry, your team members and beta testers can download them through the Sentry web interface.

1. Open the URL printed to the console after uploading the build
2. Click the **Install** button on the right side of the page

3) Either scan the QR code from a mobile device or click the **Download** button to download the build directly

After downloading, you can install the build directly on your iOS device. For more information on installing enterprise apps, see [Apple's documentation on installing custom apps](https://support.apple.com/en-us/118254).

## [Upload Metadata](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#upload-metadata)

We use build metadata to organize builds in the UI and ensure correct comparisons.

| Field                   | Description                                                                                                                                                                                                                                              |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org`\*                 | Sentry organization slug                                                                                                                                                                                                                                 |
| `project`\*             | Sentry project slug                                                                                                                                                                                                                                      |
| `build-configuration`\* | Build configuration describing how the app was built, for example `Release` or `Debug` or `Release-Bazel`                                                                                                                                                |
| `head-sha`              | Current commit SHA                                                                                                                                                                                                                                       |
| `base-sha`              | Base commit SHA (for comparisons, recommended to use the branch's merge-base)                                                                                                                                                                            |
| `vcs-provider`          | VCS provider name (for example `github`, `gitlab`, `bitbucket`, `azure`, `github_enterprise`). If not provided, the provider will be auto-detected from the git remote URL. Note: Only `github` and `github_enterprise` are supported for status checks. |
| `head-repo-name`        | Repository name (`org/repo`)                                                                                                                                                                                                                             |
| `pr-number`             | Pull request number                                                                                                                                                                                                                                      |
| `head-ref`              | Branch or tag name                                                                                                                                                                                                                                       |
| `base-ref`              | Base branch name                                                                                                                                                                                                                                         |
| `install-group`         | [Install group(s)](https://docs.sentry.io/product/build-distribution.md#install-groups) to control update visibility between builds. Can be specified multiple times                                                                                     |

\* *required field*

### [Build Configuration](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#build-configuration)

Features such as automatically comparing the head build against the base build **will only compare builds of the same build configuration**. This is important to consider when setting up Size Analysis in your CI. For example, `Release` and `Debug` builds can be drastically different depending on the compiler and linker settings used during the build process. Trying to compare the two would give unexpected results.

## [Uploading Best Practices](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#uploading-best-practices)

### [Strip Swift AST](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution.md#strip-swift-ast)

For optimal processing speeds, strip `__swift_ast` from your dSYMs before uploading to Sentry. This section of the binary is a copy of your code's swiftmodule file and is only used for runtime debugging with LLDB. It is not required for symbolication, Size Analysis, or Build Distribution.

The script below can be used to strip `__swift_ast` from your dSYMs. The xcarchive is modified in-place. Back up first if needed.

**Prerequisites**: Xcode command line tools (otool, lipo), Python 3

```bash
#!/bin/bash
set -euo pipefail

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <path-to-xcarchive>" >&2
  exit 1
fi

XCARCHIVE="$1"
DSYMS_DIR="$XCARCHIVE/dSYMs"

if [[ ! -d "$DSYMS_DIR" ]]; then
  echo "Error: No dSYMs directory found at $DSYMS_DIR" >&2
  exit 1
fi

TOTAL_ZEROED=0
STRIPPED_COUNT=0

# Zero out __swift_ast in a single-arch Mach-O binary.
# Prints the number of bytes zeroed (0 if section not found).
strip_thin_binary() {
  local binary="$1"

  local ast_info
  ast_info=$(xcrun otool -l "$binary" 2>/dev/null | awk '
    /sectname __swift_ast/ { found = 1 }
    found && /^[[:space:]]+size / {
      cmd = "printf \"%d\" " $2
      cmd | getline dec_size
      close(cmd)
    }
    found && /^[[:space:]]+offset / {
      print dec_size " " $2
      found = 0
    }
  ')

  if [[ -z "$ast_info" ]]; then
    echo "0"
    return
  fi

  local ast_size ast_offset
  ast_size=$(echo "$ast_info" | awk '{print $1}')
  ast_offset=$(echo "$ast_info" | awk '{print $2}')

  if [[ "$ast_size" -eq 0 ]]; then
    echo "0"
    return
  fi

  # Skip if already zeroed
  local already_zeroed
  already_zeroed=$(/usr/bin/python3 -c "
import sys
with open(sys.argv[1], 'rb') as f:
    f.seek(int(sys.argv[2]))
    print('1' if f.read(int(sys.argv[3])) == b'\0' * int(sys.argv[3]) else '0')
" "$binary" "$ast_offset" "$ast_size")

  if [[ "$already_zeroed" == "1" ]]; then
    echo "0"
    return
  fi

  chmod u+w "$binary" 2>/dev/null || true
  /usr/bin/python3 -c "
import sys
with open(sys.argv[1], 'r+b') as f:
    f.seek(int(sys.argv[2]))
    f.write(b'\0' * int(sys.argv[3]))
" "$binary" "$ast_offset" "$ast_size"

  echo "$ast_size"
}

# Handle both fat (multi-arch) and thin (single-arch) binaries.
strip_binary() {
  local binary="$1"

  local lipo_info
  lipo_info=$(xcrun lipo -info "$binary" 2>/dev/null) || true

  if echo "$lipo_info" | grep -q "Architectures in the fat file"; then
    local archs
    archs=$(echo "$lipo_info" | sed 's/.*: //')

    local tmp_dir
    tmp_dir=$(mktemp -d)
    local thin_files=()
    local saved=0

    for arch in $archs; do
      local thin_file="$tmp_dir/$arch"
      xcrun lipo "$binary" -thin "$arch" -output "$thin_file"

      local bytes_str
      bytes_str=$(strip_thin_binary "$thin_file")
      saved=$((saved + bytes_str))

      thin_files+=("$thin_file")
    done

    if [[ $saved -gt 0 ]]; then
      chmod u+w "$binary" 2>/dev/null || true
      xcrun lipo -create "${thin_files[@]}" -output "$binary"
    fi

    rm -rf "$tmp_dir"
    echo "$saved"
  else
    strip_thin_binary "$binary"
  fi
}

echo "Zeroing __swift_ast in dSYMs in: $DSYMS_DIR"
echo ""

while IFS= read -r dsym_bundle; do
  dsym_name=$(basename "$dsym_bundle")
  dwarf_dir="$dsym_bundle/Contents/Resources/DWARF"

  if [[ ! -d "$dwarf_dir" ]]; then
    continue
  fi

  for binary in "$dwarf_dir"/*; do
    [[ -f "$binary" ]] || continue

    zeroed=$(strip_binary "$binary")
    if [[ "$zeroed" -gt 0 ]]; then
      zeroed_mb=$(echo "scale=1; $zeroed / 1048576" | bc)
      echo "  $dsym_name — zeroed ${zeroed_mb} MB"
      TOTAL_ZEROED=$((TOTAL_ZEROED + zeroed))
      STRIPPED_COUNT=$((STRIPPED_COUNT + 1))
    fi
  done
done < <(find "$DSYMS_DIR" -name "*.dSYM" -type d)

TOTAL_MB=$(echo "scale=1; $TOTAL_ZEROED / 1000000" | bc)
echo ""
echo "Done. Processed $STRIPPED_COUNT dSYM(s), zeroed ${TOTAL_MB} MB of __swift_ast data."
```

* #### [Auto-Update SDK](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution/auto-update.md)

  Enable automatic update checks and installations for internal iOS builds using the Sentry Auto-Update SDK.

* #### [Install Groups](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution/install-groups.md)

  Control which iOS builds can see updates for each other using install groups.

## Pages in this section

- [Auto-Update SDK](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution/auto-update.md)
- [Install Groups](https://docs.sentry.io/platforms/apple/guides/ios/build-distribution/install-groups.md)
