iOS Integration Guide

Embrace.io is designed to be super simple to implement. As app developers, we hated implementing analytics tools that state “just 5 minutes to integrate” but are really a ton of events that take forever to get right.

With the Embrace.io SDK, our goal is 15 minutes or less. Slap it in and we start to gather a ton of information. What we don’t have is context so we’ll ask for it in three forms - 1) user identification, 2) logging, and 3) start/end events specifying the most important moments in your app.

Note: by this point you should have received an API key. If you still haven’t, reach out to us on Slack or via email.

Integration for iOS

Follow the directions below to get the most out of the Embrace.io SDK, or jump to a specific section and add more context to your data.

Using Cocoapods

The Embrace.io SDK is available through CocoaPods. Add the Embrace.io pod to your Podfile:

pod 'EmbraceIO'

Don’t forget to run pod install!

Configuring Symbolication

At certain instances in your app’s lifecycle Embrace.io will grab a stacktrace from the current thread. By default, stacktraces will only show memory addresses, so they have to be symbolicated to actually become useful. Symbolication will replace the memory addresses with function names, file paths, and line numbers. Follow the steps below to configure DSYM uploads for your build process and we’ll handle the symbolication.

  1. Get your upload token from Embrace.io (if you haven’t received one, reach out to us via Slack or email).
  2. Add a custom Run Script build phase in Xcode.
    1. Navigate to the “Build Phases” tab in your app’s target.
    2. Click the + icon in the upper-left and select “New Run Script Phase”
    3. Within the Run Script, type in the following:

       EMBRACE_ID=[YOUR_APP_ID] EMBRACE_TOKEN=[UPLOAD_TOKEN] "${PODS_ROOT}/EmbraceIO/run.sh"
      
  3. Build and run your app.

Your DSYM files should be uploaded each time you build and run your app. It may take us a few minutes to process any uploaded DSYMs. Once processing is complete, messages that contain stack traces should have proper symbolication.

Initialization

You initialize Embrace.io by calling startWithKey: from within your AppDelegate, passing in your provided API key. We strongly recommend that you place the Embrace initialization call on the first line of your application:didFinishLaunchingWithOptions callback. This ensures the most accurate reporting time for your application’s startup process, and allows Embrace to begin instantly monitoring other SDKs that your app initializes throughout the rest of its lifecycle.

Please instantiate the Embrace SDK synchronously, i.e. not on a dispatch_async block. Embrace does most of its processing on async queues, but instantiating synchronously helps us best catch any issues during your app’s startup.

Initializing the Embrace.io SDK is also what triggers the beginning of the startup event.

Objective-C

in AppDelegate.m

#import <Embrace/Embrace.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[Embrace sharedInstance] startWithKey:<YOUR-API-KEY>];
    
    ...
    
    return YES;
}

Swift

in AppDelegate.swift

import Embrace

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    Embrace.sharedInstance().start(withKey: <YOUR-API-KEY>)
    
    ...
    
    return true
}

Ending App Startup

The Embrace.io SDK automatically begins recording the startup event once it’s intialized, so the next step is to inform the SDK that app startup is complete. Where you put this call is entirely dependent on the structure of your app — common use cases are once a user’s home feed is loaded, a sign-in screen is displayed, or more generally the instant before the first screen becomes interactive.

If you have more than one location in your app where startup could end, such as in a push notification handler or various screens for logged in/logged out users, it’s okay to add multiple endAppStartup calls. This ensures that you don’t see erroneous failed startups on your dashboard in the event that a code path is followed where the startup is not ended.

Objective-C

E.g. in SomeViewController.m

#import <Embrace/Embrace.h>

- (void)viewDidLoad
{
    ...
    
    [[Embrace sharedInstance] endAppStartup];
}

Swift

E.g. in SomeViewController.swift

import Embrace

override func viewDidLoad() {
    ...
    
    Embrace.sharedInstance().endAppStartup()
}

Boom. Your app is now sending data for the startup event, as well as monitoring for CPU, memory, and networking issues over the course of the user’s session.

Identifying Users

To use Embrace.io effectively, you must set identifiers to the current user so that you can associate them with your internal data and perform searches in the web dashboard and through alerts and insights.

Custom Identifiers

You can set an identifier, username, or email on any user via the setUserIdentifier:, setUsername:, and setUserEmail: functions.

Objective-C

[[Embrace sharedInstance] setUserIdentifier:@"123"];
[[Embrace sharedInstance] setUsername:@"max"];
[[Embrace sharedInstance] setUserEmail:@"brian@embrace.io"];

Swift

Embrace.sharedInstance().setUserIdentifier("123")
Embrace.sharedInstance().setUsername("max")
Embrace.sharedInstance().setUserEmail("brian@embrace.io")

These attributes can be reset later with calls to clearUserIdentifier, clearUsername, and clearUserEmail.

User Personas

Mark a user as a paying or non-paying user to cohort them in the web dashboard:

Objective-C

[[Embrace sharedInstance] setUserAsPayer];
[[Embrace sharedInstance] clearUserAsPayer];

Swift

Embrace.sharedInstance().setUserAsPayer()
Embrace.sharedInstance().clearUserAsPayer()

