Custom instrumentation of the Tealeaf iOS SDK

Customer instrumentation involves many events, including error, exception, location and custom events. Each event type has a method for instrumenting the SDK.

sharedInstance

Returns the shared instance of TLFCustomEvent. You should use this instance when logging custom events.
+ (TLFCustomEvent *)sharedInstance
Returns a shared instance of TLFCustomEvent
Declared in TLFCustomEvent.h

Error events

Logs an error as described in an NSError instance.

logNSErrorEvent:message:[file:line:level:]

Logs an error as described in an NSError instance.

- (BOOL)logNSErrorEvent:(NSError _)error message:(NSString _)message [file:(const] char *)file line:(unsigned int)line level:(kTLFMonitoringLevelType)level;
- (BOOL)logNSErrorEvent:(NSError _)error message:(NSString _)message level:(kTLFMonitoringLevelType)level;
ParameterDescription
errorThe NSError returned by the SDK or your own method.
messageAn associated message for your own.
filenameThe original file where the error occurred. The source code file name, usually from the FILE preprocessor macro (optional).
lineThe source code line number, usually from the LINE preprocessor macro (optional).
levelThe monitoring level of the event. The minimum logging level at which this error is logged.
returnWhether the event was successfully logged or not.

Declared in TLFCustomEvent.h. kTLFMonitoringLevelType is declared in TLFPublicDefinitions.h

This example shows the expected JSON:

{
  "exception": {
    "unhandled": false,
    "data": {
      "message": "Custom Message"
    },
    "name": "(null)",
    "stackTrace": "",
    "description": "An Error Occured,"
  },
  "fromWeb": false,
  "count": 4,
  "screenviewOffset": 23,
  "offset": 39,
  "type": 6,
  "line": 1,
  "fileName": "/path/to/file/AppDelegate.m"
}

Exception events

Use this method to log exceptions.

logNSExceptionEvent

Requests that the framework logs an exception trapped by your own exception handler. These methods do not use the Cocoa SDK, which is not exception-safe. Sets the Unhandled flag to false.

This example shows how to call the method:

- (BOOL)logNSExceptionEvent:(NSException *)exception;
- (BOOL)logNSExceptionEvent:(NSException *)exception
             dataDictionary:(NSDictionary *)dataDictionary;
- (BOOL)logNSExceptionEvent:(NSException *)exception
             dataDictionary:(NSDictionary *)dataDictionary
                isUnhandled:(BOOL)unhandled;
ParameterDescription
exceptionThe caught NSexception instance. This value is whether the event was successfully logged. Values are true or false.
dataDictionaryThis value is additional data about the exception.
unhandledIndicates whether the exception was caught by an exception handler.

Logging exceptions in Swift

NSException codes are not supported for logging exceptions in Swift. Use the following code snippet to log exceptions in Swift.

enum MyError: ErrorType {
  case RuntimeError(String)
  case OutofIndex(String)
}

func throwError(message: String) throws {
  throw MyError.RuntimeError(message)
}

func throwException(message: String) throws {
  let info: [Int: String] = [1: "any"]
  let exceptionInfo: [String: NSException] = [
    "ExceptionObject": NSException(
      name:
        "TheException", reason: "WantToThrowNSException", userInfo: info)
  ]
  throw NSError(domain: "exception", code: 10, userInfo: exceptionInfo)
}

@IBAction func generateUnhandledException(sender: UIButton) {

  /* catching NSError with embedded NSException */
  do {
    try throwException("exceptionexception")
  } catch let err as NSError {
    let ex = err.userInfo["ExceptionObject"] as! NSException
    TLFCustomEvent.sharedInstance().logNSExceptionEvent(
      ex, dataDictionary: info,
      isUnhandled:
        true)
    TLFCustomEvent.sharedInstance().logNSErrorEvent(
      err, message: "error",
      level:
        kTLFMonitoringLevelType.TLFMonitoringLevel1)
  } catch let ex as NSException {
    TLFCustomEvent.sharedInstance().logNSExceptionEvent(
      ex, dataDictionary: info,
      isUnhandled:
        true)
  } catch {
    print("unhandled")
  }
}

Kill Switch events

Set device ID

[[TLFApplicationHelper sharedInstance] setDeviceId:@"CustomID"];
- (BOOL)setDeviceId:(NSString*)value;

This sets the Device ID.

ParameterDescription
@paramThe string that represents the new Device ID.
@returnIndicates whether the Device ID was set.

Get string representation of device ID

