Configuring Backtrace for macOS
Configure Backtrace for your macOS project. This page defines the configuration settings, classes, and methods available with the Backtrace Cocoa SDK.
The macOS SDK shares the same core API as the iOS SDK. This page highlights macOS-specific configuration and notes where behavior differs. For the full API reference, see Configuring Backtrace for iOS.
Usage
- Swift
- Objective-C
import Cocoa
import Backtrace
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let backtraceCredentials = BacktraceCredentials(
endpoint: URL(string: "https://backtrace.io")!,
token: "token")
let backtraceDatabaseSettings = BacktraceDatabaseSettings()
backtraceDatabaseSettings.maxRecordCount = 1000
backtraceDatabaseSettings.maxDatabaseSize = 10
backtraceDatabaseSettings.retryInterval = 5
backtraceDatabaseSettings.retryLimit = 3
backtraceDatabaseSettings.retryBehaviour = .interval
backtraceDatabaseSettings.retryOrder = .stack
let backtraceConfiguration = BacktraceClientConfiguration(
credentials: backtraceCredentials,
dbSettings: backtraceDatabaseSettings,
reportsPerMin: 10,
allowsAttachingDebugger: false,
oomMode: .full)
BacktraceClient.shared = try? BacktraceClient(configuration: backtraceConfiguration)
// Set custom attributes
BacktraceClient.shared?.attributes = ["foo": "bar", "testing": true]
// Enable breadcrumbs
BacktraceClient.shared?.enableBreadcrumbs()
// Enable error-free metrics
BacktraceClient.shared?.metrics.enable(settings: BacktraceMetricsSettings())
}
}
#import "AppDelegate.h"
@import Backtrace;
@interface AppDelegate () <BacktraceClientDelegate>
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
BacktraceCredentials *credentials = [[BacktraceCredentials alloc]
initWithSubmissionUrl: [NSURL URLWithString: @"https://submit.backtrace.io/{subdomain-name}/{submission-token}/plcrash"]];
BacktraceDatabaseSettings *backtraceDatabaseSettings = [[BacktraceDatabaseSettings alloc] init];
backtraceDatabaseSettings.maxRecordCount = 1000;
backtraceDatabaseSettings.maxDatabaseSize = 10;
backtraceDatabaseSettings.retryInterval = 5;
backtraceDatabaseSettings.retryLimit = 3;
backtraceDatabaseSettings.retryBehaviour = RetryBehaviourInterval;
backtraceDatabaseSettings.retryOrder = RetryOrderStack;
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: backtraceDatabaseSettings
reportsPerMin: 3
allowsAttachingDebugger: TRUE
oomMode: BacktraceOomModeNone];
BacktraceClient.shared = [[BacktraceClient alloc] initWithConfiguration: configuration error: nil];
[BacktraceClient.shared setAttributes: @{@"foo": @"bar"}];
BacktraceClient.shared.delegate = self;
[BacktraceClient.shared enableBreadcrumbs];
}
@end
Advanced Usage
For more advanced usage of BacktraceClient, you can supply BacktraceClientConfiguration as a parameter. See the following example:
- Swift
- Objective-C
let backtraceCredentials = BacktraceCredentials(endpoint: URL(string: "https://backtrace.io")!, token: "token")
let configuration = BacktraceClientConfiguration(credentials: backtraceCredentials,
dbSettings: BacktraceDatabaseSettings(),
reportsPerMin: 10,
allowsAttachingDebugger: false,
oomMode: .full)
BacktraceClient.shared = try? BacktraceClient(configuration: configuration)
BacktraceCredentials *credentials = [[BacktraceCredentials alloc]
initWithEndpoint: [NSURL URLWithString: @"https://backtrace.io"]
token: @"token"];
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: [[BacktraceDatabaseSettings alloc] init]
reportsPerMin: 3
allowsAttachingDebugger: NO
oomMode: BacktraceOomModeFull];
BacktraceClient.shared = [[BacktraceClient alloc] initWithConfiguration: configuration error: nil];
BacktraceClientConfiguration
Parameters
| Setting | Description | Type | Default |
|---|---|---|---|
credentials (Swift) or initWithCredentials (Objective-C) | The BacktraceCredentials (endpoint URL and submission token) used to initialize the BacktraceClient. | Parameter | |
dbSettings | The BacktraceDatabaseSettings used to initialize the BacktraceDatabase. | Parameter | |
reportsPerMin | The shared client-side report limit for live, pending-native, and retried reports. Set to 0 for unlimited submissions. | Integer | 30 |
loggingDestinations | Optional logging destinations installed before repository and crash reporter startup. See startup diagnostics. | Set<BacktraceBaseDestination>? | nil |
delegate | Weak reference to the delegate installed before startup report processing. Retain the delegate in your application. | BacktraceClientDelegate? | nil |
allowsAttachingDebugger | Whether to allow reporting when a debugger is attached. false / NO disables reporting under the debugger; true / YES allows it. Validate fatal-crash capture outside the debugger. | Boolean | false / NO |
oomMode | Specifies how the SDK should handle OOM (Out-Of-Memory) detection. See OOM Detection Modes for details. The options are:
| BacktraceOomMode | .none / BacktraceOomModeNone |
Database Settings
BacktraceClient allows you to customize the initialization of BacktraceDatabase for local storage of error reports by supplying a BacktraceDatabaseSettings parameter, as follows:
- Swift
- Objective-C
let backtraceCredentials = BacktraceCredentials(endpoint: URL(string: "https://backtrace.io")!, token: "token")
let backtraceDatabaseSettings = BacktraceDatabaseSettings()
backtraceDatabaseSettings.maxRecordCount = 1000
backtraceDatabaseSettings.maxDatabaseSize = 10
backtraceDatabaseSettings.retryInterval = 5
backtraceDatabaseSettings.retryLimit = 3
backtraceDatabaseSettings.retryBehaviour = RetryBehaviour.interval
backtraceDatabaseSettings.retryOrder = RetryOrder.queue
let backtraceConfiguration = BacktraceClientConfiguration(credentials: backtraceCredentials,
dbSettings: backtraceDatabaseSettings,
reportsPerMin: 10)
BacktraceClient.shared = try? BacktraceClient(configuration: backtraceConfiguration)
BacktraceCredentials *credentials = [[BacktraceCredentials alloc]
initWithEndpoint: [NSURL URLWithString: @"https://backtrace.io"]
token: @"token"];
BacktraceDatabaseSettings *backtraceDatabaseSettings = [[BacktraceDatabaseSettings alloc] init];
backtraceDatabaseSettings.maxRecordCount = 1000;
backtraceDatabaseSettings.maxDatabaseSize = 10;
backtraceDatabaseSettings.retryInterval = 5;
backtraceDatabaseSettings.retryLimit = 3;
backtraceDatabaseSettings.retryBehaviour = RetryBehaviourInterval;
backtraceDatabaseSettings.retryOrder = RetryOrderStack;
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: backtraceDatabaseSettings
reportsPerMin: 3
allowsAttachingDebugger: NO
oomMode: BacktraceOomModeNone];
BacktraceClient.shared = [[BacktraceClient alloc] initWithConfiguration: configuration error: nil];
BacktraceDatabaseSettings
Parameters
| Setting | Description | Type | Default |
|---|---|---|---|
maxRecordCount | The target maximum number of stored records. Set to 0 for no record limit. See storage limits for temporary exceptions. | Integer | 0 |
maxDatabaseSize | The physical SQLite file-size target in MB, excluding attachments and other SDK files. Set to 0 for no size limit. See storage limits. | Integer | 0 |
retryInterval | The amount of time (in seconds) to wait before the next retry if unable to send a report. | Integer | 5 |
retryLimit | Controls exhaustion of stored retry failures. Use retryBehaviour = .none to disable retries. | Integer | 3 |
retryBehaviour | .interval enables ordinary retries. .none disables them but still permits a pending native report's first submission attempt, subject to reportsPerMin. | Enum | interval |
retryOrder | Order for ordinary retries: .queue sends oldest first (FIFO); .stack sends newest first (LIFO). | Enum | queue |
See Cocoa Report Delivery for initial submission, retry classification, storage limits, and startup diagnostics.
PLCrashReporter
BacktraceClient allows you to customize the configuration of the PLCrashReporter by injecting its instance as follows:
- Swift
- Objective-C
let backtraceCredentials = BacktraceCredentials(endpoint: URL(string: "https://backtrace.io")!, token: "token")
let backtraceConfiguration = BacktraceClientConfiguration(credentials: backtraceCredentials)
BacktraceClient.shared = try? BacktraceClient(
configuration: backtraceConfiguration,
crashReporter: BacktraceCrashReporter(config: PLCrashReporterConfig.defaultConfiguration()))
// or
BacktraceClient.shared = try? BacktraceClient(
configuration: backtraceConfiguration,
crashReporter: BacktraceCrashReporter(reporter: PLCrashReporter.shared()))
BacktraceCredentials *credentials = [[BacktraceCredentials alloc]
initWithEndpoint: [NSURL URLWithString: @"https://backtrace.io"]
token: @"token"];
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials];
BacktraceClient.shared = [[BacktraceClient alloc]
initWithConfiguration: configuration
crashReporter: [[BacktraceCrashReporter alloc] initWithConfig: PLCrashReporterConfig.defaultConfiguration]
error: nil];
// or
BacktraceClient.shared = [[BacktraceClient alloc]
initWithConfiguration: configuration
crashReporter: [[BacktraceCrashReporter alloc] initWithReporter: PLCrashReporter.sharedReporter]
error: nil];
Custom Crash Directory
By default, PLCrashReporter stores .plcrash files in the app's standard cache directory. You can specify a custom directory to control where crash reports are written.
The basePath controls only PLCrashReporter payloads; it does not relocate the Backtrace database or metadata, or migrate existing reports.
- Swift
- Objective-C
// Create a custom directory for crash reports
let baseURL = try FileManager.default.url(
for: .libraryDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
let crashDir = baseURL.appendingPathComponent("btcrash", isDirectory: true)
try FileManager.default.createDirectory(
at: crashDir,
withIntermediateDirectories: true)
// Create a PLCrashReporterConfig with the custom basePath
guard let plcrashConfig = PLCrashReporterConfig(
signalHandlerType: .BSD,
symbolicationStrategy: .all,
basePath: crashDir.path) else {
fatalError("Could not create PLCrashReporterConfig")
}
let reporter = BacktraceCrashReporter(config: plcrashConfig)
BacktraceClient.shared = try? BacktraceClient(
configuration: backtraceConfiguration,
crashReporter: reporter)
// Create a custom directory for crash reports
NSError *error = nil;
NSURL *libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&error];
NSURL *crashDir = [libraryUrl URLByAppendingPathComponent:@"btcrash" isDirectory:YES];
[[NSFileManager defaultManager] createDirectoryAtURL:crashDir
withIntermediateDirectories:YES
attributes:nil
error:&error];
// Create a PLCrashReporterConfig with the custom basePath
PLCrashReporterConfig *plcrashConfig = [[PLCrashReporterConfig alloc]
initWithSignalHandlerType: PLCrashReporterSignalHandlerTypeBSD
symbolicationStrategy: PLCrashReporterSymbolicationStrategyAll
basePath: crashDir.path];
BacktraceCrashReporter *reporter = [[BacktraceCrashReporter alloc] initWithConfig: plcrashConfig];
BacktraceClient.shared = [[BacktraceClient alloc]
initWithConfiguration: configuration
crashReporter: reporter
error: nil];
On iOS, you can also set a FileProtectionType on the custom crash directory to control file access when the device is locked. See the iOS Custom Crash Directory documentation for details on FileProtectionType options. File protection is not applicable on macOS.
Handling Events
BacktraceClient allows you to subscribe to events produced before and after sending each report by attaching an object that follows the BacktraceClientDelegate protocol.
To observe pending-report delivery during startup, assign a retained delegate instance to configuration.delegate before creating the client. Assigning the client delegate only after initialization can miss those events. See Configure Startup Diagnostics.
- Swift
- Objective-C
// assign `self` or any other object as a `BacktraceClientDelegate`
BacktraceClient.shared?.delegate = self
// handle events
func willSend(_ report: BacktraceReport) -> BacktraceReport
func willSendRequest(_ request: URLRequest) -> URLRequest
func connectionDidFail(_ error: Error)
func serverDidRespond(_ result: BacktraceResult)
func didReachLimit(_ result: BacktraceResult)
// assign `self` or any other object as a `BacktraceClientDelegate`
BacktraceClient.shared.delegate = self;
//handle events
- (BacktraceReport *) willSend: (BacktraceReport *)report;
- (void) connectionDidFail: (NSError *)error;
- (void) serverDidRespond: (BacktraceResult *)result;
- (NSURLRequest *) willSendRequest: (NSURLRequest *)request;
- (void) didReachLimit: (BacktraceResult *)result;
For example, you can use BacktraceClientDelegate to modify a report before send:
- Swift
- Objective-C
func willSend(_ report: BacktraceReport) -> (BacktraceReport) {
report.attributes["added"] = "just before send"
return report
}
- (BacktraceReport *)willSend:(BacktraceReport *)report {
NSMutableDictionary *dict = [report.attributes mutableCopy];
[dict setObject: @"just before send" forKey: @"added"];
report.attributes = dict;
return report;
}
Attributes
You can add custom attributes to send alongside every crash and error report:
- Swift
- Objective-C
BacktraceClient.shared?.attributes = ["foo": "bar", "testing": true]
BacktraceClient.shared.attributes = @{@"foo": @"bar", @"testing": @YES};
You can also specify a unique set of attributes for a specific report with the willSend(_:) method of BacktraceClientDelegate.
macOS-Specific System Attributes
The SDK automatically collects system attributes. The following attributes are collected differently on macOS compared to iOS:
| Attribute | macOS Behavior | iOS Behavior |
|---|---|---|
| Hardware model | Uses HW_MODEL (e.g., MacBookPro18,1) | Uses HW_MACHINE (e.g., iPhone14,5) |
| OS name | Reports macOS | Reports device systemName (e.g., iOS) |
| Screen info | Uses NSScreen API — reports screen.main.width, screen.main.height, screen.main.scale, and screen count | Uses UIScreen API — reports screen.width, screen.height, screen.scale, plus native dimensions and brightness |
| Device orientation | Not collected (not applicable) | Collected via UIDevice |
| Battery state | Not collected via attributes | Collected via UIDevice battery monitoring |
File Attachments
All Reports
You can specify file attachments to send with every crash and error report. File attachments are specified as an array of URL that contain the path to the file.
- Swift
- Objective-C
guard let libraryDirectoryUrl = try? FileManager.default.url(
for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
throw CustomError.runtimeError
}
let fileUrl = libraryDirectoryUrl.appendingPathComponent("sample.txt")
var crashAttachments = Attachments()
crashAttachments.append(fileUrl)
BacktraceClient.shared?.attachments = crashAttachments
NSString *fileName = @"myCustomFile.txt";
NSURL *libraryUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *fileUrl = [libraryUrl URLByAppendingPathComponent:fileName];
BacktraceClient.shared.attachments = [NSArray arrayWithObjects:fileUrl, nil];
Per Report
You can specify file attachments to send for a specific report by supplying an array of file paths.
- Swift
- Objective-C
let filePath = Bundle.main.path(forResource: "test", ofType: "txt")!
BacktraceClient.shared?.send(attachmentPaths: [filePath]) { (result) in
print(result)
}
NSArray *paths = @[[[NSBundle mainBundle] pathForResource: @"test" ofType: @"txt"]];
[[BacktraceClient shared] sendWithAttachmentPaths:paths completion:^(BacktraceResult * _Nonnull result) {
NSLog(@"%@", result);
}];
You can also specify a unique set of files for specific reports with the willSend(_:) method of BacktraceClientDelegate.
Error-Free Metrics
Error-free metrics allow you to determine:
- How many of your unique users (i.e., unique device IDs) using your app are experiencing errors/crashes.
- How many application sessions (i.e., individual application sessions from startup till shutdown/exit) of your app are experiencing errors/crashes.
You can track those metrics at-a-glance, as well as in detail to find out what kinds of errors/crashes are most common. For more information, see Stability Metrics Widgets.
Enabling Error-Free Metrics
- Swift
- Objective-C
BacktraceClient.shared?.metrics.enable(settings: BacktraceMetricsSettings())
[BacktraceClient.shared.metrics enableWithSettings: [[BacktraceMetricsSettings alloc] init]];
Breadcrumbs
iOS and macOS Only
Breadcrumbs allow you track events leading up to your crash, error, or other submitted object. When breadcrumbs are enabled, any captured breadcrumbs will automatically be attached as a file to your crash, error, or other submitted object (including native crashes).
Breadcrumbs are not supported on tvOS.
Enabling Breadcrumbs
- Swift
- Objective-C
BacktraceClient.shared?.enableBreadcrumbs()
[BacktraceClient.shared enableBreadcrumbs];
Adding Manual Breadcrumbs
- Swift
- Objective-C
let attributes = ["My Attribute": "My Attribute Value"]
_ = BacktraceClient.shared?.addBreadcrumb("My Breadcrumb",
attributes: attributes,
type: .user,
level: .error)
NSDictionary *attributes = @{@"My Attribute":@"My Attribute Value"};
[BacktraceClient.shared addBreadcrumb:@"My Breadcrumb"
attributes:attributes
type:BacktraceBreadcrumbTypeUser
level:BacktraceBreadcrumbLevelError];
We recommend that you do not make calls to addBreadcrumb from performance-critical code paths.
Automatic Breadcrumbs on macOS
When breadcrumbs are enabled, the SDK automatically captures system events. The set of automatic breadcrumb events differs between macOS and iOS:
| Automatic Breadcrumb Type | macOS | iOS |
|---|---|---|
| Memory pressure (warning, critical) | ✅ Via DispatchSourceMemoryPressure | ✅ Via UIApplication.didReceiveMemoryWarningNotification |
| Battery / power source changes | ✅ Via IOKit power source monitoring | ✅ Via UIDevice battery notifications |
| Screen orientation changes | ❌ Not applicable | ✅ |
| App state changes (foreground/background) | ❌ | ✅ |
| Phone call state changes | ❌ Not applicable | ✅ Via CallKit |
On macOS, the SDK uses IOKit.ps (IOKit Power Sources) to monitor battery and power state changes, such as switching between AC power and battery, and charging level changes.
You can limit the types of automatic events that are captured by specifying which automatic breadcrumb types you want to enable:
- Swift
- Objective-C
let settings = BacktraceBreadcrumbSettings()
settings.breadcrumbTypes = [BacktraceBreadcrumbType.system, BacktraceBreadcrumbType.configuration]
BacktraceBreadcrumbSettings *settings = [[BacktraceBreadcrumbSettings alloc]
init:4096
maxQueueFileSizeBytes: 64 * 1024
breadcrumbLogFileName:@"bt-breadcrumbs-0"
breadcrumbTypes:@[[NSNumber numberWithInt:BacktraceBreadcrumbTypeManual],
[NSNumber numberWithInt:BacktraceBreadcrumbTypeLog]]
breadcrumbLevel:BacktraceBreadcrumbLevelInfo];
OOM Detection Modes
OOM (Out-Of-Memory) detection is supported on macOS. The detection mechanism differs from iOS:
- macOS: Uses
DispatchSourceMemoryPressureto monitor for.warningand.criticalmemory pressure events. - iOS: Uses
UIApplication.didReceiveMemoryWarningNotification.
The SDK provides configurable OOM detection through the BacktraceOomMode enum.
BacktraceOomMode
| Mode | Swift | Objective-C | Description |
|---|---|---|---|
| None | .none | BacktraceOomModeNone | OOM detection is disabled. No launch-time overhead. |
| Light | .light | BacktraceOomModeLight | Lightweight OOM report. Captures only the current thread without symbolication. Minimal performance impact. |
| Full | .full | BacktraceOomModeFull | Full OOM report. Captures all threads with symbolication. Provides the most diagnostic detail but has higher overhead at launch. |
Choosing the Right Mode
| Use Case | Recommended Mode | Rationale |
|---|---|---|
| Games and performance-critical apps | .light | Avoids launch-time stalls from symbolication and thread enumeration. Games are especially sensitive to frame drops and launch latency. |
| Standard apps | .full | Provides complete stack traces across all threads, making it easier to diagnose memory issues. The overhead at launch is acceptable for most apps. |
| Apps where launch time is critical | .light | The .light mode skips symbolication and runs on the current thread only, keeping launch fast. |
| OOM detection not needed | .none | Zero overhead. Use when OOM crashes are not a concern or are tracked through other means. |
How It Works
.lightmode captures a snapshot of the current thread at the moment of the low-memory warning. It skips symbolication, so the report is fast to generate but contains less detail. If the lightweight capture fails, it automatically falls back to.fullmode..fullmode captures all threads with full symbolication. This runs on a dedicated background dispatch queue (com.backtrace.oom) to avoid blocking the main thread, but the I/O and symbolication work can still impact launch performance.- Both modes capture the resident memory footprint at the time of the low-memory warning.
Usage
- Swift
- Objective-C
// Lightweight mode — recommended for games and performance-sensitive apps
let configuration = BacktraceClientConfiguration(
credentials: backtraceCredentials,
dbSettings: BacktraceDatabaseSettings(),
reportsPerMin: 10,
allowsAttachingDebugger: false,
oomMode: .light)
// Full mode — recommended for standard apps that need complete diagnostics
let configuration = BacktraceClientConfiguration(
credentials: backtraceCredentials,
dbSettings: BacktraceDatabaseSettings(),
reportsPerMin: 10,
allowsAttachingDebugger: false,
oomMode: .full)
// Lightweight mode — recommended for games and performance-sensitive apps
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: [[BacktraceDatabaseSettings alloc] init]
reportsPerMin: 10
allowsAttachingDebugger: NO
oomMode: BacktraceOomModeLight];
// Full mode — recommended for standard apps that need complete diagnostics
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: [[BacktraceDatabaseSettings alloc] init]
reportsPerMin: 10
allowsAttachingDebugger: NO
oomMode: BacktraceOomModeFull];
Legacy API (detectOOM)
The detectOOM boolean parameter is deprecated but still supported for backward compatibility. It maps to the new oomMode as follows:
Legacy detectOOM | Equivalent oomMode |
|---|---|
false (default) | .none |
true | .full |
- Swift
- Objective-C
// Legacy API (deprecated) — still works but prefer oomMode
let configuration = BacktraceClientConfiguration(
credentials: backtraceCredentials,
dbSettings: BacktraceDatabaseSettings(),
reportsPerMin: 10,
allowsAttachingDebugger: false,
detectOOM: true) // maps to oomMode: .full
// Legacy API (deprecated) — still works but prefer oomMode
BacktraceClientConfiguration *configuration = [[BacktraceClientConfiguration alloc]
initWithCredentials: credentials
dbSettings: [[BacktraceDatabaseSettings alloc] init]
reportsPerMin: 3
allowsAttachingDebugger: NO
detectOOM: TRUE]; // maps to oomMode: BacktraceOomModeFull
The legacy detectOOM API does not support .light mode. To use lightweight OOM reporting, you must migrate to oomMode.