Mobile App Development: Crash Reporting and Monitoring Setup Excellence

Your mobile app development project will face crashes. Every app does. The question for mobile development is not whether crashes happen but how quickly you learn about them and how effectively you resolve them. Without crash reporting in mobile app development, you are blind to stability issues until users leave one-star reviews.

This guide covers setting up a robust crash reporting and monitoring system for mobile apps, using tools that provide actionable data rather than noise.

Why Crash Reporting Matters

Consider the numbers: if your app crashes for 1% of sessions and you have 10,000 daily active users, that is 100 people per day experiencing a broken app. Some will retry. Many will not. Over a month, you accumulate thousands of negative experiences and a deteriorating App Store rating.

Professional crash reporting gives you:

  • Real-time crash alerts so you know about issues within minutes
  • Stack traces with line-level detail for fast debugging
  • User impact metrics showing how many users are affected
  • Crash trends to identify whether issues are growing or shrinking
  • Device and OS distribution to understand which configurations are affected
  • Breadcrumbs showing user actions leading to the crash

Firebase Crashl

ytics

Firebase Crashlytics is the most widely used crash reporting tool for mobile apps. It is free, reliable, and provides excellent crash grouping.

iOS Setup

// 1. Add Firebase to your Podfile
// pod 'Firebase/Crashlytics'

// 2. Configure in AppDelegate
import Firebase

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

Add the build phase script to upload dSYMs automatically:

# Build Phases > New Run Script Phase
"${PODS_ROOT}/FirebaseCrashlytics/run"

And the dSYM upload script:

# Build Phases > New Run Script Phase (after compile)
"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols" \
  -gsp "${PROJECT_DIR}/GoogleService-Info.plist" \
  -p ios "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}"

Android Setup

// build.gradle (project)
buildscript {
    dependencies {
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
    }
}

// build.gradle (app)
plugins {
    id 'com.google.firebase.crashlytics'
}

dependencies {
    implementation 'com.google.firebase:firebase-crashlytics-ktx:18.2.9'
    implementation 'com.google.firebase:firebase-analytics-ktx:20.1.2'
}

Custom Crash Context

Add context to help debug crashes faster:

// iOS: Set user identifier
Crashlytics.crashlytics().setUserID("user_12345")

// Add custom keys
Crashlytics.crashlytics().setCustomValue("premium", forKey: "account_type")
Crashlytics.crashlytics().setCustomValue("3.2.1", forKey: "app_version")

// Log breadcrumbs
Crashlytics.crashlytics().log("User tapped checkout button")
Crashlytics.crashlytics().log("Cart contains 3 items")
// Android: Set user identifier
Firebase.crashlytics.setUserId("user_12345")

// Add custom keys
Firebase.crashlytics.setCustomKey("account_type", "premium")
Firebase.crashlytics.setCustomKey("screen", "checkout")

// Log breadcrumbs
Firebase.crashlytics.log("User tapped checkout button")
Firebase.crashlytics.log("Cart total: $49.99")

Non-Fatal Error Reporting

Not all errors crash the app, but you should still track them:

// iOS
do {
    try processPayment()
} catch {
    Crashlytics.crashlytics().record(error: error)
    // Show user-friendly error message
    showPaymentErrorAlert()
}
// Android
try {
    processPayment()
} catch (e: PaymentException) {
    Firebase.crashlytics.recordException(e)
    showPaymentError()
}

S

entry

Sentry is an excellent alternative to Crashlytics, especially for teams that want more control over error grouping, alerting, and integration with their backend monitoring.

iOS Setup

import Sentry

func initSentry() {
    SentrySDK.start { options in
        options.dsn = "https://[email protected]/project-id"
        options.debug = false

        // Performance monitoring
        options.tracesSampleRate = 0.2

        // Session tracking
        options.enableAutoSessionTracking = true

        // Breadcrumbs
        options.maxBreadcrumbs = 100

        // Environment
        options.environment = Configuration.isProduction
            ? "production" : "staging"
    }

    // Set user context
    let user = Sentry.User()
    user.userId = currentUser.id
    user.email = currentUser.email
    SentrySDK.setUser(user)
}

