The Atatus iOS SDK reports crashes and errors as part of the RUM session they happened in, so you can see the screens, taps, and requests that led to a failure rather than a stack trace on its own.

This page covers the five kinds of failure the SDK reports: crashes, app hangs, watchdog terminations, memory warnings, and errors you report yourself.

Prerequisites

  • RUM is set up in your application. See iOS Monitoring Setup.
  • The AtatusCrashReporting product is added to your target, for crash reporting.

Enable crash reporting

Crash reporting is a separate product. Enable it after RUM:

copy
icon/buttons/copy
import AtatusCore
import AtatusRUM
import AtatusCrashReporting

Atatus.initialize(
    with: Atatus.Configuration(
        licenseKey: "<LICENSE_KEY>",
        env: "<ENV_NAME>",
        service: "<APP_NAME>"
    ),
    trackingConsent: .granted
)

RUM.enable(
    with: RUM.Configuration(
        uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
        uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate()
    )
)

CrashReporting.enable()
Warning:

The order matters. A crash is delivered as a RUM error, so CrashReporting.enable() must come after RUM.enable(with:). Enable it before RUM and the pending crash from the previous run is dropped, with only a console warning to show for it.

There is nothing to configure. CrashReporting.enable() takes no options.

What is captured

The SDK installs handlers for the failures that kill a process:

  • Fatal signals, such as SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE, and SIGTRAP.
  • Uncaught Objective-C and Swift exceptions.
  • Uncaught C++ exceptions.

A crash cannot be uploaded from a dying process, so the report is written to disk and sent on the next launch. It is attributed to the session and view that were active when the application died, which is why a crash appears in the session that caused it rather than the one that reported it.

Note:

A crash is only reported if tracking consent was .granted during the run that crashed. One pending report is kept at a time, so two crashes in quick succession report the most recent one.

Read a crash report

Crash reports leave the device unsymbolicated: each frame is a memory address plus the binary image it belongs to. Atatus resolves those addresses into file names, functions, and line numbers using the debug symbols for the exact build that crashed.

That makes the dSYM files part of every release you ship. Keep them for each build, including App Store builds, where Xcode rebuilds the binary and generates new dSYMs that you download from the Organizer or from your CI artifacts. Without the matching dSYMs, a report shows addresses only.

Stack traces are capped at 200 frames. When a stack is longer, frames are dropped from the middle so the crash site and the entry point both survive, and the report is marked as truncated.

Report app hangs

An app hang is the main thread blocking long enough that the interface stops responding. Detection is off by default. Turn it on with a threshold in seconds:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        appHangThreshold: 0.25
    )
)

Hangs are reported as errors with the type AppHang, not as long tasks. A hang that the application never recovers from, because the user force-quit it or the system killed it, is reported on the next launch as a fatal hang.

Detail Behavior
Minimum threshold 0.25 seconds is a reasonable floor. Values below 0.1 are raised to 0.1 with a console warning
Tolerance Hangs within about 2.5 percent of your threshold may not be reported
Upper bound Hangs longer than 30 seconds are discarded as false positives, since they usually mean the device was suspended
Stack trace Requires crash reporting. Without it the error says so in place of the trace
Note:

Pick a threshold you will act on. 0.25 seconds catches interface stalls a user notices, while a lower value reports work that is invisible in practice and produces volume you will learn to ignore. Hangs are only detected in the main application, not in app extensions or widgets.

Report watchdog terminations

iOS kills an application that uses too much memory, takes too long to launch, or stops responding to the system. These terminations never reach a crash handler, so the SDK infers them on the next launch:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        trackWatchdogTerminations: true
    )
)
Warning:

Watchdog termination tracking needs CrashReporting.enable() as well. With crash reporting absent, the flag has no effect and the SDK emits no warning to tell you so.

