Customize the iOS Instrumentation
Once you have instrumented your iOS application with the Mobile iOS SDK, you can also use the APIs exposed by the SDK to customize the data for your app that appears in the Controller UI.
The following sections show you how to use the iOS SDK to customize your instrumentation.
- Collect Additional Types of Data
- Capture User Interactions
- Programmatically Take Screenshots
- Block and Unblock Screenshots
- Disable the Agent to Stop Sending User Data to the Collector
- Add a Crash Reporting Callback
- Disable Crash Reporting
- Report Errors and Exceptions
- Configure Application-Not-Responding (ANR) Detection
- Configure Hybrid Application Support
- Programmatically Control Sessions
- Start and End Session Frames
- Configure the Agent for Custom App Names
- Configure the Agent for Ignoring Some HTTP Requests
- Use the Agent with a Custom HTTP Library
- Transform URLs for Network Requests
- Enable Logging and Set Logging Level
- iOS SDK Documentation
Collect Additional Types of Data
You can use methods available in the ADEUMInstrumentation class to collect six additional types of data:
Type of Data | Description | Specifications | Where Data is Displayed |
---|---|---|---|
Info points | How often a method is invoked, and how long it takes to run. |
| |
Custom timers | Any arbitrary sequence of events within your code timed, even spanning multiple methods. |
| |
Custom metrics | Any integer-based data you wish to collect. |
| |
User data | Any string key/value pair you think might be useful. This data is included with all listed instrumentation types until it is cleared. |
| |
Breadcrumbs | The context for a crash. |
| |
User interaction | Capture when users press buttons, click on lists, and select text. |
|
Info Points
- Objective-C
-
- (void)myMethod { id tracker = [ADEumInstrumentation beginCall:self selector:_cmd]; // Implementation of method here ... [ADEumInstrumentation endCall:tracker]; }
- Swift
-
func myMethod() { let tracker = ADEumInstrumentation.beginCall(self, selector: #function) // Implementation of method here ... ADEumInstrumentation.endCall(tracker) }
Custom Timers
startTimer
and stopTimer
. For example, to track the time a user spends viewing a screen, the instrumentation could look like this: - Objective-C
-
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [ADEumInstrumentation startTimerWithName:@"View Lifetime"]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [ADEumInstrumentation stopTimerWithName:@"View Lifetime"]; }
- Swift
-
func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) ADEumInstrumentation.startTimer(withName: "View Lifetime") } func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) ADEumInstrumentation.stopTimer(withName: "View Lifetime") }
This information appears in the Custom Data view of the Controller UI.
startTimerWithName
again with the same name value resets a named timer.Custom Metrics
Any integer-based data can be passed to the agent. The first parameter to the report.MetricWithName
call is the name you want the metric to appear under in the Controller UI. The metric name should only contain alphanumeric characters and spaces. Illegal characters are replaced by their ASCII hex value.
Reporting a metric called "My custom metric", for example, would look something like this:
[ADEumInstrumentation reportMetricWithName:@"My custom metric" value:<#VALUE HERE#>];
This information appears in the Custom Data view of the Controller UI.
Custom User Data
You can set, and later remove any string key/value pair using with the following methods:
-
setUserData(key, value, success, error)
-
removeUserData(key)
Once this is set, user data continues to be sent along with any instrumentation for network requests, sessions, or crashes. You can remove any previously set user data on a per-key basis. Alternatively, you can remove previously set user data for all keys using clearAllUserData()
.
Parameters
The following table describes the parameters:
Name | Type | Description |
---|---|---|
key | string | The key identifying the key-value pair. |
value | string | The value associated with the key. |
success | function | The user-defined callback for successful cases. |
error | function | The user-defined callback for failed cases. |
Example
- Objective-C
-
- (void) onUserLoggedIn:(NSString *)userid { [ADEumInstrumentation setUserData:@"User ID" value:userid]; ... }
- Swift
-
func onUserLogged(in userid: String?) { ADEumInstrumentation.setUserData("User ID", value: userid) }
- Objective-C
-
- (void) onUserLoggedIn:(NSString *)userid { [ADEumInstrumentation removeUserData:@"User ID"]; ... }
- Swift
-
func onUserLogged(in userid: String?) { ADEumInstrumentation.removeUserData("User ID") ... }
Understanding clearAllUserData()
The clearAllUserData()
API clears all user data in the sense of clearing all of the above enumerated types of data at once. It does not clear any other data outside of the scope of items set with the Splunk AppDynamics setUserData()
list of APIs described above. Also, it does not remove any user data already attached to existing instrumentation beacons that were previously queued for sending, and it does not affect user data attached on a per-request basis to network request instrumentation.
Example
clearAllUserData
SDK API: - Objective-C
-
- (void) onUserLoggedOut { [ADEumInstrumentation clearAllUserData]; ... }
- Swift
-
func onUserLoggedOut() { ADEumInstrumentation.clearAllUserData() ... }
Breadcrumbs
Breadcrumbs allow you to situate a crash in the context of your user's experience. Set a breadcrumb when something interesting happens. If your application crashes at some point in the future, the breadcrumb will be displayed along with the crash report.
There are two ways of leaving breadcrumbs:
Using this method means that breadcrumbs are reported in crash reports only.
+ (void)leaveBreadcrumb:(NSString *)breadcrumb
Using this method lets you fine tune where the breadcrumbs are reported, either only in crash reports or in crash reports and sessions.
+ (void)leaveBreadcrumb:(NSString *)breadcrumb mode:(ADEumBreadcrumbVisibility)mode
Where mode is either:
-
ADEumBreadcrumbVisibilityCrashesOnly
-
ADEumBreadcrumbVisibilityCrashesAndSessions
Capture User Interactions
You can enable the iOS Agent to track certain UI events triggered by user interactions. Once user interactions have been captured, you can sort sessions by UI event and view UI events in the timeline of the session waterfall.
You can capture when users do one or all of the following:
- press buttons
- select table cells
- select text fields
- select text views
The interaction capture mode is disabled by default for security and privacy reasons as user interactions may contain sensitive information. Moreover, this potential security and privacy issue may be compounded if you enable both the capturing of UI interactions and screenshots.
Enable User Interaction Capture Mode
To enable user interaction capture mode, you assign the capture mode to the property interactionCaptureMode of the ADEumAgentConfiguration object. The instrumentation code example below configures the iOS Agent to capture all the supported types of user interactions.
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
config.interactionCaptureMode = ADEumInteractionCaptureModeAll;
[ADEumInstrumentation initWithConfiguration:config];
You can also configure the iOS Agent to only capture one type of user interaction:
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
config.interactionCaptureMode = ADEumInteractionCaptureModeButtonPressed;
[ADEumInstrumentation initWithConfiguration:config];
Programmatically Take Screenshots
- Objective-C
-
[ADEumInstrumentation takeScreenshot];
- Swift
-
ADEumInstrumentation.takeScreenshot()
Disable Screenshots
- Objective-C
-
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.screenshotsEnabled = NO; [ADEumInstrumentation initWithConfiguration:config];
- Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); config.screenshotsEnabled = false; ADEumInstrumentation.initWith(config);
Block and Unblock Screenshots
You can also use the iOS SDK to block screenshots from being taken during the execution of a code block. This just temporarily blocks screenshots from being taken until you unblock screenshots. This enables you to stop taking screenshots in situations where users are entering personal data, such as on login and account screens.
The ADEumInstrumentation class provides the methods blockScreenshots and unblockScreenshots to block and unblock screenshots. If screenshots are disabled through the property screenshotsEnabled of the ADEumAgentConfiguration object or through the Controller UI, these methods have no effect. You can also call screenshotsBlocked to check if screenshots are being blocked.
- Objective-C
-
#import "ADEumInstrumentation.h" ... - (IBAction)loginUser:(id)sender { if(![ADEumInstrumentation screenshotsBlocked]) { [ADEumInstrumentation blockScreenshots]; } LoginCredentials creds = [UserLogin getUserCreds]; if(creds.authorized) { [LoginUser redirectToProfile:creds.user] [ADEumInstrumentation unblockScreenshots]; } } ...
- Swift
-
import ADEumInstrumentation ... @IBAction func loginUser(_ sender: UIButton) { if(!ADEumInstrumentation.screenshotsBlocked()) { ADEumInstrumentation.blockScreenshots() } let creds = UserLogin.getUserCreds() if(creds.authorized) { LoginUser.redirectToProfile(credits.user) ADEumInstrumentation.unblockScreenshots() } } ...
Disable the Agent to Stop Sending User Data to the Collector
You can disable the agent to stop sending all data to the collector while the agent is initialized and running. For example, you can disable the agent if your app has an option for users to opt-out of monitoring for privacy reasons.
shutdownAgent
- Objective-C
-
[ADEumInstrumentation shutdownAgent];
- Swift
-
ADEumInstrumentation.shutdownAgent()
- The call only stops the traffic out of the agent.
- Once the agent has been initialized, the call cannot be removed, and a license will have been consumed.
- If you want to make this state permanent for a device, add code in UserDefaults to save the state and use that flag to conditionally initialize the agent in your code.
restartAgent
- Objective-C
-
[ADEumInstrumentation restartAgent];
- Swift
-
ADEumInstrumentation.restartAgent();
- This call will respect the server side calls that can remotely shutdown the agent in a similar way.
- The call is only in effect while the app is running.
- The call will be ignored if the agent has been remotely disabled.
- If the call is removed from memory and the app restarts, or the device is rebooted, the agent will be initialized as normal.
Add a Crash Reporting Callback
You may want to make crash report information that Mobile RUM collects available to other parts of your code, for example, to Google Analytics, if you are using it. To enable you to pass on summary crash information, you can set up a crash report runtime callback. To get a callback when the iOS Agent detects and then reports a crash, you need to implement the following protocol in your code:
@protocol ADEumCrashReportCallback <NSObject>
- (void)onCrashesReported:(NSArray<ADEumCrashReportSummary *> *)crashReportSummaries;
@end
Each ADEumCrashReportSummary
passed in has the following properties:
@interface ADEumCrashReportSummary : NSObject
/** Uniquely defines the crash, can be used as key to find full crash report. */
@property (nonatomic, readonly) NSString *crashId;
/** The exception name, may be `nil` if no `NSException` occured. */
@property (nonatomic, readonly) NSString * ADEUM_NULLABLE exceptionName;
/** The exception reason, may be `nil` if no `NSException` occured. */
@property (nonatomic, readonly) NSString * ADEUM_NULLABLE exceptionReason;
/** The Mach exception signal name */
@property (nonatomic, readonly) NSString *signalName;
/** The Mach exception signal code */
@property (nonatomic, readonly) NSString *signalCode;
@end
If you are sending the information to another analytics tool, such as Google Analytics, it is best to include all five properties:
- exceptionName and exceptionReason are optional and useful for a quick identification of what the crash is. These are only present if the crash cause occurred within an exception reporting runtime, such as Objective-C.
- signalName and signalCode are useful for quick identification of the crash. These are from the system and are independent of the runtime.
-
For additional information, crashId can be used to look up the crash in the Controller UI.
For example, to print the crash information to iOS's logger, you could implement an ADEumCrashReportCallback
class like this:
// assumes the containing object has "adopted" the protocol
- (void)onCrashesReported:(NSArray<ADEumCrashReportSummary *> *)summaries {
for (ADEumCrashReportSummary *summary in summaries) {
NSLog(@"Crash ID: %@", summary.crashId);
NSLog(@"Signal: %@ (%@)", summary.signalName, summary.signalCode);
NSLog(@"Exception Name:\n%@", summary.exceptionName);
NSLog(@"Exception Reason:\n%@", summary.exceptionReason);
}
}
You set the object that implements the ADEumCrashReportCallback
protocol during agent configuration:
ADEumAgentConfiguration *config = [ADEumAgentConfiguration new];
config.crashReportCallback = myCrashReportCallback;
Your callback is invoked, on the main/UI thread, if a crash from a previous run is detected and collected. See the latest iOS SDK documentation.
Disable Crash Reporting
Crash reporting is enabled by default, but you can manually disable crash reporting through the instrumentation configuration. If you are using other crash reporting tools, you might disable crash reporting to minimize conflicts and optimize the crash report results.
crashReportingEnabled
property as shown in the following code example: - Objective-C
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey:appKey]; config.crashReportingEnabled = No [ADEumInstrumentation initWithConfiguration:config];
- Swift
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); config.crashReportingEnabled = false; ADEumInstrumentation.initWith(config);
Report Errors and Exceptions
The domain: String
used to initialize the standard NSError object used for reportError should not use unique string such as Hashes, StackTraceID or ThreadID. Including those will create an infinite number of Error Group which will impact performance.
To add unique error strings and values, you have two data collection options:
- Add a string value as a breadcrumb. If the event/error, you are reporting has a single value attached, add the value to leaveBreadcrumb. See Breadcrumbs
- Add string name and integer values. If the event/error, you are reporting has both name and integer values attached, add the values to
reportMetricWithName
. See Custom Metrics.
You can report exceptions using the method reportError from the ADEumInstrumentation
class. Reported exceptions will appear in session details.
The method can have the following two signatures:
Objective-C Function Signature | Description |
---|---|
(void)reportError:(NSError *)error withSeverity (ADEumErrorSeverityLevel)severity; |
Use this signature to report errors, set the severity level of the issue, and send the stack trace. This function signature sends the stack trace by default. If you don't want to send the stack trace, use the function signature below with the additional argument and StackTrace and set its value to NO. Warning: The
domain: String used to initialize the standard NSError object used for reportError should not use unique string such as Hashes, StackTraceID or ThreadID. Including those will create an infinite number of Error Group which will impact performance.To add unique error strings and values, you have two data collection options:
|
(void)reportError:(NSError *)error withSeverity:(ADEumErrorSeverityLevel)severity andStackTrace:(BOOL)stacktrace; |
Use this signature to report errors, set the severity level of the issue, and explicitly specify whether the stack trace should be included. If you include the stack trace with the reported error by setting stacktrace to YES, you can view the stack trace in the Code Issues Details dialog. To report the error without the stack trace, set stacktrace to NO. Warning: The string message used to construct a new throwable used for reportError should not use unique string such as hashes, StackTraceID or ThreadID. Including those will create an infinite number of Error Group which will impact performance.
To add unique error strings and values, you have two data collection options:
|
Severity Levels
You can also set one of the following severity levels for an issue. With the severity level, you can filter errors in the Code Issues Dashboard or Code Issues Analyze.
-
ADEumErrorSeverityLevelInfo
-
ADEumErrorSeverityLevelWarning
-
ADEumErrorSeverityLevelCritical
Examples of Reporting Errors
ADEumErrorSeverityLevelCritical
for a failed attempt to perform a file operation: - Objective-C
-
NSError *err = nil; [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"pathToFile" error:&err]; if (err) { [ADEumInstrumentation reportError:err withSeverity:ADEumErrorSeverityLevelCritical, andStackTrace: NO]; } else { ... }
- Swift
-
var err: Error? = nil try? FileManager.default.contentsOfDirectory(atPath: "pathToFile") if err != nil { ADEumInstrumentation.reportError(err, withSeverity: ADEumErrorSeverityLevelCritical, andStackTrace: false) } else { ... }
domain: String
used to initialize the standard NSError object used for reportError should not use unique string such as Hashes, StackTraceID or ThreadID. Including those will create an infinite number of Error Group which will impact performance.To add unique error strings and values, you have two data collection options:
- Add a string value as a breadcrumb. If the event/error, you are reporting has a single value attached, add the value to leaveBreadcrumb. See Breadcrumbs.
- Add string name and integer values. If the event/error, you are reporting has both name and integer values attached, add the values to reportMetricWithName. See Custom Metrics.
- Objective-C
-
NSString *domain = @"com.YourCompany.AddUsers.ErrorDomain"; NSString *desc = NSLocalizedString(@"Unable to add user.", @""); NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo]; [ADEumInstrumentation reportError:error withSeverity: ADEumErrorSeverityLevelWarning];
- Swift
-
var domain = "com.YourCompany.AddUsers.ErrorDomain" var desc = NSLocalizedString("Unable to add user.", comment: "") var userInfo = [NSLocalizedDescriptionKey: desc] var error = NSError(domain: domain, code: -101, userInfo: userInfo) ADEumInstrumentation.reportError(error, withSeverity: ADEumErrorSeverityLevelWarning)
Configure Application-Not-Responding (ANR) Detection
By default, the iOS Agent does not detect ANR issues, and when ANR detection is enabled, the ANR issues are reported without stack traces. You must manually enable ANR detection and set a flag to include stack traces through the iOS Agent configuration. For more information about ANR monitoring, see Code Issues. To specify thresholds for ANR issues, see Configure Application Not Responding Thresholds.
Enable ANR Detection
anrDetectionEnabled
property as shown below. - Objective-C
-
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; // Enable ANR detection adeumAgentConfig.anrDetectionEnabled = YES; [ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
- Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); // Enable ANR detection config.anrDetectionEnabled = true; ADEumInstrumentation.initWith(config);
Report Stack Traces with ANRs
anrStackTraceEnabled
to YES (Objective-C) or true (Swift) to report stack traces with the ANRs. - Objective-C
-
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; // Enable ANR detection adeumAgentConfig.anrDetectionEnabled = YES; // Set the flag to include stack traces with ANRs adeumAgentConfig.anrStackTraceEnabled = YES; [ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
- Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) // Enable ANR detection config.anrDetectionEnabled = true // Set the flag to include stack traces with ANRs config.anrStackTraceEnabled = true ADEumInstrumentation.initWith(config)
Configure Hybrid Application Support
By default, the iOS Agent instruments iOS WKWebViews, but does not collect and report Ajax calls. See Hybrid Application Support for an overview and an explanation of how it works.
You can configure the static or runtime configuration to disable hybrid application support or modify its behavior. The sections below show you how to change the defaults for hybrid support through either runtime or static configuration.
Runtime Configuration for Hybrid Application Support
The code example below disables the injection of the JavaScript Agent. By disabling the injection, the WKWebViews in your application will not be instrumented and Ajax calls will not be reported.
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
// Disable the JavaScript Agent Injection
adeumAgentConfig.jsAgentEnabled = NO;
[ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
The JavaScript Agent injection is enabled by default. To also enable the collection and reporting of Ajax calls:
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
// Enable the collection and reporting of Ajax calls
adeumAgentConfig.jsAgentAjaxEnabled = YES;
[ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
Static Configuration for Hybrid Application Support
You should use static configuration for the following reasons:
- force the instrumentation of WKWebViews and/or Ajax calls (override the runtime configuration).
- disable hybrid support and override the runtime configuration.
- set the URL to your self-hosted JavaScript Extension file.
The table below describes the supported properties and provides the default value for the info.plist file.
Property | Default Value | Description |
---|---|---|
serverJsAgentEnabled | true |
If the client receives a false for this flag, then the JavaScript Agent will be disabled. Thus, the WKWebViews and Ajax requests will not be monitored. The injection occurs during the creation of a new WKWebView. So, if a WKWebView is created when this flag is set to false, that particular WKWebView won't be instrumented even if the flag is subsequently set to true. |
ForceWebviewInstrumentation | false | When set to true , the iOS Agent will inject the JavaScript Agent into the WKWebViews regardless of the runtime configuration. |
ForceAjaxInstrumentation | true | When set to true , Ajax operations will always be collected and reported regardless of the runtime configuration. |
ADRUMExtUrlHttp |
The JavaScript Agent consists of two components: the base JavaScript Agent and the JavaScript Agent extension. The base JavaScript Agent is built into the Mobile Agent binary and injected according to the rules above. After initialization, the JavaScript Agent fetches the JavaScript Agent extension from the URLs specified by these properties. | |
ADRUMExtUrlHttps | https://cdn.appdynamics.com |
Example Configuration
The example info.plist below forces the instrumentation of WKWebViews (overriding the runtime configuration), but does not force the collection and reporting of Ajax requests. The configuration also sets the URL where the JavaScript Extension file is obtained.
<plist>
<dict>
...
<key>ADEUM_Settings</key>
<dict>
<key>ForceWebviewInstrumentation</key>
<true/>
<key>ForceAjaxInstrumentation</key>
<false/>
<key>ADRUMExtUrlHttp</key>
<string>http://<your-domain>/adrum.cdn</string>
<key>ADRUMExtUrlHttps</key>
<string>https://<your-domain>/adrum.cdn</string>
</dict>
...
</dict>
</plist>
Programmatically Control Sessions
By default, a mobile session ends after a period of user inactivity. For example, when a user opens your application, the session begins and only ends after the user stops using the app for a set period of time. When the user begins to use the application again, a new session begins.
Instead of having a period of inactivity to define the duration of a session, however, you can use the following API to programmatically control when sessions begin and end:
- (void)startNextSession
When you call the method startNextSession from the ADEumInstrumentation class, the current session ends and a new session begins. The API enables you to define and frame your sessions so that they align more closely with business goals and expected user flows. For example, you could use the API to define a session that tracks a purchase of a product or registers a new user.
Excessive use of this API will cause sessions to be throttled (excessive use is >10 calls per minute per iOS Agent, but is subject to change). When not using the API, sessions will fall back to the default of ending after a period of user inactivity.
Example of a Programmatically Controlled Session
- Objective-C
-
-(void) checkout { AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; NSString *checkoutUrl = [appDelegate.url stringByAppendingString:@"rest/cart/co/"]; NSURL *url = [NSURL URLWithString:checkoutUrl]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; NSURLResponse *response = nil; NSError *error = nil; NSData *body = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; const char *responseBytes = [body bytes]; if (responseBytes == nil) checkoutResponse = [NSString stringWithUTF8String:"Could not connect to the server"]; else { checkoutResponse = [NSString stringWithUTF8String:responseBytes]; [ADEumInstrumentation startNextSession]; } }
- Swift
-
func checkout() { let appDelegate = UIApplication.shared.delegate as? AppDelegate let checkoutUrl = appDelegate?.url ?? "" + ("rest/cart/co/") let url = URL(string: checkoutUrl) var request: NSMutableURLRequest? = nil if let url = url { request = NSMutableURLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60.0) } var response: URLResponse? = nil var error: Error? = nil var body: Data? = nil if let request = request { body = try? NSURLConnection.sendSynchronousRequest(request, returning: &response) } let responseBytes = Int8(body?.bytes ?? 0) if responseBytes == nil { checkoutResponse = String(utf8String: "Could not connect to the server") } else { checkoutResponse = String(utf8String: &responseBytes) ADEumInstrumentation.startNextSession() } }
Start and End Session Frames
You can use the SessionFrame API to create session frames that will appear in the session activity. Session frames provide context for what the user is doing during a session. With the API, you can improve the names of user screens and chronicle user flows within a business context.
Use Cases
The following are common use cases for the SessionFrame API.
- One ViewController performs multiple functions and you want more granular tracking of the individual functions.
- A user flow spans multiple ViewController or user interactions. For example, you could use the API to create the session frames "Login", "Product Selection", and "Purchase" to chronicle the user flow for purchases.
- You want to capture dynamic information based on user interactions to name session frames, such as an order ID.
SessionFrame API
- Objective-C
-
Class Method Description ADEumInstrumentation + (ADEumSessionFrame *)startSessionFrame:(NSString *)name
Use this to start and name your session frame. Naming session frames enable you to easily identify and track the frames in the Sessions Dialog.
ADEumSessionFrame - (void)updateName:(NSString *)name Rename the session frame name. You call this method from the ADEumSessionFrame object returned from startSessionFrame.
ADEumSessionFrame - (void)end End the session frame. You call this method from the ADEumSessionFrame object returned from startSessionFrame.
- Swift
-
Class Method Description ADEumInstrumentation startSessionFrame(_ name: String?) -> ADEumSessionFrame
Use this to start and name your session frame. Naming session frames enable you to easily identify and track the frames in theSessions Dialog. ADEumSessionFrame
updateName(_ name: String?)
Rename the session frame name. You call this method from the ADEumSessionFrame object returned from startSessionFrame. ADEumSessionFrame end() End the session frame. You call this method from the ADEumSessionFrame object returned from startSessionFrame.
Session Frame Example
- Objective-C
-
#import "ADEumSessionFrame.h" ... @property (nonatomic, strong) ADEumSessionFrame *checkoutSessionFrame; - (IBAction)checkoutCartButtonClicked:(id)sender { // The user starting to check out starts when the user clicks the checkout button // this may be after they have updated quantities of items in their cart, etc. checkoutSessionFrame = [ADEumInstrumentation startSessionFrame:@"Checkout"]; } - (IBAction)confirmOrderButtonClicked:(id)sender { // Once they have confirmed payment info and shipping information, and they // are clicking the "Confirm" button to start the backend process of checking out // we may know more information about the order itself, such as an Order ID. NSString *newSessionName = [NSString stringWithFormat:@"Checkout: Order ID %@",orderId]; [checkoutSessionFrame updateName:newSessionName]; } - (void)processOrderCompleted { // Once the order is processed, the user is done "checking out" so we end // the session frame [checkoutSessionFrame end]; checkoutSessionFrame = nil; } - (void)checkoutCancelled { // If they cancel or go back, you'll want to end the session frame also, or else // it will be left open and appear to have never ended. [checkoutSessionFrame end]; checkoutSessionFrame = nil; }
- Swift
-
import ADEumSessionFrame ... var checkoutSessionFrame: ADEumSessionFrame? @IBAction func checkoutCartButtonClicked(_ sender: UIButton) { // The check out starts when the user clicks the checkout button. // This may be after they have updated quantities of items in their cart, etc. checkoutSessionFrame = ADEumInstrumentation.startSessionFrame("Checkout") } @IBAction func confirmOrderButtonClicked(_ sender: UIButton) { // Once users have confirmed payment info and shipping information, and they // are clicking the "Confirm" button to start the backend process of checking out, // we may know more information about the order itself, such as an order ID. let newSessionName = "Checkout: Order ID \(orderId)" checkoutSessionFrame.updateName(newSessionName) } func processOrderCompleted() { // Once the order is processed, the user is done "checking out", so we end the session frame. checkoutSessionFrame.end() checkoutSessionFrame = nil } func checkoutCancelled() { // If they cancel or go back, you'll want to end the session frame also, or else it will be // left open and appear to have never ended. checkoutSessionFrame.end() checkoutSessionFrame = nil }
Configure the Agent for Custom App Names
By default, Splunk AppDynamics detects the application name by extracting the last segment from the bundle ID. There may be cases, however, where you deploy the same app binary with different bundle IDs to various regional app stores. To make sure all the data belonging to one app is collected and displayed together, despite varying bundle IDs, you can set a common name by giving the apps a custom name. To do this, set the application name property in the ADEumAgentConfiguration
instance that you use to set up ADEumInstrumentation
. See the latest iOS SDK documentation for more information.
com.example.appdynamics.HelloWorld
, the application name in the UI will display "HelloWorld." This applies to both default and custom app names.@property (nonatomic, strong) NSString *applicationName;
Configure the Agent for Ignoring Some HTTP Requests
In some cases, HTTP requests using NSURL are used for internal purposes in an application and do not represent actual network requests. Metrics created based on these requests are not normally useful in tracking down issues, so preventing data on them from being collected can be useful. To ignore specific NSURL requests, set the excluded URL patterns property in the ADEumAgentConfiguration instance that you use to set up ADEumInstrumentation. Use the simplest regex possible. See the latest iOS SDK documentation.
@property (nonatomic, strong) NSSet * excludedUrlPatterns;
Use the Agent with a Custom HTTP Library
The iOS Agent automatically detects network requests when the underlying implementation is handled by either by the NSURLConnection or the NSURLSession classes. This covers the great majority of iOS network requests. In some cases, however, mobile applications use custom HTTP libraries.
- To have the iOS Agent detect requests from a custom library, add request tracking code to your application manually, using the
ADEumHTTPRequestTracker
class. - To set headers to allow correlation with server-side processing, use the
ADEumServerCorrelationHeaders
class. - To configure the agent to use your custom library to deliver its beacons over HTTP, use the
ADEumCollectorChannel
protocol and theADEumAgentConfiguration
class.
Add Request Tracking
To add request tracking manually, you tell the agent when the request begins and when it ends. You also set properties to tell the agent the status of the response.
Start Tracking a Request
To begin tracking an HTTP request, call the following method immediately before sending the request.
ADEumInstrumentation
's initWithKey
methods before using this method.@interface ADEumHTTPRequestTracker : NSObject
...
+ (ADEumHTTPRequestTracker *)requestTrackerWithURL:(NSURL *)url;
Where url is the URL being requested. This parameter must not be nil.
To complete tracking an HTTP request, immediately after receiving a response or an error, set the appropriate properties on the tracker object and call the following method to report the outcome of the request back to the agent. You should not continue to use this object after calling this method. To track another request, call requestTrackerWithURL
again.
- (void)reportDone;
Set Request Tracker Properties
The following properties should be set on the requestTrackerWithURL
object to describe to the agent the results of the call:
@property (copy, nonatomic) NSError *error;
Indicates the failure to receive a response, if this occurred. If the request was successful, this should be nil
.
@property (copy, nonatomic) NSNumber *statusCode;
Reports the HTTP status code of the response, if one was received.
-
If a response was received, this should be an integer.
-
If an error occurred and a response was not received, this should be nil.
@property (copy, nonatomic) NSDictionary *allHeaderFields;
Provides a dictionary representing the keys and values from the server's response header. The format of this dictionary should be identical to the allHTTPHeadersFields
property of NSURLRequest. The dictionary elements consist of key/value pairs, where the key is the header key name and the value is the header value.
If an error occurred and a response was not received, this should be nil.
Example
Given a request snippet like this:
- (NSData *)sendRequest:(NSURL *) url error:(NSError **)error {
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
} else {
result = responseBody;
}
return result;
}
Adding the tracker could look something like this:
- (NSData *)sendRequest:(NSURL *)url error:(NSError **)error {
ADEumHTTPRequestTracker *tracker = [ADEumHTTPRequestTracker requestTrackerWithURL:url];
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
tracker.error = theError;
} else {
tracker.statusCode = theStatusCode;
tracker.allHeaderFields = theResponseHeaders;
result = responseBody;
}
[tracker reportDone];
return result;
}
Enable Server-Side Correlation
To enable correlation between your request and server-side processing, add specific headers to outgoing requests that the server-side agent can detect and return the headers obtained from the server-side agent in the response to make them available to the iOS Agent.
This is done automatically for standard HTTP libraries.
@interface ADEumServerCorrelationHeaders : NSObject
+ (NSDictionary *)generate;
@end
You must:
-
Call the generate method and set the generated headers before sending a request to the backend.
-
Report back the response headers, using the allHeaderFields property shown above.
Attach Custom Data to a Network Request
You can attach custom data to a network request by calling one (or multiple) of the following methods to add attributes to ADEumHTTPRequestTracker
:
- (NSData *)sendRequest:(NSURL *)url error:(NSError **)error {
ADEumHTTPRequestTracker *tracker = [ADEumHTTPRequestTracker requestTrackerWithURL:url];
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
tracker.error = theError;
} else {
tracker.statusCode = theStatusCode;
tracker.allHeaderFields = theResponseHeaders;
result = responseBody;
}
// Custom data can be added to this one request.
// Different types can be used.
// The data added will only appear for this network request and will not persist.
[tracker setUserData:@"trackerStringKey" value:@"Test String Value"];
[tracker setUserDataLong:@"trackerLongKey" value:66004024];
[tracker setUserDataBoolean:@"trackerBooleanKey" value:1];
[tracker setUserDataDouble:@"trackerDoubleKey" value:5905400.6];
[tracker setUserDataDate:@"trackerDateKey" value:[NSDate date]];
[tracker reportDone];
return result;
}
Configure Beacon Channel to Use Custom HTTP Library
The iOS Agent uses HTTP to deliver its beacons. To have the agent use your custom HTTP library for this purpose, do the following.
-
Implement a class that conforms to this protocol:
/** * Protocol for customizing the connection between the agent SDK and the collector. */ @protocol ADEumCollectorChannel <NSObject> /** * Sends a request synchronously and returns the response received, or an error. * * The semantics of this method are exactly equivalent to NSURLConnection's * sendSynchronousRequest:returningResponse:error: method. * * @param request The URL request to load. * @param response Out parameter for the URL response returned by the server. * @param error Out parameter used if an error occurs while processing the request. May be NULL. */ - (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error; @end
- Set the collectorChannel property in
ADEumAgentConfiguration
before initializingADEumInstrumentation
, passing in an instance of your class that implementsADEumCollectorChannel
. See the latest iOS SDK documentation.@property (nonatomic, strong) id<ADEumCollectorChannel> collectorChannel;
Transform URLs for Network Requests
When your application makes network requests, you may not want to report URLs containing sensitive information to the EUM Server. You can instead transform the network request URL before reporting it or ignore it altogether.
To do so:
- Implement a network request callback that modifies or ignores specific URLs.
- Register the network request callback in the initialization code.
Implement the Network Request Callback
The callback that modifies or ignore specific URLs is an implementation of the protocol below. The callback method networkRequestCallback
is synchronous, so it is recommended that you return from the function quickly.
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest
Transforming URLs
The networkRequestCallback
method, in general, should follow the steps below to transform URLs:
- Identify specific URLs using techniques such as regex or pattern matching.
- Modify the url property of the
ADEumHTTPRequestTracker
object. (Modifying other properties of theADEumHTTPRequestTracker
object will be ignored). - Assign a valid URL to the url property.
- If you need the request and response headers, use the following fields:
-
allHeaderFields
- It returns the response headers. -
allRequestHeaderFields
- It returns the request headers.
-
- Return YES (Objective-C) or true (Swift).
The first step is optional as you could choose to transform the URLs of all network requests.
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *maskURL = @"http://networkrequest-mask.com"; NSURL *url = [NSURL URLWithString:maskURL]; networkRequest.url = url; return YES; }
- Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let maskURL = "http://networkrequest-mask.com" let url = URL(string: maskURL) networkRequest?.url = url return true }
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *urlString = networkRequest.url.absoluteString; BOOL returnBeacon = YES; NSString *maskURL = @"http://customer-account.com"; if (!([urlString rangeOfString:@"accountInfo"].location == NSNotFound)) { networkRequest.url = [NSURL URLWithString:maskURL]; } return returnBeacon; }
- Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let urlString = networkRequest?.url.absoluteString returnBeacon = true let maskURL = "http://customer-account.com" if !(Int((urlString as NSString?)?.range(of: "accountInfo").location ?? 0) == NSNotFound) { networkRequest?.url = URL(string: maskURL) } return returnBeacon }
Ignoring URLs
If the networkRequestCallback
method returns false, the beacon is dropped. The general process for ignoring beacons is as follows:
-
Identify specific URLs using techniques such as regex or pattern matching.
- Return false.
You could theoretically ignore all network requests by having the callback networkRequestCallback
always return NO (Objective-C) or false (Swift):
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { return NO; }
- Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { return false }
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *urlString = networkRequest.url.absoluteString; BOOL returnBeacon = YES; if (!([urlString rangeOfString:@"avatar"].location == NSNotFound)) { returnBeacon = NO; } return returnBeacon; }
- Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let urlString = networkRequest?.url.absoluteString var returnBeacon = true if !(Int((urlString as NSString?)?.range(of: "avatar").location ?? 0) == NSNotFound) { returnBeacon = false } return returnBeacon }
Register the Callback
ADEumHTTPRequestTracker
object. - Objective-C
-
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.networkRequestCallback = self; [ADEumInstrumentation initWithConfiguration:config];
- Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) config.networkRequestCallback = self ADEumInstrumentation.initWith(config)
Enable Logging and Set Logging Level
You use the method loggingLevel to enable and set the logging level. You can set logging to one of the following levels:
- ADEumLoggingLevelOff
- ADEumLoggingLevelAll
- ADEumLoggingLevelVerbose
- ADEumLoggingLevelDebug
- ADEumLoggingLevelInfo
- ADEumLoggingLevelWarn
- ADEumLoggingLevelError
Examples
- Objective-C
-
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // appKey should be assigned your EUM app key ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.loggingLevel = ADEumLoggingLevelAll; [ADEumInstrumentation initWithConfiguration:config]; ... }
- Swift
-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { { // appKey should be assigned your EUM app key let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) config.loggingLevel = .all ADEumInstrumentation.initWithConfiguration(config) ... return true }
iOS SDK Documentation
See the latest iOS SDK documentation or the previous versions listed below:
- https://sdkdocs.appdynamics.com/ios-sdk/22.12/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.5/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.3/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.2/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.1/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.12/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.8/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.6/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.5/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.2/html/