Android Setup

import io.sentry.android.core.SentryAndroid
import io.sentry.SentryLevel

fun initSentry(context: Context) {
    SentryAndroid.init(context) { options ->
        options.dsn = "https://[email protected]/project-id"
        options.isDebug = BuildConfig.DEBUG

        // Performance monitoring
        options.tracesSampleRate = 0.2

        // Environment
        options.environment = if (BuildConfig.DEBUG) "development" else "production"

        // Before send hook
        options.beforeSend = SentryOptions.BeforeSendCallback { event, hint ->
            // Filter out specific errors
            if (event.throwable is NetworkException) {
                null // Don't send network errors
            } else {
                event
            }
        }
    }
}

Sentry Performance Monitoring

Sentry also tracks slow operations, not just crashes:

// iOS: Track a slow operation
let transaction = SentrySDK.startTransaction(
    name: "load-product-catalog",
    operation: "http"
)

let span = transaction.startChild(
    operation: "db.query",
    description: "Fetch products from local cache"
)
// ... perform database query
span.finish()

let networkSpan = transaction.startChild(
    operation: "http.request",
    description: "GET /api/products"
)
// ... perform network request
networkSpan.finish()

transaction.finish()

Monitoring Beyond Crashes

ANR D

etection (Android)

Application Not Responding (ANR) errors are as bad as crashes but harder to detect. Both Crashlytics and Sentry detect ANRs automatically on Android.

Common ANR causes:

  • Database operations on the main thread
  • Large JSON parsing on the main thread
  • Synchronous network calls
  • Heavy layout inflation

Memory Warnings (iOS)

Track when your app receives memory warnings:

NotificationCenter.default.addObserver(
    forName: UIApplication.didReceiveMemoryWarningNotification,
    object: nil,
    queue: .main
) { _ in
    Crashlytics.crashlytics().log("Memory warning received")
    Crashlytics.crashlytics().setCustomValue(
        ProcessInfo.processInfo.physicalMemory,
        forKey: "total_memory"
    )
}

App Startup Time

Track how long your app takes to become interactive:

// iOS
class AppDelegate: UIResponder, UIApplicationDelegate {
    private let launchTime = CFAbsoluteTimeGetCurrent()

    func applicationDidBecomeActive(_ application: UIApplication) {
        let startupTime = CFAbsoluteTimeGetCurrent() - launchTime
        Crashlytics.crashlytics().setCustomValue(
            startupTime,
            forKey: "startup_time_seconds"
        )

        if startupTime > 3.0 {
            Crashlytics.crashlytics().log(
                "Slow startup: \(String(format: "%.2f", startupTime))s"
            )
        }
    }
}

Network Error Tracking

Track API failures that degrade user experience:

class NetworkMonitor {
    func trackAPICall(
        endpoint: String,
        statusCode: Int,
        duration: TimeInterval,
        error: Error?
    ) {
        Crashlytics.crashlytics().setCustomValue(endpoint, forKey: "last_api_call")
        Crashlytics.crashlytics().setCustomValue(statusCode, forKey: "last_status_code")

        if statusCode >= 500 {
            let nsError = NSError(
                domain: "APIError",
                code: statusCode,
                userInfo: [
                    "endpoint": endpoint,
                    "duration": duration,
                ]
            )
            Crashlytics.crashlytics().record(error: nsError)
        }
    }
}

Alerting Strategy

Severity Levels

Not every crash deserves a 2am alert. Set up tiered alerting:

Critical (immediate alert): Crash rate exceeds 2% of sessions, or a new crash affects more than 50 users in an hour. Alert via PagerDuty or SMS.

High (within 1 hour): New crash type affecting more than 10 users, or crash rate exceeds 1%. Alert via Slack and email.

Medium (daily digest): Non-fatal errors trending upward, slow API responses, memory warnings increasing.

Low (weekly review): Crashes affecting fewer than 5 users, edge case device or OS combinations.

Crash-Free Rate Target

