|
| 1 | +# 0084. iOS Sentry dSYM & Dart Symbols Automation |
| 2 | + |
| 3 | +Date: 2026-04-20 |
| 4 | + |
| 5 | +## Status |
| 6 | + |
| 7 | +Accepted |
| 8 | + |
| 9 | +## Context |
| 10 | + |
| 11 | +iOS crash reports on Sentry are unreadable due to two levels of obfuscation: |
| 12 | + |
| 13 | +1. **Native (Swift/Objective-C/C++):** Xcode strips debug symbols from the release binary — only raw memory addresses are shown instead of function names and line numbers. |
| 14 | +2. **Dart:** ARM binaries lack debug info — only raw memory addresses are shown instead of Flutter stack traces. |
| 15 | + |
| 16 | +This makes production crashes nearly impossible to debug. |
| 17 | + |
| 18 | +## Decision |
| 19 | + |
| 20 | +Update the iOS Fastlane build and CI/CD pipeline to automatically generate and upload de-obfuscation artifacts to Sentry on every release. |
| 21 | + |
| 22 | +### Build (Fastlane — `ios/fastlane/Fastfile`) |
| 23 | + |
| 24 | +Pass Flutter obfuscation build settings to `build_app` via `xcargs`. Flutter's `xcode_backend.sh` reads these Xcode build settings during the build phase: |
| 25 | + |
| 26 | +```ruby |
| 27 | +symbols_path = File.expand_path("../build/ios/outputs/symbols") |
| 28 | +build_app( |
| 29 | + ... |
| 30 | + xcargs: "DART_OBFUSCATION=true SPLIT_DEBUG_INFO=#{symbols_path}", |
| 31 | + dsym_output_path: "../build/ios/outputs/dsym" |
| 32 | +) |
| 33 | +``` |
| 34 | + |
| 35 | +- `DART_OBFUSCATION=true` — instructs Flutter to obfuscate Dart code (value must be lowercase `true`, not `YES`). |
| 36 | +- `SPLIT_DEBUG_INFO=<abs_path>` — strips Dart debug symbols from the binary into `build/ios/outputs/symbols/`. |
| 37 | +- `dsym_output_path` — instructs Gym (Fastlane) to export the Xcode-generated dSYM to `build/ios/outputs/dsym/`. |
| 38 | + |
| 39 | +### Pod dependency (`ios/Podfile`) |
| 40 | + |
| 41 | +The `TwakeMailNSE` (Notification Service Extension) target must use `Sentry/HybridSDK` at the **exact same version** as `sentry_flutter` requires. Using plain `pod 'Sentry'` (Core subspec, pulls latest) causes a version conflict and `PrivateSentrySDKOnly not found` compile errors. |
| 42 | + |
| 43 | +```ruby |
| 44 | +target 'TwakeMailNSE' do |
| 45 | + use_frameworks! |
| 46 | + pod 'Sentry/HybridSDK', '8.56.2' # must match sentry_flutter's requirement |
| 47 | +end |
| 48 | +``` |
| 49 | + |
| 50 | +### CI Pipeline (GitHub Actions — `.github/workflows/release.yaml`) |
| 51 | + |
| 52 | +The two separate Sentry upload steps (iOS and Android) are merged into a single **"Upload Debug Symbols to Sentry"** step that runs on both matrix jobs independently. The step branches internally based on `${{ matrix.os }}`. |
| 53 | + |
| 54 | +After a successful build, the step: |
| 55 | + |
| 56 | +1. Validates that the git tag version matches the version in `pubspec.yaml`. |
| 57 | +2. Creates a Sentry release. |
| 58 | +3. For **iOS**: |
| 59 | + - Uploads `build/ios/outputs/dsym/` (dSYM — de-obfuscates native Swift/ObjC/C++ crashes). |
| 60 | + - Uploads `build/ios/outputs/symbols/` (Dart debug symbols — de-obfuscates Flutter stack traces). |
| 61 | +4. For **Android**: |
| 62 | + - Uploads `build/app/outputs/mapping/release/mapping.txt` (ProGuard — de-obfuscates Java/Kotlin). |
| 63 | + - Uploads `build/app/outputs/symbols/` (Dart debug symbols). |
| 64 | +5. Finalizes the release. |
| 65 | + |
| 66 | +Independence between platforms is guaranteed by the matrix strategy (`fail-fast: false`) — iOS and Android run on separate runners, so a failure on one does not affect the other. |
| 67 | + |
| 68 | +## Consequences |
| 69 | + |
| 70 | +**Benefits:** |
| 71 | +- Full readable stack traces for both Dart and native (Swift/ObjC/C++) crashes in Sentry. |
| 72 | +- Automated — no manual upload step needed. |
| 73 | +- Smaller app binary (debug info stripped from the IPA). |
| 74 | +- Single unified Sentry upload step for both platforms — easier to maintain. |
| 75 | + |
| 76 | +**Trade-offs:** |
| 77 | +- Slightly longer CI build time (symbol separation + upload). |
| 78 | +- `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, `SENTRY_PROJECT` must be maintained as CI secrets. |
| 79 | +- `pod 'Sentry/HybridSDK'` version in `Podfile` must be kept in sync manually when upgrading `sentry_flutter`. |
| 80 | +- Symbol files must be uploaded in the **same workflow run** as the build — running `flutter clean` between build and upload will break de-obfuscation. |
0 commit comments