[[TLFApplicationHelper sharedInstance] getDeviceId];
- (NSString*)getDeviceId;

This returns a string representation of the Device ID.

ParameterDescripiton
@returnA string representation of the Device ID.

Geolocation

Use this method to have the framework log a geographic location at a specific point in your application.
Requests that the framework logs a geographic location. This is an example of how to use this API:

CLLocationDegrees myLatitude=37.7888024;    
     CLLocationDegrees myLongitude=-122.40031809;    
     CLLocation *myLocation = [[CLLocation alloc] initWithLatitude:myLatitude longitude:myLongitude]; 
     [[TLFCustomEvent sharedInstance] logLocation:myLocation];
ParameterDescription
locationA CLLocation Object containing a location of interest.

GPS location events

To avoid making unnecessary location updates, and to protect the privacy of your application's users by ensuring that location is reported only when the application has some other reason to request it, location events are not logged automatically. To log location updates, you use logLocationUpdateEventWithLatitude.

logLocationUpdateEventWithLatitude:longitude:level

This method is meant to be called inside your handler for locationManager:didUpdateToLocation:fromLocation:. Your application must include the Core Location framework (CoreLocation.framework).

#import "CoreLocation/CoreLocation.h"
#import "TLFCustomEvent.h"

...

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
  CLLocationCoordinate2D c = newLocation.coordinate;
  ... [[TLFCustomEvent sharedInstance]
      logLocationUpdateEventWithLatitude:c.latitude
                               longitude:longitude];
}

- (void)logLocationUpdateEventWithLatitude:(double)latitude
                                 longitude:(double)longitude
                                     level:(kTLFMonitoringLevelType)level
ParameterDescription
latitudeThe latitude to log.
longitudeThe longitude to log.
levelThe minimum logging level for locations.

Declared in TLFCustomEvent.h. kTLFMonitoringLevelType is declared in TLFPublicDefinitions.h.

Custom events

You can log a specified event with or without also logging an associated value or dictionary.

logEvent:values:level

Logs a named event and associated dictionary. The dictionary is converted to its JSON representation.

📘

Note:

To be convertible to a JSON representation, the values of the dictionary must be NSDictionary, NSArray, NSString, NSNumber or SNull objects.

- (void)logEvent:(NSString _)eventName values:(NSDictionary _)values;
- (void)logEvent:(NSString _)eventName
          values:(NSDictionary _)values
           level:(kTLFMonitoringLevelType)level;
ParameterDescription
eventNameThe name of the event. Must not contain equal signs or square brackets.
valuesMore data items that are associated with the event.
levelThe minimum logging level for this event logged (optional, default is 1).

Declared in TLFCustomEvent.h. kTLFMonitoringLevelType is declared in TLFPublicDefinitions.h.

Log screen layout for iOS mobile app session replay

You can replay a mobile app session in cxImpact Browser Based Replay as you would an HTML web session instead of viewing the mobile app session as a series of screen captures.

The screen layouts of the native mobile app sessions are captured in Tealeaf JSON format. The screen layouts are then sent back to replay server. The replay server uses a template engine, which interprets the JSON into HTML format. You can then replay the screen lay out from the native mobile app session as HTML pages in cxImpact Browser Based Replay.

There are several advantages to using JSON data to replay mobile app session over screen captures.

  • Reduce bandwidth. Screen captures for each screenview generate relatively large image data. It not only consumes large amounts of wireless and cellular bandwidth, but it also consumes more memory inside the device. It also impacts the app performance.
  • Mask sensitive information. You cannot mask sensitive information in a screen capture. When using JSON data to replay mobile app sessions, you can mask EditTexts by adding View IDs to the MaskIdListattribute in TealeafBasicConfig.properties.
  • Draw user interactions (UI events) onto the HTML pages that are created from the JSON data.

Replay logging can be automatic, manual, or a combination of the two. To enable automatic layout logging find LogViewLayoutOnScreenTransition in TealeafBasicConfig and set it to YES. This will automatically log a view controller when the view controller's viewDidAppear:(BOOL)animated method is called.

📘

Note:

If the viewController overrode the viewDidAppear, method[super viewDidAppear:animated] must be called.

Correct

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  // Custom code
}

Incorrect

- (void)viewDidAppear:(BOOL)animated {
  // Custom code
}

Several methods are included for manual logging of screen layout.

The following is the most basic manual logging method. The following method logs the layout of the viewController passed into it.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController

