iOS manual instrumentation

Two classes defined in Dynatrace.h instrument your iOS app.

  • The Dynatrace class manages the Mobile Agent's operation.
  • The DTXAction class creates actions and report against them.

Swift apps manual instrumentation

Create a bridging header that contains the Mobile Agent header. For example:

#ifndef myApp_Bridging_Header_h
#define myApp_Bridging_Header_h

#import "Dynatrace.h"

#endif
  • In the project navigator, select your project and your target.
  • Select Build Settings.
  • Add $(SRCROOT)/myApp/Bridging-Header.h to the Objective-C Bridging Header setting.

Dynatrace

setMonitorCookie

+ (void) setMonitorCookie:(NSString *)cookieString
Parameter Description
cookieString A cookie string such as MIGRATION_FLAG=3 or n1=v1; n2=v2. Pass nil to remove the Cookie header from requests.

Set the value of the HTTP Cookie header to be sent along with every HTTP GET/POST request made by the Mobile Agent when establishing contact with the backend or sending data to it. This method is only needed if your network infrastructure requires the cookie. The method must be invoked before startupWithApplicationName:serverURL:allowAnyCert:certificatePath: to ensure that the earliest requests include the cookie. It can be invoked again to change the cookie string at any time.

startupWithApplicationName:serverURL:allowAnyCert:certificatePath

+ (DTX_StatusCode) startupWithApplicationName:(NSString *)applicationId
                                      serverURL:(NSString *)serverURL
                                   allowAnyCert:(BOOL)allowAnyCert
                                certificatePath:(NSString *)pathToCertificateAsDER

Auto-instrumentation should be preferred over manual call of startup method

Use the settings keys from Auto-Instrumentation for automatic mobile agent startup instead of the manual startup call.

Parameter Description
applicationName A user-defined application identifier, such as @EasyTravel. Do not use underscores (_) in the application name.
serverURL The path to the placed UEM agent, for example http://myhost.mydomain.com:8080/myApplication]. In the AppMon Client, you can find the path in System Profile Preferences > User Experience > JavaScript Agent > Agent Location. Be sure to include the transport mechanism (http or https) in the path.
allowAnyCert Allow any certificate for https communication. This parameter is only evaluated if you specified the https transport mechanism in the server name.
pathToCertificateAsDER Path to a (self-signed) certificate in DER format; or null.

Call this method to initialize the Mobile Agent. This method must be invoked before any other methods are called. Multiple calls to this method are ignored if the Mobile Agent was not shut down before. A certificate in DER format that is used as an additional anchor to validate HTTPS communication is needed if allowAnyCert is false and a self-signed certificate is used.
For example:

NSString *pathToCertificateAsDER = [[NSBundle mainBundle] pathForResource:@"easyTravelServerCert" ofType:@"der"];

startupWithApplicationName:serverURL:authenticationClassDelegate

+ (DTX_StatusCode) startupWithApplicationName:(NSString *)applicationId
                                      serverURL:(NSString *)serverURL
                    authenticationClassDelegate:(Class)callbackDelegate

Parameter Description
applicationName A user-defined application identifier, for example @"EasyTravel".
serverURL The path to the placed UEM agent, for example http://myhost.mydomain.com:8080/myApplication. In the AppMon Client, you can find the path in System Profile Preferences > User Experience > JavaScript Agent > Agent Location. Be sure to include the transport mechanism (http or https) in the path.
authenticationClassDelegate The name of the class that implements a class method with the following selector:
+ (void)requestAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Add <DTXAuthenticationClassDelegate> to your class interface.

Call this method to initialize the Mobile Agent. This method must be invoked before any other methods are called. Multiple calls to this method are ignored if the Mobile Agent was not previously shut down. The authenticationClassDelegate allows for advance authentication not provided by the previous startWithApplicationName method. See the Advanced authentication section for more information.

shutdown

+ (DTX_StatusCode) shutdown

Call this method to shut down the Mobile Agent. Collected data is flushed to the AppMon Server. This function does not return until the attempt to send data either succeeds or fails. Call this method on the main thread makes your UI unresponsive. If the attempt fails, the data is retained and sent later if the Mobile Agent restarts before the data becomes too old.

