Usage

public class Usage

Client Usage in Common


Flexibly collect, store, and upload client usage events. ClientUsage allows you to collect data for different targets. A target is logical group of collected events and information, collected for a specific purpose, and sent to a given analytics server. Different servers can be used for different targets using different communication protocols and data structures.

Elements:

  • Reporters: All events reported using a reporter assigned to a target. The reporter’s only task is to generate UsageRecord which is stored in an instance of UsageStoring.

  • Uploaders: Transforms the collected data of a target to a required format, uploads to a given server, and removes the successfully uploaded data. You can write a custom ‘Uploader’ which uses a custom server and custom data format. Use UsageStoring and UsageSnapsotting to help with this task. By default the SAPcpmsUsageUploader is implemented in SAPFoundation to upload logs to SAP Mobile Services.

  • UsageStoring: A protocol that manages record storing, retrieval, and deletion, and can be used by Uploaders to retrieve records and clear them after a successful upload. It can also be used to obtain an instance of UsageSnapshotting for a given target. By default the UsageStore is implemented in SAPFoundation.

See Usage Reporting for additional information.

SAP Mobile Services configuration

To use this component you must enable the server to accept records for an application. See Defining Usage Report Policy

After successful client data upload, you can find your reports on the server. See Viewing Client Data Report

Configuration

The main component of Usage is separated from the default store and uploader implementation. You must configure the component before you can use it. For example, by instantiating a default store object and passing it to the Usage’s configure method as follows:

do {
    let store = try UsageStore()
    Usage.shared.configure(with: store)
} catch {
    // handle error
}

The UsageStore class is located in SAPFoundation.

*Notes: *

  • This is not an optional step – In order to generate usage records via reporters, the usage instance must be configured. Reporters created (by subclassing and registering, or by obtaining via reporter(for:) method) prior configuration are invalid instances which can not be used for reporting.
  • Configure the instance once only – Multiple calls to the configure method have no effect and the first configured store remains in use.
  • A custom store can be created by implementing the UsageStoring protocol – Custom classes implementing the UsageStoring protocol can be passed to the configure method.

Usage

  1. Event reporting
  2. Reporting App Usage
  3. Report uploading

Event reporting

You can report events using either:

Event reporting using SAPcpms compatible reporter

The SAP Mobile Services server supports a specific format of Usage data. Since the schema of the data is not flexible the fields are mandatory.

Reporting using SAPcpmsUsage to the default target:

