The Atatus iOS SDK reports network requests as RUM resources, attributed to the view that issued them. Because it instruments URLSession itself, any client built on URLSession is covered, including Alamofire, Apollo, SDWebImage, and generated OpenAPI clients.
Prerequisites
- RUM is set up in your application. See iOS Monitoring Setup.
Report requests as resources
Resource tracking is off until you set urlSessionTracking on the RUM configuration. Setting it to an empty configuration is enough:
import AtatusRUM
RUM.enable(
with: RUM.Configuration(
uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
urlSessionTracking: .init()
)
)
Every request then reports its URL, method, status code, duration, and response size, correlated with the active view. This covers URLSession.shared, sessions you create yourself, completion handler APIs, and async/await calls, with no change to your request code.
Unlike other SDKs you may have used, you do not have to supply a URLSessionDelegate to get resources. A delegate is only needed for the timing breakdown described below.
Add the duration breakdown
To see where a request spent its time, which means DNS, connect, TLS, time to first byte, and download, register a delegate class of your own. It can be empty, because the SDK adds the delegate methods it needs:
import AtatusCore
import AtatusRUM
final class AppSessionDelegate: NSObject, URLSessionDataDelegate {}
// Call this after RUM.enable()
URLSessionInstrumentation.enableDurationBreakdown(
with: .init(delegateClass: AppSessionDelegate.self)
)
let session = URLSession(
configuration: .default,
delegate: AppSessionDelegate(),
delegateQueue: nil
)
Requests made through that session carry the full breakdown. Requests through other sessions still report as resources, just without it.
Register the class with enableDurationBreakdown, then hand URLSession an instance of that same class. A session built with a different delegate type gets no breakdown. On iOS 15 and later, the per-task delegate: parameter works too.
To stop instrumenting a delegate class, for example in a test:
URLSessionInstrumentation.disable(delegateClass: AppSessionDelegate.self)
Connect resources to backend traces
List the hosts you own in firstPartyHostsTracing so the SDK injects trace headers and records the trace and span IDs on the resource:
RUM.enable(
with: RUM.Configuration(
urlSessionTracking: .init(
firstPartyHostsTracing: .trace(
hosts: ["api.example.com"],
sampleRate: 20
)
)
)
)
A host matches its subdomains, so example.com also covers api.example.com. See Trace Collection for the headers that are injected, the supported formats, and what the backend needs.
firstPartyHostsTracing controls trace propagation only. Resources are reported for every host either way, so there is no need to list a third-party API you do not instrument.
Capture request and response headers
Header capture is off by default. Turning on the defaults records the headers that explain caching and content negotiation:
RUM.enable(
with: RUM.Configuration(
urlSessionTracking: .init(trackResourceHeaders: .defaults)
)
)
The default set is:
| Direction | Headers |
|---|---|
| Request | cache-control, content-type |
| Response | cache-control, content-encoding, content-length, content-type, etag, age, expires, vary, server-timing, x-cache, cf-cache-status, x-vercel-cache |
To add headers of your own, list them by name. Matching ignores case, and the names apply to both the request and the response:
RUM.enable(
with: RUM.Configuration(
urlSessionTracking: .init(
trackResourceHeaders: .custom([
.defaults,
.matchHeaders(["x-request-id", "x-tenant"])
])
)
)
)
Credentials are never captured, whatever you ask for. The SDK always drops authorization, www-authenticate, proxy-authorization, proxy-authenticate, connection, host, and content-length from requests, and drops any header whose name contains token, cookie, secret, password, credential, bearer, an API or access key, or a forwarding or client IP header. Do not rely on that as your only safeguard: review what you add.
Captured headers are truncated to 128 bytes per value, capped at 100 headers and 2 KB per direction, and lowercased. The default headers are kept first, so they survive the budget before your custom ones.
Attach your own attributes to a resource
A resource attributes provider runs once per request and returns the attributes to attach:
RUM.enable(
with: RUM.Configuration(
urlSessionTracking: .init(
resourceAttributesProvider: { request, response, data, error in
var attributes: [String: Encodable] = [:]
if let endpoint = request.value(forHTTPHeaderField: "x-endpoint-name") {
attributes["endpoint"] = endpoint
}
if let error = error {
attributes["error.description"] = String(describing: error)
}
return attributes
}
)
)
)
The closure runs on a background thread, so keep it quick and free of side effects. Be aware of what data contains:
- It is
nilforasync/awaitrequests. - It is
nilfor images, video, audio, and binary downloads. - It is
nilwhen the body is larger than 512 KB, so the SDK does not hold a large response in memory.
Drop resources you do not want
Health checks and polling endpoints add volume without adding insight. Drop them with a resource event mapper:
RUM.enable(
with: RUM.Configuration(
urlSessionTracking: .init(),
resourceEventMapper: { event in
if event.resource.url.contains("/health") {
return nil
}
return event
}
)
)
Returning nil drops the resource. Returning a modified event is how you redact a URL that embeds an identifier or a token.
Track a request manually
For a client that does not use URLSession, report the request yourself. Start it with a key that is unique among the requests in flight, then stop it with the same key:
import AtatusRUM
let monitor = RUMMonitor.shared()
let key = UUID().uuidString
monitor.startResource(
resourceKey: key,
httpMethod: .get,
urlString: "https://api.example.com/v2/products"
)
// On success
monitor.stopResource(resourceKey: key, statusCode: 200, kind: .fetch, size: 1_024)
// On failure
monitor.stopResourceWithError(resourceKey: key, error: error)
The resource kinds are document, xhr, beacon, fetch, css, js, image, font, media, other, and native. If you have a URLResponse, pass it instead of the status code and kind, and if you have URLSessionTaskMetrics, add them with addResourceMetrics(resourceKey:metrics:) before stopping the resource.
A manually tracked resource carries no trace context, so it cannot be linked to a backend trace. To correlate a non-URLSession client with your backend, create a span and inject its headers yourself. See Trace Collection.
Report GraphQL operations
GraphQL requests all share one URL, which makes them indistinguishable in a dashboard. Pass the operation details as request headers and the SDK turns them into resource attributes, removing the headers before the request leaves the device:
| Header | Value |
|---|---|
_atatus-custom-header-graph-ql-operation-name |
Operation name, such as GetCart |
_atatus-custom-header-graph-ql-operation-type |
query, mutation, or subscription |
_atatus-custom-header-graph-ql-variables |
Variables, as a JSON string |
_atatus-custom-header-graph-ql-payload |
The operation payload |
With an Apollo client, set them in an interceptor. The SDK also extracts the errors array from a GraphQL response, which is how a request that returns HTTP 200 with errors still shows up as a failure. That extraction needs the response body, so it works for completion handler requests and for instrumented delegates under the 512 KB limit.
Other clients
| Client | What you do |
|---|---|
| Alamofire | Nothing. It runs on URLSession. For the duration breakdown, register Alamofire.SessionDelegate.self as the delegate class |
| SDWebImage | Nothing. Image requests are reported as resources |
| Generated OpenAPI clients | Nothing, as long as they use URLSession |
| Apollo GraphQL | Set the GraphQL headers above to label the operation |
Objective-C
Resource tracking is configured on ATRUMConfiguration, and the duration breakdown through ATURLSessionInstrumentation:
@import AtatusRUM;
[ATURLSessionInstrumentation enableDurationBreakdownWith:
[[ATURLSessionInstrumentationConfiguration alloc] initWithDelegateClass:[AppSessionDelegate class]]];
Next steps
- Propagate trace context and emit spans with Trace Collection.
- Report failed requests as errors with Error Tracking.
- Review the rest of the RUM options in Configuration Options.
+1-415-800-4104