enableCrashReportingWithReport

Depending on the syntax used, the enableCrashReportingWithReport method can send crash reports to the AppMon Server, HockeyApp server, Quincy server, or Victory server, or email crash reports to specified recipients.

AppMon Server

+ (DTX_StatusCode) enableCrashReportingWithReport:(BOOL)sendCrashReport
Parameter Description
sendCrashReport true to send a complete crash report. false to send only a crash event without full details.

This methods activates the KSCrash framework within the Mobile Agent to capture signals and unhandled exceptions. When a crash occurs, a report is saved on the device. The next time the app is started, the report is processed. If the parameter is true, then the complete iOS crash report is sent to the AppMon Server. Otherwise a crash event containing some information about the crashed thread is sent instead of the complete report. If the same exception reoccurs within seven days, the crash data is not reported again but a crash event is reported that references the previously reported crash data.

HockeyApp

+ (DTX_StatusCode) enableCrashReportingWithReport:(BOOL)sendCrashReport
                               hockeyAppIdentifier:(NSString *)appIdentifier
                          hockeyWaitUntilReachable:(BOOL)waitUntilReachable
                                      hockeyUserId:(NSString *)userId
                                hockeyContactEmail:(NSString *)contactEmail
                            hockeyCrashDescription:(NSString *)description
Parameter Description
sendCrashReport true to send a complete crash report. false to send only a crash event without full details.
appIdentifier The application identifier assigned by HockeyApp to your application.
waitUntilReachable true to wait until HockeyApp is reachable before sending. false to attempt sending whether available or not; simply fails if not available.
userId Optional HockeyApp user ID. Set to nil if not needed.
contactEmail Optional HockeyApp user contact email address. Set to nil if not needed.
description Optional description to include with the crash report for HockeyApp.

Activates the KSCrash framework within the Mobile Agent to capture signals and unhandled exceptions. When a crash occurs, a report is saved on the device. When a crash occurs, a report is saved on the device. The report processes the next time the app starts. Setting the sendCrashReport parameter is true sends the complete iOS crash report to the AppMon Server. Otherwise a crash event containing some information about the crashed thread is sent instead of the complete report. If the same exception reoccurs within seven days, the crash data is not reported again but a crash event is reported that references the previously reported crash data. The crash report is also sent to HockeyApp using the parameters provided.

Quincy

+ (DTX_StatusCode) enableCrashReportingWithReport:(BOOL)sendCrashReport
                                         quincyURL:(NSString *)serverURL
                          quincyWaitUntilReachable:(BOOL)waitUntilReachable
                                      quincyUserId:(NSString *)userId
                                quincyContactEmail:(NSString *)contactEmail
                            quincyCrashDescription:(NSString *)description
Parameter Description
sendCrashReport true to send a complete crash report. false to send only a crash event without full details.
serverURL The Quincy server URL.
waitUntilReachable true to wait until the Quincy server is reachable before sending. false to attempt sending whether available or not; simply fails if not available.
userId Optional Quincy user ID. Set to nil if not needed.
contactEmail Optional Quincy user contact email address. Set to nil if not needed.
description

Activates the KSCrash framework within the Mobile Agent to capture signals and unhandled exceptions. When a crash occurs, a report is saved on the device. The report processes the next time the app starts. Setting the sendCrashReport parameter is true sends the complete iOS crash report to the AppMon Server. Otherwise a crash event containing some information about the crashed thread is sent instead of the complete report. If the same exception reoccurs within seven days, the crash data is not reported again but a crash event is reported that references the previously reported crash data. The crash report is also sent to the Quincy server using the parameters provided.

Victory

+ (DTX_StatusCode) enableCrashReportingWithReport:(BOOL)sendCrashReport
                                        victoryURL:(NSString *)serverURL
                                   victoryUserName:(NSString *)userName
                                  victoryUserEmail:(NSString *)userEmail
