Training
Learning path
Implement finance and operations apps - Training
Plan and design your project methodology to successfully implement finance and operations apps with FastTrack services, data management and more.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Important
Visual Studio App Center is scheduled for retirement on March 31, 2025. While you can continue to use Visual Studio App Center until it is fully retired, there are several recommended alternatives that you may consider migrating to.
App Center Crashes will automatically generate a crash log every time your app crashes. The log is first written to the device's storage and when the user starts the app again, the crash report will be sent to App Center. Collecting crashes works for both beta and live apps, i.e. those submitted to the App Store. Crash logs contain valuable information for you to help fix the crash.
Follow the Getting Started section if you haven't set up the SDK in your application yet.
Also, crash logs on macOS require Symbolication, check out the App Center Diagnostics documentation that explains how to provide symbols for your app.
Note
In the 4.0.0
version of App Center breaking changes were introduced. Follow the Migrate to App Center SDK 4.0.0 and higher section to migrate App Center from previous versions.
App Center supports crash reporting in macOS extensions. The usage is the same as in the container application.
App Center Crashes provides you with an API to generate a test crash for easy testing of the SDK. This API can only be used in test/beta apps and won't do anything in production apps.
[MSACCrashes generateTestCrash];
Crashes.generateTestCrash()
App Center Crashes has two APIs that give you more information in case your app has crashed.
At any time after starting the SDK, you can check if the app received a memory warning in the previous session:
[MSACCrashes hasReceivedMemoryWarningInLastSession];
Crashes.hasReceivedMemoryWarningInLastSession
Note
This method must only be used after Crashes
has been started, it will always return NO
or false
before start.
Note
In some cases, a device with low memory can't send events.
At any time after starting the SDK, you can check if the app crashed in the previous launch:
[MSACCrashes hasCrashedInLastSession];
Crashes.hasCrashedInLastSession
This comes in handy in case you want to adjust the behavior or UI of your app after a crash has occurred.
Note
This method must only be used after MSACCrashes
has been started, it will always return NO
or false
before start.
If your app crashed previously, you can get details about the last crash.
MSACErrorReport *crashReport = [MSACCrashes lastSessionCrashReport];
var crashReport = Crashes.lastSessionCrashReport
Note
This method must only be used after Crashes
has been started, it will always return nil
before start.
There are numerous use cases for this API, the most common one is people who call this API and implement their custom CrashesDelegate.
App Center Crashes provides callbacks for developers to perform additional actions before and when sending crash logs to App Center.
To add your custom behavior, you need to adopt the CrashesDelegate
-protocol, all of its methods are optional.
[MSACCrashes setDelegate:self];
Crashes.delegate = self
Note
You must set the delegate before calling AppCenter.start
, since App Center starts processing crashes immediately after the start.
Implement the crashes:shouldProcessErrorReport:
-method in the class that adopts the CrashesDelegate
-protocol if you want to decide if a particular crash needs to be processed or not. For example, there could be a system level crash that you'd want to ignore and that you don't want to send to App Center.
- (BOOL)crashes:(MSACCrashes *)crashes shouldProcessErrorReport:(MSACErrorReport *)errorReport {
return YES; // return YES if the crash report should be processed, otherwise NO.
}
func crashes(_ crashes: Crashes, shouldProcess errorReport: ErrorReport) -> Bool {
return true; // return true if the crash report should be processed, otherwise false.
}
App Center also allows you to track errors by using handled exceptions via trackError
method. An app can optionally attach properties or/and attachments to a handled error report to provide further context.
@try {
// Throw error.
} @catch (NSError *error) {
// Init attachments.
NSArray<MSACErrorAttachmentLog *> attachments = @[ MSACErrorAttachmentLog attachmentWithText:@"Hello world!" filename:@"hello.txt"] ]
// Init properties.
NSDictionary *properties = @{ "Category" : "Music", "Wifi" : "On" };
// Track errors.
[MSACCrashes trackError:error withProperties:properties attachments:attachments];
[MSACCrashes trackError:error withProperties:properties attachments:nil];
[MSACCrashes trackError:error withProperties:nil attachments:attachments];
[MSACCrashes trackError:error withProperties:nil attachments:nil];
}
do {
// Throw error.
} catch {
// Init attachments.
let attachments = [ErrorAttachmentLog.attachment(withText: "Hello world!", filename: "hello.txt")]
// Init properties.
let properties:Dictionary<String, String> = ["Category" : "Music", "Wifi" : "On"]
// Track errors.
Crashes.trackError(error, properties: properties, attachments: attachments)
Crashes.trackError(error, properties: properties, attachments: nil)
Crashes.trackError(error, properties: nil, attachments: attachments)
Crashes.trackError(error, properties: nil, attachments: nil)
}
For track exceptions you can use trackException
method:
@try {
// Throw exceptions.
} @catch (NSException *exception) {
// Init exceptions.
MSACExceptionModel *customException1 = [MSACExceptionModel initWithType:@"Custom exception" exceptionMessage:"Track custom exception.", stackTrace:exception.callStackSymbols];
MSACExceptionModel *customException2 = [MSACExceptionModel initWithException:exception];
// Track exceptions.
[MSACCrashes trackException:customException1 withProperties:properties attachments:nil];
[MSACCrashes trackException:customException2 withProperties:properties attachments:nil];
}
do {
// Throw exception.
} catch {
// Init exception.
let exception = ExceptionModel(withType: "Custom exception", exceptionMessage: "Track custom exception.", stackTrace: Thread.callStackSymbols)
// Track exception.
Crashes.trackException(exception, properties: properties, attachments: nil)
}
If user privacy is important to you, you might want to get user confirmation before sending a crash report to App Center. The SDK exposes a callback that tells App Center Crashes to await user confirmation before sending any crash reports.
If you chose to do so, you're responsible for obtaining the user's confirmation, e.g. through a dialog prompt with one of the following options: Always send, Send, and Don't send. Based on the input, you'll tell App Center Crashes what to do and the crash will then be handled accordingly.
Note
The SDK doesn't display a dialog for this, the app must provide its own UI to ask for user consent.
The following method shows how to set up a user confirmation handler:
[MSACCrashes setUserConfirmationHandler:(^(NSArray<MSACErrorReport *> *errorReports) {
// Your code to present your UI to the user, e.g. an NSAlert.
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Sorry about that!"];
[alert setInformativeText:@"Do you want to send an anonymous crash report so we can fix the issue?"];
[alert addButtonWithTitle:@"Always send"];
[alert addButtonWithTitle:@"Send"];
[alert addButtonWithTitle:@"Don't send"];
[alert setAlertStyle:NSWarningAlertStyle];
switch ([alert runModal]) {
case NSAlertFirstButtonReturn:
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationAlways];
break;
case NSAlertSecondButtonReturn:
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationSend];
break;
case NSAlertThirdButtonReturn:
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationDontSend];
break;
default:
break;
}
return YES; // Return YES if the SDK should await user confirmation, otherwise NO.
})];
Crashes.setUserConfirmationHandler({ (errorReports: [ErrorReport]) in
// Your code to present your UI to the user, e.g. an NSAlert.
let alert: NSAlert = NSAlert()
alert.messageText = "Sorry about that!"
alert.informativeText = "Do you want to send an anonymous crash report so we can fix the issue?"
alert.addButton(withTitle: "Always send")
alert.addButton(withTitle: "Send")
alert.addButton(withTitle: "Don't send")
alert.alertStyle = NSWarningAlertStyle
switch (alert.runModal()) {
case NSAlertFirstButtonReturn:
Crashes.notify(with: .always)
break;
case NSAlertSecondButtonReturn:
Crashes.notify(with: .send)
break;
case NSAlertThirdButtonReturn:
Crashes.notify(with: .dontSend)
break;
default:
break;
}
return true // Return true if the SDK should await user confirmation, otherwise return false.
})
In case you return YES
/true
in the handler block above, your app should obtain user permission and message the SDK with the result using the following API. If you're using an alert for this, as we do in the sample above, you'd call it based on the result (ModalResponse) of runModal
call.
// Depending on the user's choice, call notifyWithUserConfirmation: with the right value.
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationDontSend];
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationSend];
[MSACCrashes notifyWithUserConfirmation:MSACUserConfirmationAlways];
// Depending on the user's choice, call notify(with:) with the right value.
Crashes.notify(with: .dontSend)
Crashes.notify(with: .send)
Crashes.notify(with: .always)
AppKit catches exceptions thrown on the main thread, preventing the application from crashing on macOS, so the SDK can't catch these crashes. To mimic iOS behavior, set NSApplicationCrashOnExceptions
flag before SDK initialization, this flag allows the application to crash on uncaught exceptions and the SDK can report them.
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions" : @YES }];
UserDefaults.standard.register(defaults: ["NSApplicationCrashOnExceptions": true])
Note
App Center SDK set the flag automatically in versions 1.10.0 and below. Starting with version 1.11.0 this flag is no longer set automatically.
The App Center Crashes SDK uses swizzling to improve its integration by forwarding itself some of the application main class's methods calls. Method swizzling is a way to change the implementation of methods at runtime. If for any reason you don't want to use swizzling (e.g. because of a specific policy), you should override the application's reportException:
and sendEvent:
methods yourself in order for Crashes to report exceptions thrown on the main thread correctly.
Create ReportExceptionApplication.m file and add the following implementation:
@import Cocoa;
@import AppCenterCrashes;
@interface ReportExceptionApplication : NSApplication
@end
@implementation ReportExceptionApplication
- (void)reportException:(NSException *)exception {
[MSACCrashes applicationDidReportException:exception];
[super reportException:exception];
}
- (void)sendEvent:(NSEvent *)theEvent {
@try {
[super sendEvent:theEvent];
} @catch (NSException *exception) {
[self reportException:exception];
}
}
@end
Note
Swift's try
/catch
doesn't work with NSException
. These exceptions can be handled in Objective-C only.
Open Info.plist file and replace the NSApplication in the Principal class field with your application class name, ReportExceptionApplication in this example.
To disable swizzling in App Center SDK, add the AppCenterApplicationForwarderEnabled
key to Info.plist file, and set the value to 0
.
At times, you want to know the status of your app crash. A common use case is that you might want to show UI that tells the users that your app is submitting a crash report, or, in case your app is crashing quickly after the launch, you want to adjust the behavior of the app to make sure the crash logs can be submitted. The CrashesDelegate
-protocol defines three different callbacks that you can use in your app to be notified of what's going on:
- (void)crashes:(MSACCrashes *)crashes willSendErrorReport:(MSACErrorReport *)errorReport {
// Your code, e.g. to present a custom UI.
}
func crashes(_ crashes: Crashes, willSend errorReport: ErrorReport) {
// Your code, e.g. to present a custom UI.
}
In case we have network issues or an outage on the endpoint, and you restart the app, willSendErrorReport
is triggered again after process restart.
- (void)crashes:(MSACCrashes *)crashes didSucceedSendingErrorReport:(MSACErrorReport *)errorReport {
// Your code, e.g. to hide the custom UI.
}
func crashes(_ crashes: Crashes, didSucceedSending errorReport: ErrorReport) {
// Your code goes here.
}
- (void)crashes:(MSACCrashes *)crashes didFailSendingErrorReport:(MSACErrorReport *)errorReport withError:(NSError *)error {
// Your code goes here.
}
func crashes(_ crashes: Crashes, didFailSending errorReport: ErrorReport, withError error: Error) {
// Your code goes here.
}
Receiving didFailSendingErrorReport
means a non-recoverable error such as a 4xx code occurred. For example, 401 means the appSecret
is wrong.
This callback isn't triggered if it's a network issue. In this case, the SDK keeps retrying (and also pauses retries while the network connection is down).
You can add binary and text attachments to a crash report. The SDK will send them along with the crash so that you can see them in App Center portal. The following callback will be invoked right before sending the stored crash from previous application launches. It won't be invoked when the crash happens. Here is an example of how to attach text and an image to a crash:
- (NSArray<MSACErrorAttachmentLog *> *)attachmentsWithCrashes:(MSACCrashes *)crashes
forErrorReport:(MSACErrorReport *)errorReport {
MSACErrorAttachmentLog *attachment1 = [MSACErrorAttachmentLog attachmentWithText:@"Hello world!" filename:@"hello.txt"];
MSACErrorAttachmentLog *attachment2 = [MSACErrorAttachmentLog attachmentWithBinary:[@"Fake image" dataUsingEncoding:NSUTF8StringEncoding] filename:@"fake_image.jpeg" contentType:@"image/jpeg"];
return @[ attachment1, attachment2 ];
}
func attachments(with crashes: Crashes, for errorReport: ErrorReport) -> [ErrorAttachmentLog]? {
let attachment1 = ErrorAttachmentLog.attachment(withText: "Hello world!", filename: "hello.txt")
let attachment2 = ErrorAttachmentLog.attachment(withBinary: "Fake image".data(using: String.Encoding.utf8), filename: nil, contentType: "image/jpeg")
return [attachment1!, attachment2!]
}
Note
The size limit is currently 7 MB. Attempting to send a larger attachment will trigger an error.
You can enable and disable App Center Crashes at runtime. If you disable it, the SDK won't do any crash reporting for the app.
[MSACCrashes setEnabled:NO];
Crashes.enabled = false
To enable App Center Crashes again, use the same API but pass YES
/true
as a parameter.
[MSACCrashes setEnabled:YES];
Crashes.enabled = true
The state is persisted in the device's storage across application launches.
Note
This method must only be used after Crashes
has been started.
You can also check if App Center Crashes is enabled or not:
BOOL enabled = [MSACCrashes isEnabled];
var enabled = Crashes.enabled
Note
This method must only be used after Crashes
has been started, it will always return false
before start.
By default, App Center Crashes uses the Mach exception handler to catch fatal signals, e.g. stack overflows, via a Mach exception server.
The disableMachExceptionHandler
-method provides an option to disable catching fatal signals via a Mach exception server. If you want to disable the Mach exception handler, you should call this method BEFORE starting the SDK. Your typical setup code would look like this:
[MSACCrashes disableMachExceptionHandler];
[MSACAppCenter start:@"{Your App Secret}" withServices:@[[MSACAnalytics class], [MSACCrashes class]]];
Crashes.disableMachExceptionHandler()
AppCenter.start(withAppSecret: "{Your App Secret}", services: [Analytics.self, Crashes.self])
Training
Learning path
Implement finance and operations apps - Training
Plan and design your project methodology to successfully implement finance and operations apps with FastTrack services, data management and more.