// Only the applicationID and applicationVersion fields are used from the settingsParameter.
SAPcpmsUsage.sessionStart(applicationIdentifier: <#application identifier#>, applicationVersion: <#application version#>)
// ...
SAPcpmsUsage.event(type: "testType", key: "testKey")
SAPcpmsUsage.sessionEnd()

The SAPcpmsRecord class provides convenience methods to create a record using the proper fields. Reporting using SAPcpmsRecord to any target:

reporter = Usage.shared.reporter(for: "myTarget")
reporter.report(SAPcpmsRecord.sessionStart(applicationIdentifier: <#application identifier#>, applicationVersion: <#application version#>))
// ...
reporter.report(SAPcpmsRecord.event(type: "testType", key: "testKey"))

Note: Additional convenience method is available by importing the SAPFoundation. See Additional convenience method on SAPcpmsUsage for more information.

Event reporting using flexible reporter

Create a report using custom events and include an info dictionary as a parameter:

// get the default reporter
let reporter = Usage.shared.reporter()

// report session start event
reporter.report(.sessionStart)

// ...

// report custom event
reporter.report(.event("SomeCustomEvent"))

// ...

// report custom event with additional info
reporter.report(.event("SomeCustomEvent"), info: ["MyCustomKey": "MyCustomData"])

In this case you must upload reports using a custom uploader.

Reporting app usage

App usage events may be logged using the SAPcpmsUsage.logBehaviorEvent(behaviorEvent:viewIdentifier:) method in SAPCommon. This method is a wrapper around the SAPcpmsUsage.event() API and provides a structured schema for server-side generated analytics reports about usage behaviors and application analytics.

Logging when views display

Utilize a view controller’s viewDidAppear() method to log when a view is presented to the user. The value for behaviorEvent should be viewDisplayed from the SAPcpmsUsage.BehaviorEvent enumeration to identify this event on the server-side as a screen being displayed in your app. The viewIdentifier should always be the name of the screen; this could either be a string representation of the class name or a “business-friendly” name that may be more readable to an analyst reviewing the reports generated from this data. For example:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.makeSelection()
    SAPcpmsUsage.logBehaviorEvent(behaviorEvent: .viewDisplayed, viewIdentifier: "Products Listing Screen")
}

Logging user interactions within your app

Use SAPcpmsUsage.logBehaviorEvent(behaviorEvent:viewIdentifier:) with its optional parameters to log when users interact with controls and other on-screen elements within your app:

  • The value for behaviorEvent is userInteraction from the SAPcpmsUsage.BehaviorEvent enumeration and identifies this event on the server-side as an action performed by the user.
  • viewIdentifier is always the name of the screen (as a best practice, follow the same convention you use when logging viewDisplayed events).

Use the optional parameters to add details about the nature of the user’s interaction which are utilized when generating analytics reports, for example:

  • elementIdentifier is the name of the control that interacts with and follows a similar naming convention to viewIdentifier
  • action is a description of the action the user performs on the control (if applicable)
  • value contains any value deemed relevant to the interaction (if applicable).

Here are some examples:

Logging when the user presses your app’s refresh button:

private func refresh() {
    SAPcpmsUsage.logBehaviorEvent(behaviorEvent: .userInteraction, viewIdentifier: "Products Listing Screen", elementIdentifier: "Refresh Button")
    // Note: The only action that can be performed in this example is the button being pressed, so the action parameter is not used.
    // ...
}

Logging when the user selects a row in a table of products:

override func prepare(for segue: UIStoryboardSegue, sender _: Any?) {
    SAPcpmsUsage.logBehaviorEvent(behaviorEvent: .userInteraction, viewIdentifier: "Products Listing Screen", elementIdentifier: "Products Table", action:"Row Selected", value: selectedEntity.productName)
    // Note: In this example, selectedEntity.productName is a variable with the product name of the item selected, e.g., "iPad Pro".
    // ...
}

The parameters for logBehaviorEvent() can be flexibly applied to a variety of controls and possible interactions, but you should decide on an appropriate convention to follow for logging interactions within your app.

Report uploading

You can upload reports using either:

Uploading reports using a custom uploader

Uploads a snapshot of the data to an endpoint using data format and protocol for communication. The steps to upload the data are:

  1. Retrieve the snapshot for a given target using: Usage.store.snapshot(for:).
  2. Read all records from the snapshot and create the proper data format. Use UsageSnapshot.records() to enumerate the records.
  3. Upload the generated data to the server.
  4. If the upload is successful, delete the original records calling UsageSnapshot.removeRecords().

You can use Streaming to merge the second and third steps.

Usage component Logger ID

This component uses the following name prefix for logging: ‘SAP.Common.Usage’

Usage

  • The default target identifier

    Declaration

    Swift

    public static let defaultTargetID: String
  • StorageID name used for shared data.

    Declaration

    Swift

    public static let unattributedStorageID: String
  • UserID used for shared/anonymous data.

    Declaration

    Swift

    public static let unattributedUserID: String
  • Returns a shared singleton usage object.

    Declaration

    Swift

    public class var shared: Usage { get }
  • Returns the store object associated with the usage object. The UsageStore used by the Usage component to save the data until it is uploaded to a server. It is used also by uploader components to access the data. If the store is not initialized, an attempt is made to initialize it with default parameters.

    Declaration

    Swift

    public var store: UsageStoring? { get }
  • Read-only access to representation of SAPcpmsUsagePolicy server-side setting.

    Declaration

    Swift

    private(set) public var dataCollectionEnabled: Bool { get }
  • Read-only access to current consent value.

    Declaration

    Swift

    private(set) public var userConsent: Bool { get }
  • Unique identifier created during onboarding process.

    Declaration

    Swift

    private(set) public var userID: String? { get }
  • Configures the usage instance with the given UsageStoring. Sets userID and userConsent values used to control Usage message output on a per-user basis.

    Declaration

    Swift

    public func configure(with store: UsageStoring, retainLastUnattributedSession: Bool = false, addSessionData applicationIdentifier: String? = nil)

    Parameters

    store

    the UsageStoring used to store the UsageRecords

    retainLastUnattributedSession

    if true, temporarily save last unattributed session in memory to inject into attributed store, else leave unattributed data in place.

    applicationIdentifier

    optionally add session data when outputEnabled state transition is detected.

  • Returns a UsageReporter instance for the given target identifier. If the reporter doesn’t exist a new one will be created automatically. All other calls will return the same reporter instance. Uses Usage.defaultTargetID by default.

    Declaration

    Swift

    public func reporter(for targetID: String = defaultTargetID, injectUnattributed: Bool = true) -> UsageReporter

    Parameters

    targetID

    the target identifier where to reporter will report to

    injectUnattributed

    controls if saved unattributed data is injected

    Return Value

    UsageReporter associated with the given target identifier

  • Registers a custom created UsageReporter instance. Custom UsageReporters can be created by subclassing the UsageReporter class. UsageReporters created this way must be registered to Usage through this register method, otherwise they can not report to the usage store. If there was a reporter with this ID that will be replaced with the new instance.

    Declaration

    Swift

    public func register(reporter: UsageReporter, for targetID: String = defaultTargetID, injectUnattributed: Bool = true)

    Parameters

    reporter

    custom created UsageReporter

    targetID

    the target identifier which will be associated with the reporter

    injectUnattributed

    controls if saved unattributed data is injected

  • Unregisters and invalidates a previously registered or created reporter and removes it from Usage. After unregistering a reporter it can no longer report to the usage store and must be re-registered to use again.

    Declaration

    Swift

    public func unregisterReporter(from targetID: String)

    Parameters

    targetID

    the target identifier which identifies the reporter

  • Retrieves the used target identifiers. This method retrieves all the distinct target identifiers used by the reporter both in the usage store and in-memory.

    Declaration

    Swift

    public func targetIdentifiers() -> [String]

    Return Value

    array of used target identifiers

  • Set dataCollectionEnabled true and optionally emit session data.

    Declaration

    Swift

    public func enableDataCollection(addSessionData applicationIdentifier: String? = nil)

    Parameters

    applicationIdentifier

    optionally add session data when outputEnabled state transition is detected.

  • Set dataCollectionEnabled false and optionally emit session data.

    Declaration

    Swift

    public func disableDataCollection(addSessionData emitData: Bool = false)

    Parameters

    emitData

    optionally add session data when outputEnabled state transition is detected.

  • Record consent value for specfied user.

    Declaration

    Swift

    public func consentForUser(_ userID: UUID, given consent: Bool = false, addSessionData applicationIdentifier: String? = nil)

    Parameters

    userID

    specified user.

    consent

    value to record.

    applicationIdentifier

    optionally add session data when outputEnabled state transition is detected.

  • Obtain consent value for specified user.

    Declaration

    Swift

    public func hasConsentForUser(_ userID: UUID) -> Bool

    Parameters

    userID

    whom to query consent for.

    Return Value

    associated consent value.

  • Determine if Usage data may be output.

    Declaration

    Swift

    public func outputEnabled() -> Bool

    Return Value

    boolean indicating state.