Parameter Description
sendCrashReport true to send a complete crash report. false to send only a crash event without full details.
serverURL The Victory server URL.
userName Optional Victory user name. If not set, the value of UIDevice.currentDevice.name is used.
userEmail Optional Victory user contact email address.

Activates the KSCrash framework within the Mobile Agent to capture signals and unhandled exceptions. When a crash occurs, a report is saved on the device. The report processes the next time the app starts. Setting the sendCrashReport parameter is true sends the complete iOS crash report to the AppMon Server. Otherwise a crash event containing some information about the crashed thread is sent instead of the complete report. If the same exception reoccurs within seven days, the crash data is not reported again but a crash event is reported that references the previously reported crash data.  The crash report is also sent to the Victory server using the parameters provided.

Email crash report

+ (DTX_StatusCode) enableCrashReportingWithReport:(BOOL)sendCrashReport
                                   emailRecipients:(NSArray *)recipients
                                      emailSubject:(NSString *)subject
                                      emailMessage:(NSString *)message
                                     emailFilename:(NSString *)filename
                              sendAppleStyleReport:(BOOL)sendAppleStyleReport
                                        alertTitle:(NSString *)alertTitle
                                      alertMessage:(NSString *)alertMessage
                                     yesButtonText:(NSString *)yesButtonText
                                      noButtonText:(NSString *)noButtonText
Parameter Description
sendCrashReport true to send a complete crash report. false to send only a crash event without full details.
recipients An array of NSStrings, each containing one email address.
subject The email subject.
message The email body text.
filename The name to use for the file attachment that contains the crash report. Set to nil to use the default name.
sendAppleStyleReport true to send an Apple-style crash report. false to send a JSON-style crash report.
alertTitle Title for the alert.
alertMessage Message for the alert.
yesButtonText Label for the yes button in the alert. If the user taps this button, then the email UI appears.
noButtonText Label for the no button in the alert. If the user taps this button, then the crash report is not emailed. It is still sent to AppMon.

Activates the KSCrash framework within the Mobile Agent to capture signals and unhandled exceptions. When a crash occurs, a report is saved on the device. The report processes the next time the app starts. Setting the sendCrashReport parameter is true sends the complete iOS crash report to the AppMon Server. Otherwise a crash event containing some information about the crashed thread is sent instead of the complete report. If the same exception reoccurs within seven days, the crash data is not reported again but a crash event is reported that references the previously reported crash data.

You can also email the report using the settings you provide. First, an alert dialog box appears. If you press the yes button, the email UI appears, pre-populated with the values specified.  Press Send to send the crash report.

setGpsLocation

+ (DTX_StatusCode) setGpsLocation:(id)gpsLocation
Parameter Description
gpsLocation CLLocation object with GPS coordinates acquired by the customer's application.

Use this method to record the current GPS location of the user. The Mobile Agent does not automatically collect any location information. The GPS location is currently not evaluated by the AppMon Server, but the data might be available in a future AppMon release.

lastErrorCode

+ (DTX_StatusCode) lastErrorCode

Return the error code associated with the most recent internal Mobile Agent error. Zero (0) is returned if there is no error.

lastErrorMsg

+ (NSString *) lastErrorMsg

Return the error message associated with the most recent internal Mobile Agent error. It returns nothing if there is no error.

flushEvents

+ (DTX_StatusCode) flushEvents

Send all collected events immediately. To reduce network traffic/usage, the collected events are usually sent in packages where the oldest event has an age of up to nine minutes. Use this method to force sending of all collected events regardless of their age.

DTXAction

enterActionWithName

+ (DTXAction *) enterActionWithName:(NSString *)actionName
+ (DTXAction *) enterActionWithName:(NSString *)actionName
                       parentAction:(DTXAction *)parentAction

Parameter Description
actionName The name of the action.
parentAction The DTX Action object for the parent of this action.
Error Value Description
DTX_Error_InvalidParameter The parentAction is nil, already ended, or the action name is nil or empty.
DTX_CaptureOff The Mobile Agent is not initialized.
DTX_TruncatedEventName The action name was truncated to the maximum length. This is a warning; the action is still created.
DTX_Error_InternalError An Mobile Agent internal error occurred.

