Ana Sayfa / Blog / Crash symbolication: doğru kurmak için .dSYM yönetimi

Crash symbolication: doğru kurmak için .dSYM yönetimi

Crashlytics'te crash gördün ama stack trace hex address'ler. Niye? .dSYM file'ları eksik. Doğru upload disiplini.

Firebase Crashlytics’e (veya Sentry, Bugsnag) crash geldiğinde iki possibility var:

Symbolicated: UserProfileView.updateName() at UserProfileView.swift:142 – okunabilir, actionable.

Unsymbolicated: veya hex address’ler. Tamamen useless.

Farkı: .dSYM (debug symbol) file’ları crash reporter’a doğru upload edilmiş mi? 12 uygulamamda bu disiplin zamanımızı 100x hızlandırdı. Bu yazıda doğru .dSYM workflow’unu anlatacağım.

.dSYM nedir?

Release build yaparken Xcode iki file üretiyor:

  1. App binary (.ipa): Compiled machine code. Symbol’ler stripped (debug info removed, app küçük).
  2. dSYM bundle (.dSYM): Symbol database. Hex address’i function name + line number’a çeviriyor.

Crash reporter crash’i yakaladığında stack trace hex address’ler olarak geliyor (symbol’ler app’te yok). dSYM ile bu adresler sembol adlarına resolve ediliyor.

Doğru .dSYM olmadan crash report okunmuyor. Firebase hex address gösteriyor, “we need dSYM for this build” warning’i çıkıyor.

Upload workflow: 3 variant

.dSYM’i crash reporter’a upload etmenin 3 yolu:

1. Archive sonrası Xcode manual upload:

  • Product > Archive
  • Organizer açılıyor
  • “Download Debug Symbols” (Apple’ın bitcode yaptığı versiyonlar için)
  • Third-party crash reporter’ın dSYM upload dialog’unu aç

Manuel. Unuttuğunda crash symbolication kayboluyor.

2. Fastlane + run script:

# Fastfile
lane :deploy do
  gym(scheme: "MyApp")
  upload_to_testflight
  upload_symbols_to_crashlytics(
    dsym_path: lane_context[SharedValues::DSYM_OUTPUT_PATH],
    gsp_path: "./GoogleService-Info.plist"
  )
end

Fastlane ile otomasyon. upload_symbols_to_crashlytics action Crashlytics için hazır.

3. Build phase script (in-Xcode):

Xcode project’in Build Phases section’ına new Run Script ekleyin:

# Crashlytics dSYM upload
"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" -gsp "${PROJECT_DIR}/GoogleService-Info.plist" -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"

Bu script her build’de otomatik çalışıyor. Ama debug build’lerde istemezsin (unnecessary upload). Condition:

if [ "$CONFIGURATION" = "Release" ]; then
    # upload script
fi

Bitcode complication (iOS 16 öncesi)

Apple Bitcode ile app binary’yi Apple’ın re-compile etmesine izin veriyordu. Upload edilen dSYM’in, Apple’ın re-compile sonrası dSYM’i ile farklı olabiliyordu.

Çözüm: Apple’ın yeni dSYM’ini indirip upload etmek. Xcode Organizer’da “Download Debug Symbols” butonu.

Ama iOS 16’da Bitcode deprecated. Yeni projelerde disable. Eski projeler upgrade ederse bu complication kalkıyor.

Missing dSYM detection

Crashlytics dashboard’unda “Missing dSYM” section’ı var. Crash geldi ama ilgili build için dSYM upload edilmemiş durumlar.

List’te build version’lar görünüyor: “v2.3.1 (build 47) – 18 crashes, no dSYM”.

Bu list bir uyarı. Weekly check ediyorum:

  • Build date 7 gün’den yeni ise: upload script’imde problem var
  • Build eski ise: historical missing, workaround üret

Workaround historical missing için: eski archive’den dSYM’i çıkar, manually upload.

App Store Connect API integration

App Store Connect yeni dSYM’leri TestFlight/Store release’den sonra generate ediyor. Bu dSYM’leri otomatik Crashlytics’e aktarmak için Fastlane’in download_dsyms + upload_symbols_to_crashlytics action’ları:

lane :refresh_dsyms do
  download_dsyms(version: "latest")
  upload_symbols_to_crashlytics(gsp_path: "./GoogleService-Info.plist")
  clean_build_artifacts
end

