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.
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:
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:
pod 'AtatusCore', '~> 1.0'
pod 'AtatusRUM', '~> 1.0'
pod 'AtatusCrashReporting', '~> 1.0'
Then install them:
$ pod install
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
productionorstaging.
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:
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 |
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:
@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:
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:
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.
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:
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.
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.
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:
@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:
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.
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:
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:
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.
9. Configure tracking consent
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:
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:
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.
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:
RUM.enable(
with: RUM.Configuration(
trackBackgroundEvents: true
)
)
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:
#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:
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.
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
sitematches 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.pendingthe 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
- Tune the SDK and emit custom events with Configuration Options.
- Report API calls as resources with Network Tracking.
- Connect mobile requests to backend traces with Trace Collection.
- Report crashes, app hangs, and custom errors with Error Tracking.
- Measure launch time, frame rate, and hangs with Mobile Vitals.
- Record and replay real sessions with Session Replay.
- Correlate embedded web content with the native session using WebView Tracking.
- Forward application logs with Log Collection.
+1-415-800-4104