Start an action, which results in a mobile action PurePath in AppMon. Call this method at the beginning of the code that you want to time. You must set the action end using a call to leaveAction. A value is returned if the action is created successfully, but not if an error occurs. Call lastErrorCode or lastErrorMsg to find out which error occurred.

The returned DTXAction is retained, therefore it has to be released if no longer needed (only valid for non-ARC environments).

leaveAction

- (DTX_StatusCode) leaveAction

End a previously started action. All reported events, values, or tagged web requests between the start and end of an action are part of the action, and nested in the mobile action PurePath. Call this method at the end of the code that you want to time.

endVisit

- (DTX_StatusCode) endVisit
+ (DTX_StatusCode) endVisit

Ends the current visit, closes all current actions, flushes pending data to the server, and then begins a new visit.

reportEventWithName

- (DTX_StatusCode) reportEventWithName:(NSString *)eventName
Parameter Description
eventName The name of the event.

Send an event to AppMon, which results in a node of a mobile action PurePath.

reportValueWithName

- (DTX_StatusCode) reportValueWithName:(NSString *)valueName
                               intValue:(NSInteger)value
- (DTX_StatusCode) reportValueWithName:(NSString *)valueName
                            doubleValue:(double)doubleValue
- (DTX_StatusCode) reportValueWithName:(NSString *)valueName
                            stringValue:(NSString *)stringValue

Parameter Description
valueName The name of the value.
intValue An integer value in the range of 0‐2147483.
doubleValue Double value.
stringValue String value.

Send a key/value pair to AppMon, which results in a node of a mobile action PurePath. The value can be processed by a measure and be charted.

reportErrorWithName