The following method performs the same action as the first method, but you can pass in a specific name for the screen layout that is logged. This is helpful when you log a view controller that can perform several different functions.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController
                                  andName:(NSString *)name

The following method performs the same action as the first method, but after the specified delay. This is helpful for logging after certain events, such as reloading the data in a table. The delay is measured in seconds.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController
                                 andDelay:(CGFloat)delay;

The following method performs the same function as the previous method, but it allows you to pass in a name for the layout.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController
                                 andDelay:(CGFloat)delay
                                  andName:(NSString *)name

In addition to logging the main view controller passed in, this method allows you to pass in an array of other views to be logged at the same time. This is useful in instances where there are elements on screen that are not part of the same view hierarchy, such as an overlay attached directly to the application's window or an alert view.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController
                          andRelatedViews:(NSArray *)views

The following method performs the same action as the previous method, but it allows you to pass in a name for the layout.

- (BOOL)logScreenLayoutWithViewController:(UIViewController *)viewController
                          andRelatedViews:(NSArray *)views
                                  andName:(NSString *)name

Where and when to call manual logging

With automatic logging enabled, view controllers are logged during the viewDidAppear stage of the view lifecycle. If the view that is logged is loading remote data, this is not adequate. In this case, the ideal time to call the logging method is when the remote data is done loading and displaying.

