Session Replay records what a user saw and did in your iOS application, so you can watch a bug happen instead of reconstructing it from a stack trace. Replays are linked to the RUM session they belong to, so you can move from an error straight to the recording. Session Replay ships as its own product, AtatusSessionReplay, and runs on top of RUM.
Session Replay is available on iOS and iPadOS only. The module is empty on tvOS, so SessionReplay.enable does not exist there.

How it records

The SDK does not record the screen as video. It walks the view hierarchy and captures the shape of each view: its frame, background, border, corner radius, opacity, and the type-specific details such as label text, switch position, or image content. Those become wireframes that the player reassembles into a replay. Two consequences follow from that design:
  • Masking happens on the device, before anything is written to disk. Text you mask never leaves the device at all.
  • Snapshots are taken when the screen changes, at most ten times a second, and not at all while the screen is static.

Prerequisites

  • RUM is set up and tracking views. See iOS Monitoring Setup.
  • iOS 15.0 or later.
  • The AtatusSessionReplay product is added to your target.
Session Replay captures nothing without RUM. Every record is attached to the active RUM view, so an application that enables RUM without a views predicate, and without calling startView itself, produces empty replays.

Setup

1. Add the product

With Swift Package Manager, add the AtatusSessionReplay product to your target. With CocoaPods, declare the pod:

2. Enable Session Replay

Enable it after RUM, in the same place you initialize the SDK:
The defaults mask all text, all images, and all touches, so a replay recorded with no privacy configuration shows layout and interaction without revealing content. Relax that deliberately, and per view rather than globally.
SessionReplay.enable never throws. When something is wrong, such as the SDK not being initialized yet or the product being enabled twice, it prints the reason to the Xcode console and carries on. Check the console first when no replay appears.

Configuration

Sampling

The replay rate applies to sessions RUM has already sampled, and the two multiply. With a RUM sessionSampleRate of 80 and a replaySampleRate of 20, one in five recorded sessions has a replay, which is 16 percent of all sessions:
The decision is made once per session and derived from the session ID, so sessions with a replay are always a subset of the sessions RUM kept. A session either has a complete replay or none.
replaySampleRate: 0 does not register the product at all, so SessionReplay.startRecording() has nothing to start. To keep recording off until the user asks for it, use startRecordingImmediately: false with a rate above 0.

Start and stop recording yourself

To hold recording back until a point you choose, for example until the user opts in, enable the product without starting it:
Stopping is not the same as withdrawing consent. It stops new records, and anything already recorded still uploads. To delete collected data, set consent to .notGranted or call Atatus.clearAllData().

Mask sensitive content

Text and input

textAndInputPrivacyLevel divides text into four kinds and treats each differently: Static text is masked character by character, so "Jane Doe" becomes "xxxx xxx". Word lengths and spacing survive, which keeps the layout faithful, so treat the length of a string as something that does leave the device. Sensitive fields are always ***, at every level and with no way to reveal them. A field counts as sensitive when it is a secure text entry field, or when its textContentType is a password, one-time code, email address, telephone number, credit card number, postal address, or postal code. At .maskAll and .maskAllInputs, the state of a control is masked too: a switch loses its on and off position, a slider loses its value, a stepper and a date picker lose their selection, and a segmented control loses the selected segment.

Images

imagePrivacyLevel decides which images are recorded: .maskNonBundledOnly is the useful middle ground: your interface keeps its icons while user content, such as a profile photo or a document preview, stays masked.
In SwiftUI the SDK cannot tell whether an image came from your bundle, so .maskNonBundledOnly falls back to a size test: a raster image is recorded only when it is 100 points or smaller on both sides, and vector images are always recorded. Mask SwiftUI images you care about explicitly rather than relying on that heuristic.

Touches

touchPrivacyLevel is .hide by default, which records no touch coordinates at all. Set .show to draw touch indicators in the replay:

Override privacy for one view

Global levels are the floor, not the ceiling. Any UIKit view can carry its own levels, which its subviews inherit:
A child’s value wins over its parent’s, except hide, which cannot be undone by a child. A hidden view is recorded as a solid placeholder, so the replay keeps the space it occupied without any of its content.
An override changes the text of a switch, slider, stepper, segmented control, or date picker, but not its state. Those controls read the global textAndInputPrivacyLevel for their value, so set the global level or hide the view if the value itself is sensitive.

SwiftUI

SwiftUI recording is experimental and off by default. With the flag off, a SwiftUI screen is recorded as a single placeholder, which makes replays of a SwiftUI application close to useless. Turn it on to record the view tree:
SwiftUI recording reads SwiftUI’s internal rendering structures, which change between iOS releases. Fidelity can vary by OS version, and a new major iOS release can reduce it until the SDK is updated. Test your replays after each iOS upgrade, and treat this flag as experimental.
To mask part of a SwiftUI hierarchy, wrap it in SessionReplayPrivacyView, which needs iOS 16 or later:
There is no view modifier equivalent, so the wrapper is the only way to scope privacy in SwiftUI. It is inert when Session Replay is not enabled, so it is safe to leave in place.

Web views and system screens

A WKWebView is recorded as a reserved area that the web content fills in from the page side. Install the browser agent in the page and enable WebView tracking natively:
Without an instrumented page, the web view appears as an empty area in the replay. See WebView Tracking. Some screens are never recorded because they belong to the system rather than your application. A Safari view controller appears as a placeholder labelled Safari, and the share sheet as one labelled Activity. Alerts are recorded normally.

Offline behavior

Records are compressed and written to disk, then uploaded when the network is reachable and the device is not low on battery or in Low Power Mode. Replay data is held for up to five hours, which is shorter than the retention for other events, so a device offline for longer than that loses the oldest part of the recording. Everything else uploads when connectivity returns.

Objective-C

Session Replay is available to Objective-C with AT-prefixed types:
startRecordingImmediately and customEndpoint are properties rather than initializer parameters in Objective-C, and SessionReplayPrivacyView has no Objective-C equivalent because it is SwiftUI only.

Next steps