The SDK reports a termination only when it can rule out every other explanation: a crash, a graceful exit, an app update, an OS update, a device reboot, a reinstall, a debugger session, and a run on the simulator. In practice this means terminations are reported from release builds on real devices.

The report carries the type WatchdogTermination and no stack trace, because none exists. One case it cannot distinguish: a user force-quitting your application from the app switcher while it is in the foreground is reported as a watchdog termination.

Report memory warnings

Memory warnings are reported as errors by default, which is how you find the screens that precede an out-of-memory termination:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        trackMemoryWarnings: true
    )
)

Each warning becomes an error with the type MemoryWarning and no stack trace, attributed to the view that was active.

Report errors yourself

A failure your code handles still matters, because a retry loop or an empty screen is invisible in crash data. Report it:

copy
icon/buttons/copy
import AtatusRUM

do {
    try placeOrder()
} catch {
    RUMMonitor.shared().addError(error: error, source: .source)
}

Or report a message when you have no error value:

copy
icon/buttons/copy
RUMMonitor.shared().addError(
    message: "Payment declined",
    type: "PaymentError",
    source: .source,
    attributes: ["order.id": "A-1234"]
)

The source tells you where the failure came from: .source for your own code, .network for a request, .webview for embedded web content, .console, .logger, and .custom. The default is .custom. When you omit the stack, the SDK records the file and line of the call.

Group errors with a fingerprint

Errors are grouped by their message and stack trace, so a message that embeds an identifier splits one problem into many. Set a fingerprint to group them yourself:

copy
icon/buttons/copy
RUMMonitor.shared().addError(
    message: "Checkout failed for order A-1234",
    source: .source,
    attributes: [RUM.Attributes.errorFingerprint: "checkout-failed"]
)

Every error with the same fingerprint is grouped together regardless of its message.

Note:

The fingerprint applies to errors you report and to failed resources. Crashes, fatal hangs, watchdog terminations, and memory warnings are grouped by the SDK and cannot be fingerprinted this way.

Redact or drop errors

An error event mapper runs on every error before it is uploaded. Use it to strip sensitive content, or return nil to drop the error:

copy
icon/buttons/copy
var configuration = RUM.Configuration(
    uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate()
)

configuration.errorEventMapper = { event in
    var event = event

    if event.error.message.contains("internal-debug") {
        return nil
    }

    event.error.message = event.error.message.replacingOccurrences(
        of: #"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+"#,
        with: "<email>",
        options: .regularExpression
    )

    return event
}

RUM.enable(with: configuration)

The fields you can change are the message, the stack, the fingerprint, the causes, and the event context. The category, type, source, and threads are fixed.

Warning:

Crashes cannot be dropped. Returning nil for a crash sends the unmodified event and logs a warning, because losing a crash is worse than keeping one you did not want. Redaction still works. Fatal hangs and watchdog terminations skip the mapper entirely, so plan your redaction around that.

Errors from logs

Logs written at error or critical are also reported as RUM errors, with the source logger. See Log Collection.

Objective-C

copy
icon/buttons/copy
@import AtatusRUM;
@import AtatusCrashReporting;

ATRUMConfiguration *configuration = [ATRUMConfiguration new];
configuration.appHangThreshold = 0.25;          // 0 disables hang reporting
configuration.trackWatchdogTerminations = YES;
configuration.trackMemoryWarnings = YES;
[ATRUM enableWith:configuration];

[ATCrashReporter enable];

[[ATRUMMonitor shared] addErrorWithMessage:@"Checkout failed"
                                     stack:nil
                                    source:ATRUMErrorSourceSource
                                attributes:@{}];

In Objective-C, appHangThreshold is a plain NSTimeInterval where 0 means disabled, and addError takes no error type.

Platform support

Platform Notes
iOS, iPadOS Everything on this page is supported
tvOS Supported, except that memory warnings follow the same rules as iOS and actions are press based
App extensions Crashes are reported. App hangs and watchdog terminations are not

Next steps