Atatus Real User Monitoring (RUM) for iOS reports how your application behaves on real devices and networks. The SDK hooks into the UIViewController lifecycle, SwiftUI view appearance, UIApplication touch handling, and URLSession, so views, user actions, network requests, errors, and crashes are collected without you writing tracking code for each screen.

The SDK is modular. You add AtatusCore plus the products you need, and each product stays inert until you enable it.

Prerequisites

Before you start, make sure you have:

  • Xcode 16.0 or later, and Swift 5.9 or later.
  • A deployment target of iOS 15.0 or tvOS 15.0 at minimum.
  • A RUM application in Atatus, which provisions the license key the SDK authenticates with.
Note:

Objective-C applications are supported. Core, RUM, Logs, Trace, Crash Reporting, Session Replay, and WebView Tracking each ship an Objective-C interface whose types carry an AT prefix, such as ATAtatus, ATConfiguration, and ATRUMConfiguration.

Setup

1. Add the SDK dependency

The SDK is distributed through Swift Package Manager, CocoaPods, and prebuilt XCFrameworks. Pick one.

Swift Package Manager

In Xcode, choose File > Add Package Dependencies, enter https://github.com/atatus/atatus-ios-release, and select the AtatusCore and AtatusRUM products. Add AtatusCrashReporting as well if you want crash reports.

For a package manifest, declare the dependency and the products you use:

copy
icon/buttons/copy
dependencies: [
    .package(url: "https://github.com/atatus/atatus-ios-release", from: "1.0.0"),
],
targets: [
    .target(
        name: "MyApp",
        dependencies: [
            .product(name: "AtatusCore", package: "atatus-ios-release"),
            .product(name: "AtatusRUM", package: "atatus-ios-release"),
            .product(name: "AtatusCrashReporting", package: "atatus-ios-release"),
        ]
    ),
]

CocoaPods

Declare the pods in your Podfile:

copy
icon/buttons/copy
pod 'AtatusCore', '~> 1.0'
pod 'AtatusRUM', '~> 1.0'
pod 'AtatusCrashReporting', '~> 1.0'

Then install them:

copy
icon/buttons/copy
$ pod install
Warning:

The pods build from source, and their source repository is not publicly reachable yet, so pod install resolves the version and then fails to fetch the code. Use Swift Package Manager or the XCFrameworks until that repository is published.

XCFramework

Each release attaches one prebuilt archive per module, such as AtatusCore.xcframework.zip and AtatusRUM.xcframework.zip. There is no combined archive, so download the modules you need, unzip them, and drag the bundles into your target's Frameworks, Libraries, and Embedded Content, set to embed and sign. AtatusInternal is a transitive dependency of every module, so always take AtatusInternal.xcframework.zip alongside whatever else you pick. Add OpenTelemetryApi.xcframework.zip as well if you use AtatusTrace, whose OpenTelemetry API depends on it. Each release also publishes checksums.txt, so you can verify what you downloaded.

2. Retrieve your application details

Open your RUM application in the Atatus dashboard and note two values:

  • License key, which authenticates the SDK against your account.
  • Environment, the deployment identifier your events are tagged with, such as production or staging.
Warning:

Authenticate with the license key, never your account API key. Anything shipped inside an application binary is recoverable, and the license key is the only credential scoped for client-side distribution.

3. Initialize the SDK

Initialize the SDK in application(_:didFinishLaunchingWithOptions:), before anything else in your application runs. The SDK measures application start time from the process launch, and anything that happens before initialization cannot be attributed to a view or a session:

copy
icon/buttons/copy
import AtatusCore

Atatus.initialize(
    with: Atatus.Configuration(
        licenseKey: "<LICENSE_KEY>",
        env: "<ENV_NAME>",
        service: "<APP_NAME>",
        version: "1.0.0"
    ),
    trackingConsent: .granted
)

The parameters that identify your application are:

Parameter Default Purpose
licenseKey Required Authenticates the SDK against your Atatus account
env Required Environment tag, such as production or staging
service Bundle identifier Your application's identity in Atatus, shown as the application name
version CFBundleShortVersionString Application version, used to compare releases
site .us Region your data is ingested in: .us, .in, or .eu
Note:

Set service to the name of the application you created in Atatus. When it is omitted the SDK falls back to the bundle identifier, which still works but makes your application harder to recognize in the dashboard.