Bu lane’i nightly cron’da çalıştır. Apple Apple Store Connect’ten latest dSYM indiriyor, Crashlytics’e yüklüyor, Apple tarafındaki re-compile’den dolayı farklı olabilir dSYM’leri handle ediyor.

Multi-build scenarios

Birden fazla build variant (staging, production, etc):

lane :deploy_staging do
  gym(configuration: "Staging")
  upload_symbols_to_crashlytics(
    dsym_path: lane_context[SharedValues::DSYM_OUTPUT_PATH],
    gsp_path: "./Staging/GoogleService-Info.plist"  # Staging Firebase project
  )
end

lane :deploy_production do
  gym(configuration: "Release")
  upload_symbols_to_crashlytics(
    dsym_path: lane_context[SharedValues::DSYM_OUTPUT_PATH],
    gsp_path: "./Production/GoogleService-Info.plist"  # Production Firebase project
  )
end

Her Firebase project için ayrı GoogleService-Info.plist.

Alternative: Sentry

Sentry dSYM upload flow biraz farklı:

lane :deploy do
  gym
  sentry_upload_dif(
    auth_token: ENV['SENTRY_AUTH_TOKEN'],
    org_slug: 'my-org',
    project_slug: 'my-app',
    path: lane_context[SharedValues::DSYM_OUTPUT_PATH]
  )
end

Sentry’nin sentry-cli tool’u da var: sentry-cli upload-dif ./path/to/dSYM. CI/CD pipeline’larda script olarak çağrılıyor.

Upload disciplini

Gerçek hayatta çalışıyor olan workflow:

  1. CI/CD pipeline’da automatic. Her release build’den sonra otomatik upload. Manuel step yok.
  2. Verification. Crashlytics / Sentry dashboard’da “last upload: 2 hours ago”. Build freshness check.
  3. Weekly audit. Missing dSYM list’i scan. Historical upload gerekirse yap.
  4. Alerting. CI/CD dSYM upload fail etse bile release çıkıyor. Slack alert: “dSYM upload failed for v2.3.1”.

Bu 4 step’le symbolication issue’ları önleniyor.

dSYM storage: ne kadar saklamalı?

.dSYM file’ları büyük (100MB-500MB her build için). Crashlytics/Sentry kendi storage’ında tutuyor. Local’de de saklamak lazım mı?

Evet. Yıllar sonra eski build’ler için crash report çıkabilir. dSYM’i kaybettiyse hex address’leri resolve etmen imkansız.

Solution:

  • Xcode Archives folder (~/Library/Developer/Xcode/Archives/) manually backup
  • S3 bucket’a automatic upload: Fastlane upload_to_s3 action
  • Git LFS eğer dSYM’leri kod repo’suyla versionlamak istersen (dikkat: size büyük)

Dentii için ben S3’e saklıyorum: s3://my-dsyms/dentii/v2.3.1-build47.dSYM.zip.

Performance impact

dSYM upload’ının build time’a etkisi:

  • Upload file size: 100-500MB per build
  • Upload time (internet dependent): 30 saniye – 5 dakika

CI/CD pipeline’da 1-2 dakika ek time. Parallel yapmak mantıklı (build + upload concurrent).

Troubleshooting: symbolication çalışmıyor

Crashes unsymbolicated kalıyor, ne yapayım:

Check 1: Build UUID matches?

dSYM ve app binary aynı UUID’ye sahip olmalı. Xcode’da dwarfdump --uuid /path/to/app ve dwarfdump --uuid /path/to/dSYM. İki UUID aynı mı?

UUID farklıysa wrong dSYM yüklenmiş demektir.

Check 2: Architecture match?

dSYM arm64, arm64e, x86_64 için ayrı. App Store build arm64, simulator x86_64. Çift dSYM gerekiyorsa ikisi de upload edilsin.

Check 3: Upload success?

Crashlytics/Sentry dashboard’da “dSYMs uploaded” log’unu check. Error varsa log’da.

Check 4: Wait time?

Crashlytics upload sonrası processing 2-4 saat sürebiliyor. Hemen resolve olmuyor.

Sonuç

.dSYM upload disiplini iOS developer’ın en çok atladığı konu. Crash geliyor, debug edemiyor, zaman kaybediyor.

Fastlane + CI/CD automation bu disiplini otomatize ediyor. Weekly audit missing dSYM’leri yakalıyor. Local backup yıllar sonra da resolve edebilmeyi sağlıyor.

1-2 saatlik setup investment, her crash report’ın actionable olmasını garanti ediyor. iOS app’te bu workflow yoksa production’a gitmiş demek değil.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç