Crashes Pre-Beta — Mapa de prioridad
Análisis| Error | Plataforma | Módulo | Frecuencia | Severidad |
|---|---|---|---|---|
OutOfMemoryError |
Android | FoodListFragment → Firestore | ~1 de cada 12 cargas | Critical |
EXC_BAD_ACCESS |
iOS | CameraScanner → AVFoundation | Reproducible en iPhone 13 mini | Critical |
NullPointerException |
Android | UserProfileViewModel | Cold start ocasional | Medium |
Android — Configuración Gradle (Kotlin DSL)
Androidgoogle-services.json en app/ antes de proceder. Obtenerlo con: npx firebase-tools apps:sdkconfig ANDROID <App-ID>// NutriTrack — build.gradle.kts (project level) plugins { // ... plugins existentes id("com.google.gms.google-services") version "4.4.2" apply false id("com.google.firebase.crashlytics") version "3.0.3" apply false }
// NutriTrack — app/build.gradle.kts plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("com.google.gms.google-services") // Google Services id("com.google.firebase.crashlytics") // ← Crashlytics plugin } dependencies { // Firebase BoM — gestiona versiones automáticamente implementation(platform("com.google.firebase:firebase-bom:33.7.0")) // Crashlytics + Analytics (recomendado conjunto) implementation("com.google.firebase:firebase-crashlytics-ktx") implementation("com.google.firebase:firebase-analytics-ktx") }
firebase-crashlytics-ndk y activa nativeSymbolUploadEnabled = true en el buildType release para simbolizar los crash reports nativos de Firestore.import com.google.firebase.crashlytics.FirebaseCrashlytics class FoodListFragment : Fragment() { private fun loadFoodList() { val crashlytics = FirebaseCrashlytics.getInstance() // Context personalizado antes de operaciones de riesgo crashlytics.setCustomKey("food_list_page", currentPage) crashlytics.setCustomKey("user_plan", "premium") crashlytics.setCustomKey("firestore_collection", "alimentos_es") crashlytics.log("Iniciando carga de alimentos — página $currentPage") // Identificar usuario en el reporte crashlytics.setUserId(viewModel.currentUserId) db.collection("alimentos_es") .limit(50) .get() .addOnFailureListener { exception -> // Reportar excepción no-fatal con contexto crashlytics.recordException(exception) showErrorState() } } }
iOS — Swift Package Manager + dSYM Upload
iOS SwiftUIRegistrar la app iOS en Firebase
Bundle ID: com.cultivaia.nutritrack — crea el registro y descarga GoogleService-Info.plist, añádelo al Xcode target.
Instalar SDK via Swift Package Manager
Repo: https://github.com/firebase/firebase-ios-sdk.git — añade el producto FirebaseCrashlytics
Configurar Debug Information Format
Build Settings → DWARF with dSYM File en Debug y Release. Requerido para simbolizar EXC_BAD_ACCESS.
Añadir Run Script Phase (dSYM upload)
Build Phases → New Run Script Phase con el script de upload de Crashlytics.
import SwiftUI import FirebaseCore import FirebaseCrashlytics @main struct NutriTrackApp: App { init() { FirebaseApp.configure() // Habilitar recolección (por defecto true en release) Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true) } var body: some Scene { WindowGroup { ContentView() } } }
import AVFoundation import FirebaseCrashlytics class CameraScannerViewModel: ObservableObject { func startCapture(deviceModel: String) { let crashlytics = Crashlytics.crashlytics() // Keys de contexto para reproducir EXC_BAD_ACCESS crashlytics.setCustomValue(deviceModel, forKey: "device_model") crashlytics.setCustomValue(UIDevice.current.systemVersion, forKey: "ios_version") crashlytics.setCustomValue("barcode_scanner", forKey: "active_feature") crashlytics.log("Iniciando AVCaptureSession — dispositivo: \(deviceModel)") do { let session = AVCaptureSession() // ... setup cámara } catch { // Non-fatal: fallo al acceder a cámara crashlytics.record(error: error) showCameraPermissionAlert() } } func setCurrentUser(_ userId: String, plan: String) { Crashlytics.crashlytics().setUserID(userId) Crashlytics.crashlytics().setCustomValue(plan, forKey: "subscription_plan") } }
# Añadir en Build Phases → New Run Script Phase ${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run # Input Files: # $(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH) # $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(TARGET_NAME)
Verificación — Forzar Test Crash
RequeridoANDROID — Test crash
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ⚠️ SOLO PARA TEST — eliminar Handler(Looper.getMainLooper()) .postDelayed({ throw RuntimeException("NutriTrack Test Crash") }, 3000) }
iOS — Test crash
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -> Bool { FirebaseApp.configure() // ⚠️ SOLO PARA TEST — eliminar DispatchQueue.main.asyncAfter( deadline: .now() + 3) { fatalError("NutriTrack Test Crash") } return true }
Ejecutar app y esperar el crash
La app crasheará a los 3 segundos. iOS: desconectar el debugger de Xcode antes de lanzar.
Relanzar la app
El SDK envía el reporte a Firebase en el siguiente arranque (no en el crash inmediato).
Verificar en Firebase Console
DevOps & Engagement → Crashlytics. El crash aparece en ~2-5 minutos con stack trace simbolizado.
Eliminar el código de test
Confirmar recepción del reporte y quitar el bloque de fatalError / RuntimeException del código.
Beta Launch Checklist — NutriTrack
Pre-Release-
✓
Firebase project nutritrack-prod creado y configurado google-services.json y GoogleService-Info.plist en su lugar correcto
-
✓
SDK Crashlytics añadido via BoM (Android) y SPM (iOS) firebase-crashlytics-ktx + FirebaseCrashlytics instalados
-
✓
Test crash ejecutado y verificado en ambas plataformas Reportes visibles en Firebase Console con stack traces correctos
-
!
Custom keys configurados en módulos críticos (FoodList, CameraScanner) Pendiente: añadir keys en UserProfileViewModel para rastrear NPE
-
!
NDK crash reporting activo para OutOfMemoryError Pendiente: nativeSymbolUploadEnabled = true en buildType release
-
User IDs vinculados para correlación con soporte setUserId() llamado tras autenticación en ambas plataformas
-
Alertas configuradas en Firebase Console (crash-free users < 99%) Email notifications al equipo de dev cuando crash rate supere umbral
-
Eliminar código de test crash del build de producción Verificar que no hay fatalError() ni RuntimeException("Test") en main branch