In Objective-C, build an ATConfiguration and pass it to ATAtatus:

copy
icon/buttons/copy
@import AtatusCore;

ATConfiguration *configuration = [[ATConfiguration alloc] initWithLicenseKey:@"<LICENSE_KEY>"
                                                                         env:@"<ENV_NAME>"];
configuration.service = @"<APP_NAME>";

[ATAtatus initializeWithConfiguration:configuration
                      trackingConsent:[ATTrackingConsent granted]];

A SwiftUI application that has no app delegate can adopt one with @UIApplicationDelegateAdaptor, which keeps initialization at the earliest point in the launch sequence:

copy
icon/buttons/copy
import SwiftUI
import AtatusCore
import AtatusRUM

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {
        Atatus.initialize(
            with: Atatus.Configuration(
                licenseKey: "<LICENSE_KEY>",
                env: "<ENV_NAME>",
                service: "<APP_NAME>"
            ),
            trackingConsent: .granted
        )

        RUM.enable(with: RUM.Configuration())
        return true
    }
}

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

4. Choose your region

Your license key belongs to one Atatus region, so set site to match it. The default is .us:

copy
icon/buttons/copy
Atatus.initialize(
    with: Atatus.Configuration(
        licenseKey: "<LICENSE_KEY>",
        env: "<ENV_NAME>",
        site: .in,
        service: "<APP_NAME>"
    ),
    trackingConsent: .granted
)

Each region has its own intake hosts, and the SDK picks them for you once site is set. There is nothing else to configure.

Note:

Sending to the wrong region means your data is accepted by no intake your account can read, so check site first when nothing appears in the dashboard. To send to a self-hosted intake instead, set serverUrl on the configuration to a base URL with no path, such as https://collector.example.com. It overrides site for every product.

5. Enable RUM

Enabling the RUM product starts the automatic instrumentation that produces views, actions, long tasks, and errors. Pass the default predicates to track UIKit and SwiftUI screens and taps:

copy
icon/buttons/copy
import AtatusRUM

RUM.enable(
    with: RUM.Configuration(
        sessionSampleRate: 100,
        uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
        uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate(),
        swiftUIViewsPredicate: DefaultSwiftUIRUMViewsPredicate(),
        swiftUIActionsPredicate: DefaultSwiftUIRUMActionsPredicate(isLegacyDetectionEnabled: true),
        trackFrustrations: true
    )
)

Call this immediately after Atatus.initialize(), in the same method.

Note:

The predicates default to nil, which means no automatic view or action tracking. A RUM.Configuration() with no arguments collects sessions, errors, resources, and vitals, but no views until you pass a views predicate or call the manual APIs. See Configuration Options for writing a predicate that renames or drops screens.

Note:

isLegacyDetectionEnabled: true also detects SwiftUI taps on iOS 17 and earlier, where the names are less precise and some non-interactive views are reported as taps. Pass false to report SwiftUI taps on iOS 18 and later only.

In Objective-C, set the same properties on an ATRUMConfiguration:

copy
icon/buttons/copy
@import AtatusRUM;

ATRUMConfiguration *configuration = [ATRUMConfiguration new];
configuration.sessionSampleRate = 100;
configuration.uiKitViewsPredicate = [ATDefaultUIKitRUMViewsPredicate new];
configuration.uiKitActionsPredicate = [ATDefaultUIKitRUMActionsPredicate new];

[ATRUM enableWith:configuration];

6. Instrument network requests

Set urlSessionTracking to report your application's requests as RUM resources, correlated with the view that issued them:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
        uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate(),
        urlSessionTracking: .init(
            firstPartyHostsTracing: .trace(hosts: ["api.example.com"])
        )
    )
)

This covers every client built on URLSession, including URLSession.shared, async/await calls, Alamofire, and generated API clients, with no change to your request code. firstPartyHostsTracing lists the hosts you own, which is what makes the SDK inject trace headers so a mobile request lines up with your backend trace.

Note:

A URLSessionDelegate is not required to report resources. Register one only to add the DNS, connect, TLS, and time-to-first-byte breakdown, as described in Network Tracking.

7. Enable crash reporting

Crash reporting is a separate product. Enable it after RUM so crashes are attributed to the session that was running when the application died:

copy
icon/buttons/copy
import AtatusCrashReporting