- (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                            errorValue:(NSInteger)errorValue
- (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                             exception:(NSException *)exception
- (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                                 error:(NSError *)error
+ (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                            errorValue:(NSInteger)errorValue
+ (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                             exception:(NSException *)exception
+ (DTX_StatusCode) reportErrorWithName:(NSString *)errorName
                                 error:(NSError *)error
Parameter Description
errorName The name of the value.
errorValue The integer error value.
exception The description of this exception is passed to the Server.
error The localizedDescription of this exception is passed to the Server.

Send an named error to AppMon, which results in a node of a mobile action PurePath. There are instance and class versions of these methods.

getRequestTagHeader

- (NSString *) getRequestTagHeader

Return the name of the HTTP header that you must add to a Web request for the AppMon Server to link it to the DTXAction in the PurePath. You should not normally need this method, because the automatic request tagging should catch all web requests.

getRequestTagValue

- (NSString *) getRequestTagValue

Return the value to assign to the HTTP header returned by getRequestTagHeader, for the AppMon Server to link it to the DTXAction in the PurePath. You should not normally need this because the automatic request tagging should catch all web requests.

getDTXWebRequestTiming

+ (NULLABLE DTXWebRequestTiming *)getDTXWebRequestTiming:(NONNULL NSString *)requestTagString
                                              requestUrl:(NULLABLE NSURL *)requestUrl

Creates an instance of the DTXWebRequestTiming object. Parameter requestTagString is the value of the HTTP header that has to was to the web request. This obtained from the getRequestTagValueForURL method.

startWebRequestTiming

- (DTX_StatusCode)startWebRequestTiming

The Dynatrace OneAgent automatically times web requests made using NSURLRequest, NSURLConnection, NSURLProtocol, NSString, UIWebView, and ASIHTTPRequest. If you use an alternate technology to make web requests and want to time them, use the getRequestTagHeader method, adding that information to your request, and then this method to start the timing.

stopWebRequestTiming

- (DTX_StatusCode)stopWebRequestTiming:(NULLABLE NSString *)errorCode

The Dynatrace OneAgent automatically times web requests made using NSURLRequest, NSURLConnection, NSURLProtocol, NSString, UIWebView, and ASIHTTPRequest. If you use an alternate technology to make web requests and want to time them, use the getRequestTagHeader method, adding that information to your request, and then this method to stop the timing and send the information to the mobile action PurePath.

Instrumentation example for manual web request tagging and timing (in Swift)

For 3rd party web request frameworks that are not automatically tagged this can be done manually. As AppMon does not display parentless web request events in the User Action PurePaths dashlet this requires 3 steps:

  • create a manual action
  • create client side (mobile App) part of web request event (web request timing)
  • add tag to web request
/**
 * simplified manual tagging demo
 */
<!-- *force italic end in Atom* -->
import UIKit
import WebKit
import Dynatrace

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let wkWebView = WKWebView(frame: self.view.frame)
        self.view.addSubview(wkWebView)

        manualTaggingDemo(wkWebView: wkWebView)
    }

    func manualTaggingDemo(wkWebView: WKWebView) {
        let parentAction = DTXAction.enter(withName: #function)

        let url = URL(string: "https://www.dynatrace.com")
        downloadRequest(url: url!, wkWebView: wkWebView, parentAction: parentAction)  //as this is async parent action should be left when request is done
    }

    func downloadRequest(url: URL, wkWebView: WKWebView, parentAction: DTXAction?) {
        let childAction = DTXAction.enter(withName: #function, parentAction: parentAction)  //add child action to see method call trace
        let session = URLSession.shared
        let request = NSMutableURLRequest(url: url)
        request.httpMethod = "GET"
        request.cachePolicy = .reloadIgnoringCacheData

        var webrequestTiming: DTXWebRequestTiming?
        if let dynatraceHeaderValue = Dynatrace.getRequestTagValue(for: url) {
            let dynatraceHeaderKey = Dynatrace.getRequestTagHeader() //this could be cached as it always is x-dynatrace
            request.addValue(dynatraceHeaderValue, forHTTPHeaderField: dynatraceHeaderKey)
            webrequestTiming = DTXWebRequestTiming.getDTXWebRequestTiming(dynatraceHeaderValue, request: url)
        }

        let task = session.downloadTask(with: request as URLRequest) {
            (location, response, error) in

            defer {
                parentAction?.leave()   //leave parent action when request finished - all child actions are automaticlly left on leaving parent
            }

            guard let _:URL = location, let _:URLResponse = response, error == nil else {
                webrequestTiming?.stop("failed") //stop webrequest timing in error case
                return
            }

            let urlContents = try! String(contentsOf: location!, encoding: .utf8)

            guard !urlContents.isEmpty else {
                webrequestTiming?.stop("empty content") //stop webrequest timing in error case
                return
            }

            webrequestTiming?.stop(nil) //stop webrequest when request finished

            wkWebView.loadHTMLString(urlContents, baseURL: nil)
        }

        webrequestTiming?.start()    //start webrequest timing
        task.resume()
    }

}

Return code definitions

Codes Value Description
DTX_CaptureOff 1 Data capturing is off.
DTX_CaptureOn 2 Data capturing is on, or the method call was successful.
DTX_CrashReportingUnavailable 4 The KSCrash framework could not be initialized.
DTX_CrashReportingAvailable 5 The KSCrash framework initialized successfully.
DTX_Error_NotInitialized -1 The Mobile Agent failed to initialize properly.
DTX_Error_InvalidRange -2 The integer value is outside of the valid range.
DTX_Error_InternalError -3 An error occurred within the Mobile Agent. Check the log for further details.
DTX_Error_ActionNotFound -4 When using the JavaScript bridge, this means the action name was not found.
DTX_Error_InvalidParameter -5 An invalid parameter was passed to an Mobile Agent method.
DTX_Error_ActionEnded -6 An operation is being performed on an action already ended by the leaveAction method.
DTXR_ReportErrorOff -8 The AppMon Server has disabled error reporting.
DTX_TruncatedEventName -9 An action, error, or value name is too long and has been truncated.

Logging

third-party

You can enable logging and set the logging level by adding the DTXLogLevel key to the application’s info.plist file. See Auto-Instrumentation for iOS.

Instrumentation Example

The example code below creates a User Action PurePath that looks like this:

Example PurePath
Example PurePath
#import "Dynatrace.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    // initialize Dynatrace here or anywhere else (mind that actions and events that occur before initialization are not reported to dynaTrace.)
    [Dynatrace startupWithApplicationName:@"easyTravel"
                                   serverURL:@"http://easytravel.example.com:8080/eT/"
                                allowAnyCert:NO
                             certificatePath:nil];
    ...
}

- (IBAction)performSearch:(id)sender
{
    ...
    // [1a] start action "search"
    DTXAction *action = [DTXAction enterActionWithName:@"search"];

    // [2] named event inside an action
    [action reportEventWithName:@"searchStart"];

    // [3a] starting sub-action
    DTXAction *child = [DTXAction enterActionWithName:@"searchRequest"
                                         parentAction:action];

    // [4] NSURLRequest and NSMutableURLReqest are tagged automatically
    NSError* error;
    NSHTTPURLResponse* response;
    NSURL *searchURL = [[NSURL URLWithString:
                         [NSString stringWithFormat:@"http://127.0.0.1:8080/ajax/TimeService?tz=GMT",
                          [sender query]]] retain];
    NSData *data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:searchURL]
                                         returningResponse:&response error:&error];
    [searchURL release];
    if(data == nil){
        // [x] report an error if communication fails
        [action reportErrorWithName:@"CommunicationError" errorValue:[error code]];
    }

    // [5] custom value inside an action
    [child reportValueWithName:@"responseCode" intValue:[response statusCode]];

    // [3b] end action "searchRequest"
    [child leaveAction];

    // [6] named event on successful completion
    [action reportEventWithName:@"searchEnd"];

    // [1b] end action "search"
    [action leaveAction];
    ...
}

Lifecycle instrumentation

Lifecycle Instrumentation is performed by Auto-Instrumentation.

Use the auto-startup feature or initialize the mobile agent very early in your application to capture the initial view appearance. Calling startupWithApplicationName from main() is acceptable.

Overhead

Adding the iOS Mobile Agent to an application increases the size of the application binary as follows:

Architecture Size
armv7 801 KB
armv7s 801 KB
arm64 976 KB
All three 2576 KB

Actions, events, and errors created automatically or through the Mobile Agent API are stored in a SQLite database in the application's Document directory. On average, each of these items takes up about 150 bytes in the database. This size can vary depending on the length of the strings included in items such as names and error messages.

By default, the Mobile Agent stores items in the database up to two minutes before sending them to the Server and deleting them. You can manage this send interval in the AppMon Client.

Advanced authentication

Configuring advanced authentication disables the Mobile Agent internal handling of authentication. For example, the DTXAllowAnyCert setting is ignored and must be handled in the callback.

There are two ways to enable advanced authentication. Both ways involve creating or modifying an existing class to implement the DTXAuthenticationClassDelegate protocol and contains a method called requestAuthenticationChallenge.

...
@interface AuthenticationManager : NSObject <DTXAuthenticationClassDelegate>
...
@end
#import "AuthenticationManager.h"

@implementation AuthenticationManager
...
+ (void)requestAuthenticationChallenge:(NONNULL NSURLAuthenticationChallenge *)challenge {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        // Handle server side authentication
        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
        return;
    } else if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
		// Handle client side authentication

        [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
        return;
    }

    // If everything fails, cancel the challenge.
    [[challenge sender] cancelAuthenticationChallenge:challenge];
}
...
@end

If using auto-instrumentation, add the following to your applications info.plist.

DTXAuthenticationClassDelegate = AuthenticationManager

Otherwise, you can call startupWithApplicationName:serverURL:authenticationClassDelegate: to configure and start the Mobile Agent.

#import "Dynatrace.h"
#import "AuthenticationManager.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    // initialize Dynatrace here or anywhere else (mind that actions and events that occur before initialization are not reported to dynaTrace.)
    [Dynatrace startupWithApplicationName:@"easyTravel"
                                   serverURL:@"http://easytravel.example.com:8080/eT/"
                 authenticationClassDelegate:[AuthenticationManager class]];
    ...
}