Method swizzling usage

Our automatic instrumentation relies on Objective-C method “swizzling” — amending the behavior of selectors at runtime – to wrap framework method calls to take measurements. This page describes our usage, and the justification for it, as well as providing options for avoiding its usage in your app.

Every care is taken to ensure this is done without negative impact to the functionality and performance of your app and takes place as early as possible in the Objective-C initialization phase and the app is still single-threaded, to eliminate unexpected behavior changes or conflicts with other method swizzling in your app or by other libraries.

Methods swizzled for instrumentation

The following methods are wrapped using swizzling:

Selector Purpose
NSURLSession sharedSession Network requests
NSURLSession
sessionWithConfiguration
:delegate:delegateQueue
Network requests
NSURLSessionTask resume Network requests
UIViewController loadView View loads
UIViewController viewDidAppear View loads
UIViewController viewWillDisappear View loads

Disabling swizzling

If you do not wish to having swizzled methods in your app, the automatic network request and view load instrumentation can be disabled using the autoInstrumentNetworkRequests and autoInstrumentViewControllers configuration options.

However because swizzling is applied so early in the app’s startup, to prevent any method swizzling being registered, you will need to set the disableSwizzling key in your app’s Info.plist:

<key>bugsnag</key>
<dict>
    <key>performance</key>
    <dict>
        <key>disableSwizzling</key>
        <true/>
    </dict>
</dict>

Manual instrumentation

Without automated instrumentation of network requests and view loads, no measurements will be taken for these areas of your dashboard. You can instead send these measurements by calling startViewLoadSpan and reportNetworkRequestSpan methods yourself in the appropriate places in your app.

For example in URLSessionTaskDelegate URLSession:task:didFinishCollectingMetrics:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                     didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
    [Bugsnag reportNetworkRequestSpanWithTask:task metrics:metrics];
}
func urlSession(_ session: URLSession, task: URLSessionTask,
                didFinishCollecting metrics: URLSessionTaskMetrics) {
    Bugsnag.reportNetworkRequestSpan(task: task, metrics: metrics)
}

And for view loads in viewDidLoad and viewDidAppear:

- (void)viewDidLoad {
    self.span = [BugsnagPerformance
                 startViewLoadSpanWithName:@"MyViewController"
                 viewType:BugsnagPerformanceViewTypeUIKit];
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.span end];
}
override func viewDidLoad() {
    span = BugsnagPerformance.startViewLoadSpan(
        name: "MyViewController", viewType: .uiKit)
    super.viewDidLoad()
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated);
    span?.end()
    span = nil
}