You can also set custom personas:

Objective-C

[[Embrace sharedInstance] setUserPersona:@"power_user"];
[[Embrace sharedInstance] clearUserPersona:@"power_user"];

Swift

Embrace.sharedInstance().setUserPersona("power_user")
Embrace.sharedInstance().clearUserPersona("power_user")

A default list of personas is available to all apps. These include:

new_user,
power_user,
logged_in,
vip,
content_creator,
tester

Any combination of the above can be set at any point in your app’s lifecycle, though we recommend setting them as soon as you would receive the relevant user information from any local data stores or external APIs.

After initial integration, only the default custom personas are allowed. However, additional personas can be remotely enabled. If you need us to add any for your app, just ask 😀

Logging Errors and Info Messages

When you want visibility into something instantaneous that happens in your application rather than a moment, you can send info or error messages to Embrace.io for insight into what was happening to the user. These messages will contain diagnostic information, the current thread’s stack trace, and custom properties. Error messages also allow the screenshot functionality so that you can see what the device was displaying at the moment of the error.

Logging Messages

Error messages (including Handled Exceptions) are triggered with a call to logErrorMessage:screenshot:properties:, with the same parameters as the info logs and an additional flag for whether or not you’d like a screenshot to be taken.

Info messages can be sent with a call to logInfoMessage:properties:, where the message is a string that can be searched for later in the web dashboard, and properties is an optional dictionary of (maximum 10) properties.

Objective-C

// Log an error message
NSString *error = @"checkout_error";
[[Embrace sharedInstance] logErrorMessage:logMessage screenshot:YES properties:nil];

// Log an info message
NSString *info = @"something_happened";
[[Embrace sharedInstance] logInfoMessage:info properties:@{@"screen": @"checkout"}];

Swift

// Log an error message
Embrace.sharedInstance().logErrorMessage("something_bad", screenshot: true, properties: [:])

// Log an info message
Embrace.sharedInstance().logInfoMessage("something_happened", properties: ["error": "not_found"])

Defining App Moments

You should define 2-3 moments in your app that are of the utmost importance to the experience of your users. In addition to app startup, these could be media uploads or downloads, content loading, video streaming, in-app purchases, or any other critical path in your app that could drastically harm a user’s experience if something goes haywire.

Recommendations for implementing successfully:

Measuring Custom Moments

Measuring custom moments is done with calls to beginEventWithName: and endEventWithName:. The name is used to match an end event with its corresponding start event, as well as for display on the web dashboard.

There are more parameters you can pass to the begin method as well, using beginEventWithName:identifier:allowScreenshot:properties:. Moments can optionally take a screenshot if we detect that something has gone wrong, and you can log arbitrary values in the properties dictionary (up to 10 keys).

Here’s a sample custom moment for a networking call using NSURLSession

Objective-C

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURL *url = [NSURL URLWithString:@"http://foo.com"];

NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    [[Embrace sharedInstance] endEventWithName:@"foo_request"];
}];

[[Embrace sharedInstance] beginEventWithName:@"foo_request" identifier:nil allowScreenshot:YES properties:@{@"foo": @"bar"}];
[dataTask resume];

Swift

let session = URLSession.init(configuration: URLSessionConfiguration.default)
guard let url = URL.init(string: "http://foo.com") else { return }
let dataTask = session.dataTask(with: url, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
    Embrace.sharedInstance().endEvent(withName: "foo_request")
})
    
Embrace.sharedInstance().beginEvent(withName: "foo_request", identifier: "", allowScreenshot: true, properties: ["foo": "bar"])
dataTask.resume()

If you’re trying to measure the performance of several similar requests or interactions, you can use beginEventWithName:identifier: and endEventWithName:identifier: to prevent naming collisions. This will record measurements for a given name, identifier pair.

Network Traffic Analysis

In iOS 10, Apple introduced NSURLSessionTaskMetrics, which provides a wealth of diagnostics and timing info about network requests. Embrace.io can collect and analyze this data for you and combine it with our insights.

Enhanced Network Monitoring

If you have a class that implements NSURLSessionTaskDelegate, add the following implementation:

Objective-C

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics
{
    [[Embrace sharedInstance] logURLSessionTaskMetrics:metrics forURLSessionTask:task];
}

Swift

@available(iOS 10.0, *)
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
    Embrace.sharedInstance().logURLSessionTaskMetrics(metrics, for: task)
}

How do I know if I am integrating correctly?

By default, if your phone is plugged in to your computer, your app interactions will be in “Debugging” mode and data will only show up in Live Events. If you want the data to show up in regular dashboard, you must unplug your phone and restart the app so the Embrace.io SDK will treat it as regular events.

But if you don’t see data in both cases, contact your Embrace.io team via Slack private channel, we will check for you.

Wait! What about crash reporting?

We do not ask our customers to change crash reporting solutions. We support Crashlytics and HockeyApp. If you use one of those tools, then we automatically pull crashes into our reports and insights. We’ll let you look up those users with crashes and help you prioritize which ones to solve by impact to the user experience.

Next Steps

Happy developing! 🚀