Step-by-step instructions for adding performance monitoring to your iOS projects.
The BugSnag Performance iOS integration automatically instruments app starts, scene loads and network requests. Arbitrary operations can also be manually instrumented in your code.
New to BugSnag? Create an account
Looking for error monitoring? See our integration guide
Add the BugsnagPerformance
pod to your Podfile
:
pod 'BugsnagPerformance'
Run pod install
after updating your Podfile
.
Open your Xcode project and select File → Add Packages…
Search for https://github.com/bugsnag/bugsnag-cocoa-performance
as the package URL
Set Dependency Rule exact version
to v1.1.1
, then click Add Package.
Clone the BugSnag Performance GitHub repository into a local directory providing the version tag
after --branch
:
git clone --branch v1.1.1 https://github.com/bugsnag/bugsnag-cocoa-performance
Drag BugsnagPerformance.xcodeproj
into your Xcode workspace.
Select your project in the Project Navigator and in the project editor that appears, select your app’s target.
Under the General tab, click the Frameworks, Libraries and Embedded Content section’s +
button and select BugsnagPerformance.framework
(from BugsnagPerformance
).
The latest available version of bugsnag-cocoa-performance
is v1.1.1
.
Configure your API key by adding a bugsnag
Dictionary to your Info.plist
file:
Or in XML:
<key>bugsnag</key>
<dict>
<key>apiKey</key>
<string>YOUR-API-KEY</string>
</dict>
This is the same API key and the same plist key used by the BugSnag Error Monitoring library.
You can find your API key in Project Settings from your BugSnag dashboard.
If your app implements an app delegate, import the BugsnagPerformance
module and initialize BugsnagPerformance in the application:didFinishLaunchingWithOptions:
method:
#import <BugsnagPerformance/BugsnagPerformance.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[BugsnagPerformance start];
return YES;
}
import BugsnagPerformance
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BugsnagPerformance.start()
return true
}
}
Like us, some other monitoring vendor SDKs make use of the Activity Tracing API. This API does not play well with multiple consumers. Attempting to use BugSnag Performance alongside one of these other SDKs may have unwanted impact on the data collected.
BugSnag will automatically detect and report app starts, measuring them from the process start time for the app until the UIApplicationDidBecomeActiveNotification
is received. These timings are shown under the “App starts” tab in the BugSnag Performance dashboard.
The instrumentation determines whether the app is being launched for the first time since installation or reboot and so is a full “cold” start or is otherwise a “warm” start. These two app start types are displayed separately on your BugSnag dashboard.
To disable automatic instrumentation, set the autoInstrumentAppStarts
configuration option:
BugsnagPerformanceConfiguration *config = [BugsnagPerformanceConfiguration loadConfig];
config.autoInstrumentAppStarts = NO;
[BugsnagPerformance startWithConfiguration:config];
let config = BugsnagPerformanceConfiguration.loadConfig()
config.autoInstrumentAppStarts = false
BugsnagPerformance.start(configuration: config)
BugSnag will automatically detect and report UIViewController
loads, measuring the time taken by any controllers in your app between loadView
and viewDidAppear
being called. These timings are shown under the “Screen loads” tab in the BugSnag Performance dashboard.
Automatic capture of view loads is enabled by default and can be disabled using the autoInstrumentViewControllers
configuration option:
BugsnagPerformanceConfiguration *config = [BugsnagPerformanceConfiguration loadConfig];
config.autoInstrumentViewControllers = NO;
[BugsnagPerformance startWithConfiguration:config];
let config = BugsnagPerformanceConfiguration.loadConfig()
config.autoInstrumentViewControllers = false
BugsnagPerformance.start(configuration: config)
This instrumentation relies on Objective-C method “swizzling” and is injected early in your app’s startup code. See our guide on Method swizzling usage for full details on which methods are affected and how it can be fully disabled if required.
If you want control over which views are automatically instrumented, you can implement a viewControllerInstrumentationCallback
:
BugsnagPerformanceConfiguration *config = [BugsnagPerformanceConfiguration loadConfig];
config.viewControllerInstrumentationCallback = ^(UIViewController *viewController){
return ![viewController isKindOfClass:[IgnoredViewController class]];
};
[BugsnagPerformance startWithConfiguration:config];
let config = BugsnagPerformanceConfiguration.loadConfig()
config.viewControllerInstrumentationCallback = { viewController in
!(viewController is IgnoredViewController)
}
BugsnagPerformance.start(configuration: config)
When the callback returns false
, the view will not be recorded. This also allows you to manually instrument the view in order to control the start and end of the measurement yourself.
Automatic instrumentation of SwiftUI views is currently not supported.
To instrument view loads manually, call startViewLoadSpan
with a view name and type:
- (void)loadView {
self.span = [BugsnagPerformance
startViewLoadSpanWithName:@"MyViewController"
viewType:BugsnagPerformanceViewTypeUIKit];
[super loadView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.span end];
}
override func loadView() {
span = BugsnagPerformance.startViewLoadSpan(
name: "MyViewController", viewType: .uiKit)
super.loadView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated);
span?.end()
span = nil
}
BugSnag will automatically detect and report network requests in your app made through URLSession
. These timings are shown under the “Network requests” tab in the BugSnag Performance dashboard.
Automatic capture of network requests is enabled by default and can be disabled using the autoInstrumentNetworkRequests
configuration option:
BugsnagPerformanceConfiguration *config = [BugsnagPerformanceConfiguration loadConfig];
config.autoInstrumentNetworkRequests = NO;
[BugsnagPerformance startWithConfiguration:config];
let config = BugsnagPerformanceConfiguration.loadConfig()
config.autoInstrumentNetworkRequests = false
BugsnagPerformance.start(configuration: config)
This instrumentation relies on Objective-C method “swizzling” and is injected early in your app’s startup code. See our guide on Method swizzling usage for full details on which methods are affected and how it can be fully disabled if required.
To instrument network requests manually, call reportNetworkRequestSpan
from your
URLSessionTaskDelegate
URLSession:task:didFinishCollectingMetrics
method:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
[BugsnagPerformance reportNetworkRequestSpanWithTask:task metrics:metrics];
}
func urlSession(_ session: URLSession, task: URLSessionTask,
didFinishCollecting metrics: URLSessionTaskMetrics) {
BugsnagPerformance.reportNetworkRequestSpan(task: task, metrics: metrics)
}
The networkRequestCallback
configuration option allows you to control the data sent in these network request spans using a callback.
To send custom spans to BugSnag for any other operations you wish to measure, use BugsnagPerformance.startSpan
to start a span, and call the span’s end
method to end the measurement:
BugsnagPerformanceSpan *span = [BugsnagPerformance startSpanWithName:@"Login"];
[api login:^(BOOL success){
[span end];
}];
let span = BugsnagPerformance.startSpan(name: "Login")
api.login { _ in
span.end()
}
The options
parameter allows you to customize some elements of the span’s behavior:
If a custom span is “first class”, its performance characteristics will be aggregated and shown in the “Custom” tab of the BugSnag dashboard. If the custom span is useful only for adding insight into the performance of its parent (through the waterfall diagram on the span instance page), you should set the isFirstClass
span option to false
:
BugsnagPerformanceSpanOptions *options = [BugsnagPerformanceSpanOptions new];
options.firstClass = BSGFirstClassNo;
[BugsnagPerformance startSpanWithName:@"always-nested" options:options];
BugsnagPerformance.startSpan(
name: "always-nested",
options: BugsnagPerformanceSpanOptions.setFirstClass(BSGFirstClass.no))
When viewing a single instance of a span in the BugSnag dashboard, a waterfall diagram will show you that instance’s children and their children, and so on. This allows you to see in more detail where the time was spent, and aid diagnosing and fixing any problems. Usually this will be automatically managed by BugSnag, however when creating your own spans, you can use the options
parameter to control their parent-child relationships to produce a meaningfully representative hierarchy in your dashboard. See Maintaining span context for more information.
By default, a span will use the current timestamp as its start time. However, you can use the startTime
span option to report spans that have already started by providing your own timestamp. You can also end a span with your own timestamp to provide a retrospective end time:
NSDate *activityStartTime = [NSDate date];
// Do some work
NSDate *activityEndTime = [NSDate date];
BugsnagPerformanceSpanOptions *options = [BugsnagPerformanceSpanOptions new];
options.startTime = activityStartTime;
BugsnagPerformanceSpan *span1 = [BugsnagPerformance startSpanWithName:@"retro-span" options:options];
[span1 endWithEndTime:activityEndTime];
let activityStartTime = Date()
// Do some work
let activityEndTime = Date()
BugsnagPerformance.startSpan(
name: "retro-span",
options: BugsnagPerformanceSpanOptions.setStartTime(activityStartTime))
.end(endTime: activityEndTime)