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

AppMon 2018 October Universal startup API

startupWithConfig

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.

+ (DTX_StatusCode)startupWithConfig:(NONNULL NSDictionary<NSString*,id>*)config;

Configuration dictionary

The configuration dictionary is a key-value dictionary where the Keys correspond to the keys you can use in your Info.plist, but with a "k" prefix. For example, DTXLogLevel => kDTXLogLevel. The Info.plist keys themselves are not changed. You can check the full list of available keys in the Intrsumentation keys section of the iOS auto instrumentation page.

var startupDictionary = [
            kDTXApplicationID: <your-app-id>,
            kDTXAgentStartupPath: <agent-startup-path>,
            ...
            ] as [String : Any?]
Dynatrace.startup(withConfig: startupDictionary)

startupWithInfoPlistSettings

+ (DTX_StatusCode)startupWithInfoPlistSettings;

You can use this method if you have defined your keys in the Info.plist, but you have chosen to not auto start the agent, but instead prefer a manual startup.

To defer automatic startup and instead use manual startup later, add the following plist key: DTXAutoStart = false

+ (DTX_StatusCode)startupWithInfoPlistSettings;
Dynatrace.startupWithInfoPlistSettings()

AppMon 2018 April and later setBeaconHeaders

+ (BOOL)setBeaconHeaders:(NULLABLE NSDictionary<NSString *, NSString *> *)beaconHeaders
Parameter Description
beaconHeaders A dictionary of custom headers (header name, header value) to be included in all Mobile Agent data transmissions. Pass nil to remove all headers from future requests.

The Mobile Agent sends data via HTTP or HTTPS. If your infrastructure requires custom headers to be added to HTTP requests to pass a firewall, you can use this method to provide those headers. The method needs to be called as early as possible to ensure that the headers are available for the first communication with the server. It can be called again later to change or to remove the headers. Returns true if the headers where successfully set, false otherwise.

The headers are inspected for invalid or not allowed values.

Limitations:

  • max number of headers: 10
  • max header length: 4096 bytes
  • max total size: 8192 bytes

You can e.g. set Cookies, any custom Header or authorization information:

NSMutableDictionary* headers = [NSMutableDictionary dictionaryWithCapacity:3];
[headers setObject:@"Cookie" forKey:@"n1=v1; n2=v2"];
[headers setObject:@"MyHeader" forKey:@"MyHeader"];
[headers setObject:@"Authorization" forKey:@"API-Token aa11bb22cc33dd44ee55"];
[Dynatrace setBeaconHeaders:headers];

If setBeaconHeaders sets, for example, an Authorization header containing a token, you need to update the header when the token expires. You can receive notifications about network errors occurring for Mobile Agent communication, which also happens on failed authorization.

Use this example to receive notifications about Mobile Agent communication problems. If communication fails you can update the token using setBeaconHeaders:, which restarts the Agent communication with the new headers.

[[NSNotificationCenter defaultCenter] addObserverForName:[Dynatrace getCommunicationProblemNotificationName] ... ];

beaconHeaders AppMon 2018 April and later

+ (NULLABLE NSDictionary<NSString *,NSString *> *)beaconHeaders

Returns the dictionary of custom headers currently set for the Mobile Agent data requests.

+ (NULLABLE NSDictionary<NSString *,NSString *> *)beaconHeaders

Returns the dictionary of custom headers currently set for the Mobile Agent data requests.


getCommunicationProblemNotificationName AppMon 2018 April and later

+ (NONNULL NSString *)getCommunicationProblemNotificationName

Returns the name of the Mobile Agent communication problem notification.


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, 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.


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 Mobile Agent 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 Mobile Agent automatically times web requests made using NSURLRequest, NSURLConnection, NSURLProtocol, NSString, UIWebView. 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. The errorCode parameter can also be used to pass in the HTTP status code received for the request.


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
 */
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((response as! HTTPURLResponse).statusCode.description) //stop webrequest when request finished

            DispatchQueue.main.async {
                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.


Data privacy (user opt-in mode) AppMon 2018 April and later

As of version 7.1.4, the Mobile Agent enables you to dynamically adjust data-privacy settings so that you can build your apps in compliance with GDPR data-privacy regulations. To activate this feature, enable the user opt-in mode by adding the keys below to the Info.plist or Dynatrace.plist file:

	<key>DTXUserOptIn</key>
	<true/>

The privacy API methods allow you to dynamically activate/deactivate crash reporting and to change the data-collection level based on the individual preferences of your end users. The available data-privacy levels are:

  • DTX_DataCollectionOff: AppMon doesn't capture any monitoring data.
  • DTX_DataCollectionPerformance: AppMon only captures anonymous performance data. Monitoring data that can be used to identify individual users (for example, visit tags or custom values), is not captured.
  • DTX_DataCollectionUserBehavior: AppMon captured both performance and user data. In this mode AppMon recognizes and reports on users who re-visit in future sessions.

When the Mobile Agent starts for the first time, it deactivates crash reporting and sets the data-collection level to DTX_DataCollectionOff. You should change the privacy settings based on each user's individual preference, when your app starts the first time. The Mobile Agent does not provide a privacy settings dialog or similar UI component. You have to integrate a privacy dialog into your app. We recommend that you display the privacy dialog before your app is displayed and then apply the user's privacy preference. You should also enable your users to change their privacy settings in the future.

The Mobile Agent persists the privacy setting and automatically applies it when the app is restarted. Each time the user changes the data-collection level, the Mobile Agent generates a new session with the new privacy settings. Do not wrap this API method with a manual action. Otherwise, the Mobile Agent can't attach the user action to the correct session.

With the + (void)setCrashReportingOptedIn:(BOOL)optedIn; method you can activate or deactivate crash reporting. The data collection level can be changed with the + (void)setDataCollectionLevel:(DTX_DataCollectionLevel)dataCollectionLevel completion:(void(^ _Nullable)(BOOL successful))completion; method with the completion handler being called when switching to the new data-collection level is finished.

You can retrieve the privacy settings with + (DTX_DataCollectionLevel)dataCollectionLevel; and + (BOOL)crashReportingOptedIn; methods.


Security and Authentication AppMon 2018 October and later

In AppMon 2018 October and later , we have introduced the new public key pinning functionality. This prevents