Crash reporting

Get detailed crash reports and insights about the circumstances that led up to them.

note

Crash reporting is currently in beta.

Introduction

Shake records crashes and uncaught exceptions that occur in your app and intelligently groups them on the dashboard, offering enough contextual information to help you with solving the issue. It is powerful on its own, but when combined with rest of the Shake features, it becomes a crash reporting power tool.

Enable crash reporting

Crash reporting is disabled by default but can be enabled by setting the isCrashReportingEnabled flag to true prior to calling the start method.

AppDelegate.swift
Shake.configuration.isCrashReportingEnabled = true

Sending the report

Crash reports are automatically sent during the next app launch after the crash occurs. These reports follow the same rules as regular feedback reports to some extent. The crash reports will be saved even if the app is offline and sent later when app regains connection.

Your end users can't opt out of sending their crash report as they can with the standard feedback, however they can optionally provide more information and describe what happened prior to the crash.

Editing the report

On the next app launch after the crash occurs, a sheet offering the user a chance to describe the crash will appear.

This can be enabled by setting the isAskForCrashDescription flag to true prior to calling the start method.

AppDelegate.swift
Shake.configuration.isAskForCrashDescriptionEnabled = true

When enabled, this feature allows the user to provide a more detailed description of the actions that could have led up to the crash, and potentially edit the information that is being passed to Shake dashboard.

Report context

As mentioned before, the crash report is similar to the regular feedback report.

All of the existing Shake features elegantly interoperate with it, meaning that your crash report will provide the screenshot of the last thing that user saw, and even better, the last 15 seconds of the screen recording before the crash!

A detailed crash report like this one, along with logs and all other useful information, provides you with plenty of usable data to help you resolve the root issue efficiently.

Symbolicating crash reports

The crash reports are a lot more useful when you see your symbolicated application frames.

In order to symbolicate the reports, Shake needs the dSYMS which can be found on your system.

note

Unsymbolicated crash reports will appear in the special section on the Shake dashboard and won't be processed if the dSYM files are not uploaded. Dashboard will display the UUID of the required dSYMS which can be found on you system.

Enable dSYMS for debug builds

If you plan using Shake Crash in a development environment, every debug build action needs to generate dSYM files.

When in your project / workspace, click your App.xcodeproj blue project icon — it's usually located at the top of the File Inspector tab in your left-hand pane. Select your main app target and open the Build Settings for that target. Navigate to the Build Options section and choose dwarf with dSYM file option for your Debug/Development configuration.

Finding dSYMS

Run the below command in your terminal

mdfind -name .dSYM | while read -r line; do dwarfdump -u "$line"; done

This will list all of the available dSYM files found on the system so you can identify the ones that are missing.

Missing dSYMs are marked on your Shake dashboard.

DSYMS can be uploaded to Shake with one of the methods listed below.

For the apps built with Bitcode setting, use the Fastlane plugin to grab the correct dSYMS and upload them to Shake.

Upload dSYMS manually via Shake dashboard

Your application dSYM files can be manually zipped and uploaded to the Shake dashboard.

Symbolication files are located in the project build folder which is easily accessed by navigating to Xcode Preferences › Locations › Derived Data and clicking the Finder folder link.

For archived applications with Bitcode enabled, which have already been uploaded to the AppStore, use the Xcode Organizer tool (Xcode › Window › Organizer) to manually download the AppStore generated debug symbols by clicking the "Download Debug Symbols" button on the right hand pane.

Make sure to ZIP the symbolication files before dropping them on the Shake dashboard.

Upload dSYMS using RunPhase script

Shake ships with the upload-symbols.shscript which uploads dSYMS to Shake servers.

Add this script as a RunScript phase of your Xcode project build process so the latest dSYMS are uploaded after every build action. Make sure to replace the placeholder values with the correct values for your environment.

Path/to/upload-symbols.sh \
--client_id your_client_id \
--client_secret your_client_secret
note

This method is not suitable for apps using Bitcode binary format.

Also, you can add the previous script to the Archive › Post-actions. So the latest dSYMS will be uploaded after every archive action. Make sure to select your app to provide build settings to script.

If your reports are not being symbolicated, make sure the script is working properly by doublechecking the Xcode Build log for Shake upload script errors.

