Manually instrument iOS applications

Manually instrument iOS applications to collect additional telemetry, sanitize Personal Identifiable Information (PII), add global attributes, and more.

You can manually instrument iOS applications for Splunk RUM using the iOS RUM library to collect additional telemetry, sanitize Personal Identifiable Information (PII), add global attributes, and more.

Filter spans

You can modify or drop spans using the spanInterceptor function. For example, you can drop or redact spans that contain personally identifiable information (PII).

The following example shows how to remove a span:

let interceptor: (SpanData) -> SpanData? = { spanData in
    // Discard spans
    if spanData.name == "Drop this" {
        return nil
    }
    // Redact attribute values for all spans
    var atts = spanData.attributes
    atts["http.url"] = .string("redacted")
    return spanData.settingAttributes(atts)
}

// Usage in AgentConfiguration:
let config = AgentConfiguration(
    endpoint: myEndpoint,
    appName: "MyApp",
    deploymentEnvironment: "dev"
).spanInterceptor(interceptor)

Manage global attributes

Global attributes are key-value pairs added to all reported data. Global attributes are useful for reporting app or user-specific values as tags.

Add user metadata using global attributes

By default, the iOS RUM library doesn’t automatically link traces to users of your application. However, you might need to collect user metadata to filter or debug traces. It is the application's responsibility to provide the proper privacy manifest and obtain the user consent according the respective laws in the case the user is tracked.

You can identify users by adding global attributes from the OpenTelemetry specification, such as enduser.id and enduser.role, to your spans.

The following examples show how to add identification metadata as global attributes when initializing the agent or after you’ve initialized it, depending on whether user data is accessible at initialization:

Add identification metadata during initialization
let agentConfig = AgentConfiguration(
            endpoint: endpointConfig,
            appName: "App Name",
            deploymentEnvironment: "dev"
        )
            .enableDebugLogging(true)
            .globalAttributes(MutableAttributes(dictionary: [
                "enduser.role": .string("premium"),
                "enduser.id": .int(128762)]))

 _ = try SplunkRum.install(with: agentConfig)
Add identification metadata after initialization
// Using Set methods
SplunkRum.shared.globalAttributes.setString("premium", for: "enduser.role")
SplunkRum.shared.globalAttributes.setInt(128762, for: "enduser.id")

// Using dictionary
SplunkRum.shared.globalAttributes[string: "Name"] = "John" 
SplunkRum.shared.globalAttributes[bool: "isContractor"] = true
Manually change screen names

Manual detection will consist of a simple API that lets clients track the currently visible screen.

SplunkRum.shared.navigation.track(screen: String)
Note: Use SplunkRum.shared.navigation.track(screen: String) in all the views of your application to avoid inconsistent names in your data.

Report custom events/workflow

To record custom workflows in your iOS application you can use the trackWorkflow API. Additionally, you have the option to set custom attributes. See example below

Note: A workflow includes a duration, meaning it measures the time taken for a specific action by recording a start and end timestamp.
func calculateTax() {
   let span = SplunkRum.shared.customTracking.trackWorkflow("calculateTax")
   span.setAttribute(key: "numClaims", value: claims.count) 
   //...
   //...
   span.end() // You can also use defer for this
}

To record custom events in your mobile application, you can use the trackCustomEvent API. Additionally, you have the option to set custom attributes for this event. See example below

Note: An event has no duration, that is, which happens in an instant.
SplunkRum.shared.customTracking.trackCustomEvent("Completed Tax Filing")
Alternatively, you can also add additional attributes to your custom event
let dictionary: NSDictionary = [
                  "Account Type": "Premium",
                  "Referral Source": "Google Ads",
                  "Filing year": 2025
]
SplunkRum.shared.customTracking.trackCustomEvent(name: "Completed Tax filing", attributes: dictionary)

Report errors and exceptions

You can report handled errors, exceptions, and messages using the trackError API. Additionally, you have the option to set custom attributes for each error/exception.

trackError overloads are available for String, Error, NSError and NSException types in an iOS application with optional attributes (MutableAttributes type).

// String Type
SplunkRum.shared.customTracking.trackError("This is a sample string error")

// Swift Error Type 
let attributes = MutableAttributes()
attributes["ErrorType"] = .string("SwiftError")
attributes["ErrorCode"] = .int(404)
let sampleError: Error = NSError(domain: "com.example.error", code: 100, userInfo: [NSLocalizedDescriptionKey: "Sample Swift error"])
SplunkRum.shared.customTracking.trackError(sampleError, attributes)

// NSError Type
let attrs = MutableAttributes()
attrs["ErrorDomain"] = .string("com.example.nserror")
attrs["ErrorCode"] = .int(200)
attrs["Description"] = .string("Sample NSError description")
let nsError = NSError(domain: "com.example.nserror", code: 200, userInfo: [NSLocalizedDescriptionKey: "Sample NSError"])
SplunkRum.shared.customTracking.trackError(nsError, attrs)

// NSException Type
let exceptionDict = MutableAttributes()
exceptionDict["ExceptionName"] = .string("GenericException")
exceptionDict["Reason"] = .string("Sample NSException reason")
exceptionDict["Handled"] = .bool(true)
let exception = NSException(name: .genericException, reason: "Sample NSException reason", userInfo: ["Key": "Value"])
SplunkRum.shared.customTracking.trackException(exception, exceptionDict)

Add server trace context from Splunk APM

The iOS RUM library collects server trace context using back-end data provided by APM instrumentation through the Server-Timing header. In some cases, you might want to generate the header manually.

To create the Server-Timing header manually, provide a Server-Timing header with the name traceparent, where the desc field holds the version, the trace ID, the parent ID, and the trace flag.

Consider the following HTTP header:

Server-Timing: traceparent;desc="00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"

The example resolves to a context containing the following data:

version=00 trace-id=4bf92f3577b34da6a3ce929d0e0e4736
parent-id=00f067aa0ba902b7 trace-flags=01

When generating a value for the traceparent header, make sure that it matches the following regular expression:

00-([0-9a-f]{32})-([0-9a-f]{16})-01

Server timing headers with values that don’t match the pattern are automatically discarded. For more information, see the Server-Timing and traceparent documentation on the W3C website.

Next steps