- (void)RESTRequestCompleted:(RESTRequest *)request
                responseData:(NSDictionary *)responseData
                    response:(NSHTTPURLResponse *)response {
  [self updateUI:[responseData objectForKey:[self productKeyKey]]];
  [self hideActivityIndicator];
  [[TLFCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
}

In some cases, you need to delay triggering logging to give time for UI animations to complete or a UITableView reloadData call to complete. The Custom Event provides a method to accomplish this.

- (void)RESTRequestCompleted:(RESTRequest *)request
                responseData:(NSDictionary *)responseData
                    response:(NSHTTPURLResponse *)response {
  items = [responseData objectForKey:[self itemsKey]];
  [self.itemsTable reloadData];
  [self hideActivityIndicator];
  [[TLFCustomEvent sharedInstance] logScreenLayoutWithViewController:self
                                                            andDelay:0.1];
}

After certain UIEvents, it may be beneficial to trigger logging, such as upon selection of an item on table view that stretches beyond one screen.

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  [[TLFCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
  return indexPath;
}

A manual logging call is required to capture an alert view.

- (IBAction)btnSubmitFormClick:(id)sender {
  UIAlertView *alert =
      [[UIAlertView alloc] initWithTitle:@"Thank You!"
                                 message:@"We will be in touch with you soon."
                                delegate:self
                       cancelButtonTitle:@"Ok"
                       otherButtonTitles:nil];
  [alert show];
  [[TLFCustomEvent sharedInstance]
      logScreenLayoutWithViewController:self
                        andRelatedViews:@[ alert ]];
}

You should also log the screen layout after the alert dialog is dismissed.

- (void)alertView:(UIAlertView *)alertView
    clickedButtonAtIndex:(NSInteger)buttonIndex {
  [[TLFCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
}

Tealeaf screen layout logging only logs the views and controls that are on screen when the logging call is made. When UITableViewcontains more rows than can be view on a screen at once, call the screen layout logging when an item is selected. This ensures that the event matches the row selected. Use the following code in your UITableViewDelegate to make this change.

- (NSIndexPath *)tableView:(UITableView *)tableView
    willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  [[TLFCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
  return indexPath;
}

Disabling auto-instrumentation to include advanced custom instrumentation

By default, the iOS SDK automatically instruments your application by using a template of selected items that are based on the configured logging level.

As needed, you can configure the iOS SDK for custom instrumentation. A predefined set of events and objects are instrumented in the application, and the rest can be instrumented through custom methods.

📘

Note:

Before you begin, complete the initial configuration tasks that are associated with instrumentation.

  • Optionally, you can disable auto-instrumentation and use manual instrumentation.
  • In your implementation file, import TLFCustomEvent.h.
  • You can use any of the available custom instrumentation APIs to meet your application requirements.

Manual instrumentation

You turn off the auto-instrumentation feature in the TealeafBasicConfig.plist file. When you do so, no method swizzling occurs, the application state is not monitored, and screen changes or any other events are not automatically tracked.

To disable auto-instrumentation, in the TealeafBasicConfig.plist file, which is in the TLFResources.bundle, set DisableAutoInstrumentation flag to YES.

📘

Note:

Disabling auto-instrumentation is not recommended because of the large configuration effort, high chance of errors, and possibility of incomplete coverage. If you choose to disable auto-instrumentation, you are responsible for implementing theses changes.

Required actions

When you use the iOS SDK with auto-instrumentation turned OFF, you must configure a set of actions to occur that auto-instrumentation would otherwise do. The list of required actions follows.

  • View Controller changes must be logged by using the API logAppContext from the TLFCustomEvent class.
  • HTTP Connection updates must be logged by using the API logConnection from the TLFCustomEvent class. There are three logConnection APIs: one each for initialization, successful response, and failure.
  • Button click events must be logged by using API logClickEvent from the TLFCustomEvent.
  • UITableViewCell tap events must be logged by using the API logValueChangeEvent from the TLFCustomEvent class.
  • Text change events for UITextField, UITextView, and UILabel must be logged by using the API logTextChangeEvent from the TLFCustomEvent class.
  • To sessionize all NSURLMutableRequest objects, you use the API sessionizeRequest from the TLFApplicationHelper class.
  • To track all requests that are made by UIWebView from the UIWebViewDelegate shouldStartLoadWithRequest, you use the API isTealeafHybridBridgeRequest from the TLFApplicationHelper class.
  • To inject the Acoustic Tealeaf hybrid bridge into the JavaScript for all web page loads from UIWebViewDelegate webViewDidFinishLoad, you use the API InjectTealeafHybridBridgeOnWebViewDidFinishLoadfrom the TLFApplicationHelper class.

TLFCustomEvent class

Use the following information to manually track various events with the TLFCustomEvent class.

- (BOOL)logAppContext:(NSString_)logicalPageName
    applicationContext:(NSString_)applicationContext
              referrer:(NSString\*)referrer

A custom event which in conjunction can have a dictionary of keys and values.

- (BOOL)logEvent:(NSString_)eventName values:(NSDictionary_)values;

Use this API to log failures that occur when a connection is attempted; typically from NSURLConnectionDelegate didFailWithError or when sendSynchronousRequest returns an error. The first parameter is the connection object, and the second parameter is the error that you received.

- (BOOL)logConnection:(NSURLConnection_)connection error:(NSError_)error

Use this API to log successful connections; typically from NSURLConnectionDelegate didReceiveResponse or when sendSynchronousRequest returns success. The first parameter is the connection object. The second parameter is the response that you received, and the third is the connection's response type in milliseconds.

- (BOOL)logConnection:(NSURLConnection_)connection
                      response:(NSURLResponse_)response
    responseTimeInMilliseconds:(long long)responseTime;

Use this API to log connection initialization; typically before or after a NSURLConnection initWithRequest call. The first parameter is connection object, and the second parameter is the request object.

- (BOOL)logConnection:(NSURLConnection_)connection
              request:(NSURLRequest_)request;

Use this API to log failures that occur when a connection is attempted; typically from NSURLConnectionDelegate didFailWithError or when sendSynchronousRequest returns an error. The first parameter is the connection object, and the second parameter is the error that you received.

- (BOOL)logNSURLSession:(NSObject_)urlSession error:(NSError_)error;

Use this API to log successful connections; typically from NSURLConnectionDelegate didReceiveResponse or when sendSynchronousRequest returns success. The first parameter is the connection object. The second parameter is the response that you received, and the third is the connection's response type in milliseconds.

- (BOOL)logNSURLSession:(NSObject_)urlSession
                      response:(NSURLResponse_)response
    responseTimeInMilliseconds:(long long)responseTime;

Use this API to log connection initialization; typically before or after a call NSURLConnection initWithRequest. The first parameter is connection object, and the second parameter is the request object.

- (BOOL)logNSURLSession:(NSObject_)urlSession request:(NSURLRequest_)request;

Use this API to log button click events. Call this from your button click event handlers. The first parameter view is the UIButton object on which the click event happened. The second parameter is optional, and is for future use. You can pass Nil for now.

- (BOOL)logClickEvent:(UIView_)view data:(NSDictionary_)data;

Use this API to log UITableViewCell tap events. Call this from your UITableViewDelegate didSelectRowAtIndexPath. The first parameter view is the UITableViewCell object on which the tap event happened The second parameter is optional, and is for future use. You can pass Nil for now.

- (BOOL)logValueChangeEvent:(UIView_)view data:(NSDictionary_)data;

Use this API to log text change events for UITextField, UITextView, and UILabel. Call this from your application wherever contents of these three controls changed. If you add the UITextViewTextDidEndEditingNotification observer, you can call it from there. The first parameter view is the object of any of UITextField, UITextView, and UILabel whose text was edited. The second parameter is optional, and is for future use. You can passNil for now.

- (BOOL)logTextChangeEvent:(UIView_)view data:(NSDictionary_)data;

Use this API to indicate form completion on view.

- (BOOL)logFormCompletion:(BOOL)submitted;

Use this API to log the image as passed in, inside a new type 10 layout message. During replay, the image is displayed under Dynamic Update or as a screenview when the API is called. If the API is called when the screen has already transitioned, the image is shown as Dynamic Update in the replay navlist in Tealeaf SaaS and Tealeaf OnPrem version 10.x. In Tealeaf OnPrem (prior to 10.x) or if the screen has not transitioned, the image displays as a new screenview.

The API returns true if image is successfully logged; else returns false. If image is nil, API returns false.

  • All APIs are blocking calls. They are all optional and can be called based on your application's design and state machine.
  • All the APIs return YES if data is logged, and NO in case of failure. The console debug log shows the reason for failure.
- (BOOL)logScreenLayoutWithImage:(UIImage \*)image;

TLFApplicationHelper class

- (BOOL)sessionizeRequest:(NSMutableURLRequest*)request;

Use this API so that the Acoustic Tealeaf iOS SDK can add various Headers and Cookies that can be used to tie all the application session hits together on the server. Call this API as soon as you create the NSMutableURLRequest object, and before you start the HTTP connection. The first parameter is the object of NSMutableURLRequest that the Acoustic Tealeaf SDK updates.

- (BOOL)isTealeafHybridBridgeRequest:(NSURLRequest_)request
                             webView:(UIWebView_)webView;

Start this API from UIWebViewDelegate shouldStartLoadWithRequest. The first parameter is object of NSURLRequest, and the second is object of the current UIWebView. The API determines whether the request is specific to and meant for the Acoustic Tealeaf iOS SDK from the Acoustic Tealeaf JavaScript SDK. If it is, the API consumes the data that is sent by the Acoustic Tealeaf JavaScript SDK. If not, handle the request inside your shouldStartLoadWithRequest. For example, if this API returns YES, ignore the request and return NO from shouldStartLoadWithRequest. It was not an actual page navigation request from your HTML or JavaScript. If this API returns NO, handle the request as it came from your own HTML page or JavaScript.

- (BOOL)InjectTealeafHybridBridgeOnWebViewDidFinishLoad:(UIWebView *)webView;

Use this API to inject Acoustic Tealeaf specific JavaScript into your web page. The JavaScript injection helps transfer data from the Acoustic Tealeaf JavaScript UI Capture SDK to the Acoustic Tealeaf Native iOS SDK. The first parameter is the object of UIWebView in which the current web page is loaded. Call it every time a new page is loaded into the UIWebView. Place it in UIWebViewDelegate webViewDidFinishLoad.

Base instrumentation

The objects and events to populate the following sections are automatically instrumented, even if you enable custom instrumentation.

Environmental Data: This data set is automatically captured during initialization.

📘

Note:

User Actions and Behaviors are not captured when auto-instrumentation is disabled. These events must be manually instrumented.

Custom instrumentation APIs

The iOS SDK logs many events automatically, but you can also use it to log errors, exceptions, and custom events.

To log custom events, you can use the TLFCustomEvent class. This singleton class offers different methods to log custom events.

For convenience, Acoustic Tealeaf provides standard events for location tracking and wireless carrier recording.

Example

[[TLFCustomEvent sharedInstance] logEvent:@"PurchaseConfirmed"];

API - log event

You use the logEvent API to log a simple custom event quickly.

[[TLFCustomEvent sharedInstance] logEvent:@"EventName1"];
- (void)logEvent:(NSString*)eventName;

API - log event and dictionary of values

You use the logEvent API to log a custom event and a dictionary of values that are related to that event.

[[TLFCustomEvent sharedInstance] logEvent:@"EventName3" values:dictionary];
- (void)logEvent:(NSString_)eventName values:(NSDictionary_)values;

API - log event, dictionary of values, and set monitoring level

You use the logEvent API to log a custom event, a dictionary of values, and set a specific TLFMonitoringLevel.

[[TLFCustomEvent sharedInstance] logEvent:@"EventName6" values:
dictionary level:2];
- (void)logEvent:(NSString_)eventName
          values:(NSDictionary_)values
           level:(kTLFMonitoringLevelType)level;