StareGlare
Compare Guides Docs Privacy Terms Contact us

How to collect bug reports and crashes from an iOS app

Last updated 9 November 2026

The short answer: give people a Report an Issue button (or let them shake the device) that collects their description and attaches the log, versions and a screenshot for them, and have the app record its own crashes, since iOS won't hand them over. This guide shows how to do both in a SwiftUI or UIKit app with the StareGlareReporter Swift package.

What makes an iOS bug report useful?

  • The app, iOS and device: version and build, iOS or iPadOS version, the model identifier (iPhone15,2), free disk, memory, battery, Low Power Mode and thermal state.
  • The app's recent log: its entries in the unified log (Logger, os_log), or the tail of your own log files.
  • A screenshot of the app's window, taken before the review sheet appears. The keyboard, system alerts and other apps are not captured.
  • A way to ask a follow-up question, since the first report is rarely enough.

Step 1: Create an app in StareGlare

Sign in (the first sign-in starts a 30-day free trial) and create an app. You get a report page link, a public app key and an inbox for that app.

Step 2: Add the StareGlareReporter Swift package

In Xcode, choose File → Add Package Dependencies…, paste https://github.com/gorgekara/snagfrog-swift, pick version 0.7.0 or later and add the StareGlareReporter library to your app target. In Package.swift:

.package(url: "https://github.com/gorgekara/snagfrog-swift", from: "0.7.0")

It needs iOS 15 or later.

Step 3: Set it up at launch

import SwiftUI
import StareGlareReporter

let stareGlare = StareGlareReporter(
    appSlug: "your-app",
    publicKey: "stareglare_pk_…",
    baseURL: URL(string: "https://stareglare.com")!
)

@main
struct MyApp: App {
    init() {
        // As early as possible: records a crash so the next launch can report it.
        StareGlareReporter.installCrashDetection(unifiedLog: .app)
        // Optional: shaking the device opens a report.
        stareGlare.reportsOnShake(unifiedLog: .app)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                // After a crash: "MyApp quit unexpectedly. Would you like to send a report?"
                .task { await stareGlare.offerReportAfterCrash() }
        }
    }
}

The dashboard's Install tab (iOS app) shows this with your real app slug and key. In a UIKit app, call installCrashDetection and reportsOnShake in application(_:didFinishLaunchingWithOptions:), and offerReportAfterCrash once your first screen is showing. Shake to report is off unless you call reportsOnShake.

Step 4: Add a report button

Button("Report an Issue") {
    Task { await stareGlare.report(logFiles: [logFileURL], unifiedLog: .app) }
}

A sheet lists what was collected, each file with a switch and a Show button, and Cancel sends nothing. Then the report page opens in a Safari sheet over your app, where the person writes what happened and sends it. The unified log covers this launch only, and values your code didn't mark privacy: .public show as <private>, as they do in Console.

Step 5: Get crashes

iOS doesn't let an app read its own crash reports, so installCrashDetection records them itself: an uncaught Objective-C exception (its name, reason and backtrace, plus the unified log up to the crash if you pass unifiedLog:), or a fatal signal, such as a Swift runtime error (EXC_BREAKPOINT (SIGTRAP)), a bad memory access or an abort, with the stack. On the next launch only, offerReportAfterCrash asks once whether to send a report, and any report attaches the record as last-crash.crash and is marked as a crash in your inbox.

Crash: EXC_BAD_ACCESS (SIGSEGV)
App: com.acme.app 1.4.2 (88)
OS: iOS 18.1
Model: iPhone15,2
Binary: MyApp 0x102a4c000 uuid 6A1C2B3D-…

Stack:
0   MyApp                     0x0000000102a50f10 MyApp + 20240
1   libsystem_platform.dylib  0x00000001f0e2a0a4 _sigtramp + 56
…

Opening the report shows a summary (the exception or signal, the device, the binary and the top frames) above the raw file. Being ended by iOS for memory, a watchdog timeout or a swipe away is not a crash, and is not recorded.

Step 6: Turn addresses into function names

Frames in your app often read MyApp + 20240. The record's Binary line has your binary's load address and UUID: find the dSYM of that build (its UUID matches; Xcode keeps it in the archive, and dwarfdump --uuid prints it), then pass the load address and the frame's address to atos:

atos -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp -l 0x102a4c000 0x0000000102a50f10

Limits to know

  • No automatic symbolication: StareGlare shows frames as the device wrote them; atos and your dSYM do the rest.
  • A signal crash has no log or message: only the stack can be written safely at that moment. An uncaught exception has its reason and, if asked for, the log.
  • Only what people choose to send, unless you also call sendCrashAutomatically() at launch, which sends the exception and stack to the Errors tab without asking, where identical crashes are grouped and counted. Only use it if your privacy policy says the app reports crashes on its own.

Questions

What is the simplest way to collect bug reports from an iOS app?

Add a "Report an Issue" button, for example in Settings, that attaches the app version, the iOS version, the device model, a screenshot and the app's recent log for the person, then opens a short form. With StareGlare this is the StareGlareReporter Swift package (0.7.0 or later) and a few lines of code.

Can an iOS app read its own crash reports?

No. iOS keeps them out of the app's reach: users find them under Settings → Privacy & Security → Analytics & Improvements, and developers in Xcode's Organizer for App Store and TestFlight builds. That is why the package records crashes itself and attaches the record to the next report.

Are the stack frames symbolicated?

Frames in Apple's libraries and an uncaught exception's backtrace usually name their functions. Frames in your own app are often only an address. The record includes your binary's load address and UUID, so atos and the dSYM of that build turn them into function names and line numbers.

Does it send anything without asking?

Not by default. A report is only sent when the person chooses to, after a review sheet lists everything attached, and they can leave out any file. Sending crashes to the Errors tab on their own is a separate call you have to add.

Does it read the device's name?

No. The details are the app version and build, the iOS or iPadOS version, the model identifier, device family, free disk, memory, battery, Low Power Mode, thermal state, orientation and screen size. The device's name is never read, because it is often the owner's name.

Related guides

  • How to add a bug report button to your website
  • How to add a custom feedback form to your website
  • How to collect bug reports from a macOS app
  • How to get crash reports from a macOS app
  • How to collect bug reports from a Godot game
  • How to collect bug reports from a Unity game
  • How to track JavaScript errors on your website

All 9 guides, from the first report to the fix.

Contact us · More guides · Docs · Compare with other tools

StareGlare by Karakabakov Privacy · Terms · DPA