CrashReporting.enable()

A crash is written to disk as the process dies and uploaded on the next launch, attributed to the session that was running when the application died. Enable it after RUM, because a crash is reported as a RUM error. See Error Tracking for app hangs, watchdog terminations, and custom errors.

8. Configure the session sample rate

The SDK collects every session by default. Sampling reduces ingested volume proportionally, and applies at session granularity so a retained session is recorded in full:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        sessionSampleRate: 75
    )
)

The value is a percentage from 0 to 100. Because the decision is made once per session rather than per event, sampled data never contains partial user journeys.

Where a consent regime applies, pass the user's current decision at initialization. The SDK enforces it at the storage and transport layers:

Consent value SDK behavior
.granted Collects events and uploads them to Atatus.
.pending Collects events and holds them on disk, uploading nothing.
.notGranted Collects nothing.

Update the value whenever the user's decision changes:

copy
icon/buttons/copy
Atatus.set(trackingConsent: .granted)

Data buffered under .pending is uploaded if consent becomes .granted, and deleted from disk if it becomes .notGranted.

Track SwiftUI views and actions

The SwiftUI predicates cover screens the SDK can detect on its own. To name a screen yourself, or to track a view the predicate misses, add the modifiers to your view hierarchy:

copy
icon/buttons/copy
import SwiftUI
import AtatusRUM

struct CheckoutView: View {
    var body: some View {
        VStack {
            Button("Pay") { pay() }
                .trackRUMTapAction(name: "Pay")
        }
        .trackRUMView(name: "Checkout")
    }
}

trackRUMView(name:) starts the view when it appears and stops it when it disappears. trackRUMTapAction(name:) reports a tap on the control it is attached to.

Warning:

Inside a List, trackRUMTapAction can swallow the default gesture, which breaks Button actions and NavigationLink navigation. Report those actions manually with RUMMonitor.shared().addAction(type:name:) instead.

Track background events

Events recorded while no view is active, such as a request that completes after the application is backgrounded, are discarded by default because they have no view to attribute to. Enable background tracking to retain them:

copy
icon/buttons/copy
RUM.enable(
    with: RUM.Configuration(
        trackBackgroundEvents: true
    )
)
Note:

Background activity can open sessions that would not otherwise exist, which increases your billed session count. Enable it when background execution is part of what you need to observe.

Offline behavior

Events are serialized to batch files in the application's cache directory and uploaded when network availability and battery level permit, so a session recorded offline survives until connectivity returns. Batches that exceed their retention window are evicted to bound disk usage. No configuration is required.

To keep uploading for a short while after the application is backgrounded, set backgroundTasksEnabled: true on Atatus.Configuration. It uses UIApplication background tasks, which can extend execution by about 30 seconds.

Verify the integration

Turn on verbose logging in debug builds so the SDK reports what it is doing:

copy
icon/buttons/copy
#if DEBUG
Atatus.verbosityLevel = .debug
#endif

Build and run the application. The console prints upload activity for each product as batches leave the device:

⏳ (rum) Uploading batches...
   → (rum) accepted, won't be retransmitted: success

To confirm the transport path end to end, emit a synthetic error:

copy
icon/buttons/copy
import AtatusRUM

RUMMonitor.shared().addError(message: "Test Atatus setup", source: .custom)

Locating this error in your dashboard confirms initialization, batching, and upload are all working. Views, actions, resources, and errors appear within a few minutes.

Note:

Remove Atatus.verbosityLevel from release builds. It is a debugging aid, and leaving it on prints SDK internals to the device console in production.

When nothing arrives

Work through these in order:

  • Confirm site matches the region your license key belongs to.
  • Confirm the license key is the one from the RUM application you are looking at.
  • Check the console for Agent heartbeat allowed = false. The SDK polls Atatus every 30 minutes and suspends collection when your account rejects the application, for example when the license key and application name do not match a provisioned application.
  • Confirm consent is .granted. Under .pending the SDK collects but uploads nothing.
  • Confirm a views predicate is set, if views are the thing that is missing.

App Store privacy

Each module ships its own PrivacyInfo.xcprivacy manifest, declaring the data it collects and the reason it accesses UserDefaults. Xcode merges those manifests into your application's privacy report, so you do not need to copy the SDK's declarations into your own manifest. You are still responsible for declaring the data your own code collects.

Next steps