Track your crash-free session rate and set targets:

  • Below 95%: Emergency. Your app has serious stability issues.
  • 95% to 98%: Needs improvement. Prioritise stability work.
  • 98% to 99.5%: Good. Most apps operate in this range.
  • Above 99.5%: Excellent. You have a stable app.

Apple and Google both use crash-free rate as a factor in store ranking and featuring decisions. Maintaining above 99% is a business imperative.

Debugging with Crash Reports

Reading a Crash Report

A typical iOS crash report includes:

  1. Exception type: The kind of crash (EXC_BAD_ACCESS, SIGABRT, etc.)
  2. Thread backtrace: Stack trace showing exactly where the crash occurred
  3. Device info: Model, OS version, memory state
  4. Binary images: Framework versions loaded at crash time

Symbolication

Raw crash reports contain memory addresses, not function names. Symbolication translates these to readable code references. Ensure you:

  1. Upload dSYM files for every release build (Crashlytics does this automatically with the build phase script)
  2. Keep ProGuard/R8 mapping files for Android releases
  3. Never strip debug symbols from release builds without uploading them first

Crash Triage Process

When a new crash appears:

  1. Assess impact: How many users affected? What percentage of sessions?
  2. Reproduce: Can you trigger the crash locally?
  3. Analyse: Read the stack trace, check breadcrumbs, review custom keys
  4. Fix: Write a fix with a regression test
  5. Ship: Deploy the fix as a hotfix if impact is high
  6. Verify: Confirm the crash rate drops after the fix rolls out

Conclusion

Crash reporting is not optional for production mobile app development projects. Set it up before your first release, not after users start complaining. Firebase Crashlytics provides a solid free solution for most mobile development teams, while Sentry offers more advanced monitoring features for teams with complex mobile app development needs.

The goal in mobile development is a crash-free rate above 99% and the ability to detect, diagnose, and fix issues within hours rather than days. That level of responsiveness builds user trust and protects your App Store rating in mobile app development.

Explore more monitoring strategies in our guides on mobile DevOps automation and mobile CI/CD pipelines.

Frequently Asked Questions About Mobile App Development Crash Reporting

What crash reporting tool should I use for mobile app development?

Firebase Crashlytics is recommended for most mobile app development projects - it’s free, easy to integrate, and works seamlessly with Firebase ecosystem. Use Sentry for advanced debugging needs in complex mobile development or when you need cross-platform monitoring beyond mobile app development.

When should I set up crash reporting in mobile development?

Set up crash reporting (Crashlytics) before your first production release in mobile app development. Install during initial project setup, test crash reporting in development, and verify symbolication works before releasing. Never wait until after users report crashes in mobile development.

What is a good crash-free rate for mobile app development?

Target 99%+ crash-free sessions for production mobile app development. Below 95% indicates serious stability issues. 95-98% needs improvement. 98-99.5% is good for most mobile development projects. Above 99.5% is excellent. App stores use crash-free rate for ranking in mobile app development.

How do I debug crashes in production mobile app development?

Read stack traces and symbolicated crash reports, review breadcrumbs for user actions before crashes, check custom keys for app state in mobile app development, reproduce crashes locally when possible, and prioritize by user impact. Ship fixes quickly for high-impact mobile development crashes.

Should I monitor non-fatal errors in mobile development?

Yes, monitoring non-fatal errors in mobile app development provides early warnings. Track API failures, memory warnings, performance issues, and handled exceptions using Crashlytics.recordError(). Many user experience problems in mobile development don’t cause crashes but degrade quality.

Expert Mobile App Development Crash Reporting Insights

Maintaining 99%+ crash-free rate is a business imperative - Apple and Google use this metric for store ranking and featuring decisions in mobile app development.

Symbolication is non-negotiable for mobile development - upload dSYMs (iOS) and ProGuard/R8 mapping files (Android) automatically in your build process or crash reports are unreadable.

Critical crashes (affecting over 2% of sessions or over 50 users/hour) deserve immediate alerts via PagerDuty or SMS in mobile app development - delays of hours can result in thousands of affected users and negative reviews.

For help setting up comprehensive monitoring for your mobile app, contact eawesome. We build and monitor production mobile apps for Australian businesses with the stability standards they demand through expert mobile app development.