When viewing the Build log, search for "SHAKE_SCRIPT" keyword to identify potential problems and check if the upload was successful.

Upload dSYMS using fastlane plugin

Fastlane is an open source platform aimed at simplifying mobile development tasks. It handles authentication with App Store Connect and downloading dSYM files.

Use this method if your app has Bitcode format enabled.

Installing fastlane

It is recommended that you use Bundler and Gemfile to define your dependency on fastlane. This will clearly define the fastlane version to be used and its dependencies, and will also speed up fastlane execution.

First of all, you should run bundle init command which will generate Gemfile. After the Gemfile is generated, add fastlane gem.

Now, your newly created Gemfile should look like this:

Gemfile
source "https://rubygems.org"
gem "fastlane"

Next, run the bundle install command which will install fastlane and all associated dependencies.

Navigate your terminal to your project's directory and run fastlane init command.

For more info about installing fastlane, visit the fastlane docs.

Installing Shake plugin

Terminal
bundle exec fastlane add_plugin upload_symbols_to_shake

Using the plugin

If you have Bitcode enabled in your application's project settings, dSYM files are generated by the App Store when your app is recompiled after the upload, and will need to be downloaded by fastlane.

In this situation you can use Shake plugin with download_dsyms to upload dSYM files:

Fastfile
lane :refresh_dsyms do
download_dsyms(version: "latest")
upload_symbols_to_shake(client_id: "<Shake client id>", client_secret: "<Shake client secret>", bundle_id: "<Bundle id of project>", plist_path: "<Path to Info.plist>")
end

On the other hand, if you have Bitcode disabled, add the upload_symbols_to_shake action with gym to upload dSYMs generated from the build:

Fastfile
lane :refresh_dsyms do
gym
upload_symbols_to_shake(client_id: "<Shake client id>", client_secret: "<Shake client secret>", bundle_id: "<Bundle id of project>", plist_path: "<Path to Info.plist>")
end

You can also pass the dSYM file paths manually:

Fastfile
upload_symbols_to_shake(client_id: "<Shake client id>", client_secret: "<Shake client secret>", bundle_id: "<Bundle id of project>", dsym_array_paths: ["./App1.dSYM.zip", "./App2.dSYM.zip"], plist_path: "<Path to Info.plist>")

Test it out

Let's crash you app. Enable crash reporting and paste the snippet below in the viewDidLoad method in one of your view controllers. We'll crash the app on a button tap by accessing the array with the out of bounds index.

Launch you app after the crash, add a sentence or two if you want to and submit the report. Your report will be visible on the Shake dashboard in a few minutes.

note

Before testing this, make sure to disconnect your device from the Xcode debugger. Xcode debugger attaches itself to the application process and will disable crash recording.

ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let crashButton = UIButton(type: .roundedRect)
crashButton.setTitle("Crash me", for: .normal)
crashButton.frame = .init(x: 0, y: 0, width: 100, height: 50)
crashButton.center = self.view.center
self.view.addSubview(crashButton)
crashButton.addTarget(self, action: #selector(crashButtonHandler), for: .touchUpInside)
}
@objc func crashButtonHandler() {
let arr = [1, 2, 3]
let myVar = arr[5]
}

Handling errors

Sometimes, the application is making great use of error handling and wants to document these errors, or has some definitive points where caught errors could use a bit more context.

Shake SDK enables you to report the caught errors and lets you decide how they are grouped. These non-fatal error reports will have all of the same contextual information as crash reports, and act as an extension to the crash reporting feature.

note

Avoid using unique values for error clusterID as this will cause a large number of reported errors appear unrelated — although they actually are — which will clog your Shake dashboard.

ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
do {
try functionThatCanThrowError()
} catch let error {
Shake.handleError(error, clusterID: "MyViewController")
}
}
private func functionThatCanThrowError() throws {
throw MyError.testError
}

Error structure

The handleErrorfunction will accept NSError types which naturally contain the following properties:

  • code: Int
  • domain: String
  • userInfo: [AnyHashable : Any]? = nil

Caught Exceptions?

Shake doesn't provide an interface to directly report caught NSExceptions. Cocoa APIs are not exception safe, and should be treated as a developer error which terminates the program shortly after. Although the NSException can be caught by using the @catch statement, this is considered a bad practice and really shouldn't be done anywhere in your code.