Stack trace
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2)
frame #0: CoreText`TBaseFont::GetInitializedGraphicsFont() const
frame #1: CoreText`TFont::GetAdvancesForGlyphsWithStyleFromCG(...)
frame #2: CoreText`TFont::GetUnsummedAdvancesForGlyphs(...)
frame #3: CoreText`CTFontGetTransformedAdvancesForGlyphsAndStyle
frame #4: UIFoundation`-[NSCoreTypesetter _NSFastDrawString:...]
frame #5: UIFoundation`-[NSCoreTypesetter _stringDrawingCoreTextEngineWithOriginalString:...]
frame #6: UIFoundation`__NSStringDrawingEngine
frame #7: UIFoundation`-[NSString(NSExtendedStringDrawing) boundingRectWithSize:options:attributes:context:]
frame #8: UIFoundation`-[NSString(NSStringDrawing) sizeWithAttributes:]
frame #9: UIKitCore`-[NSString(UIStringDrawingLegacy) _legacy_sizeWithFont:]
frame #10: UIKitCore`-[UITextField _textSizeUsingFullFontSize:]
frame #11: UIKitCore`-[UITextField _intrinsicSizeWithinSize:]
frame #12: UIKitCore`-[UIView(UIConstraintBasedLayout) intrinsicContentSize]
* frame #13: <AppBinary>.debug.dylib`SessionReplayViewAttributes.init(view:frame:clip:overrides:)
at ViewTreeSnapshot.swift:135:42
frame #14: <AppBinary>.debug.dylib`ViewTreeRecorder.recordRecursively(nodes:view:typeIndex:context:overrides:)
at ViewTreeRecorder.swift:84:26
frame #15-416:
<AppBinary>.debug.dylib`ViewTreeRecorder.recordRecursively(nodes:view:typeIndex:context:overrides:)
repeatedly recurses between the same small set of views
at ViewTreeRecorder.swift:102:17
frame #417: <AppBinary>.debug.dylib`ViewTreeRecorder.record(anyView:context:)
at ViewTreeRecorder.swift:32:9
frame #418: <AppBinary>.debug.dylib`UITextFieldRecorder.recordAppearance(textField:textFieldAttributes:context:)
at UITextFieldRecorder.swift:67:32
frame #419: <AppBinary>.debug.dylib`UITextFieldRecorder.semantics(view:attributes:context:)
at UITextFieldRecorder.swift:49:31
frame #421: <AppBinary>.debug.dylib`ViewTreeRecorder.nodeSemantics(view:attributes:context:)
at ViewTreeRecorder.swift:130:52
frame #422: <AppBinary>.debug.dylib`ViewTreeRecorder.recordRecursively(nodes:view:typeIndex:context:overrides:)
at ViewTreeRecorder.swift:85:25
frame #423: <AppBinary>.debug.dylib`ViewTreeRecorder.recordRecursively(nodes:view:typeIndex:context:overrides:)
at ViewTreeRecorder.swift:102:17
frame #424: <AppBinary>.debug.dylib`ViewTreeRecorder.record(anyView:context:)
at ViewTreeRecorder.swift:32:9
frame #425: <AppBinary>.debug.dylib`ViewTreeSnapshotBuilder.createSnapshot(rootView:recorderContext:)
at ViewTreeSnapshotBuilder.swift:46:38
frame #426: <AppBinary>.debug.dylib`WindowViewTreeSnapshotProducer.takeSnapshot(context:)
at WindowViewTreeSnapshotProducer.swift:22:32
frame #428: <AppBinary>.debug.dylib`Recorder.captureNextRecord(recorderContext:)
at Recorder.swift:130:67
frame #430: <AppBinary>.debug.dylib`closure #1 in RecordingCoordinator.captureNextRecord()
at RecordingCoordinator.swift:166:45
frame #432: <AppBinary>.debug.dylib`closure #1 in objc_rethrow<A>(_:file:line:)
at DDError.swift:115:29
frame #435: <AppBinary>.debug.dylib`+[__dd_private_ObjcExceptionHandler catchException:error:]
at ObjcExceptionHandler.m:14:9
frame #437: <AppBinary>.debug.dylib`objc_rethrow<A>(_:file:line:)
at DDError.swift:113:27
frame #438: <AppBinary>.debug.dylib`RecordingCoordinator.captureNextRecord()
at RecordingCoordinator.swift:166:17
frame #439: <AppBinary>.debug.dylib`closure #1 in RecordingCoordinator.init()
at RecordingCoordinator.swift:64:51
frame #441: <AppBinary>.debug.dylib`closure #1 in ScreenChangeScheduler.screenDidChange()
at ScreenChangeScheduler.swift:80:30
frame #443: <AppBinary>.debug.dylib`ScreenChangeScheduler.screenDidChange(snapshot:)
at ScreenChangeScheduler.swift:80:20
frame #444: <AppBinary>.debug.dylib`closure #1 in closure #1 in ScreenChangeScheduler.start()
at ScreenChangeScheduler.swift:57:27
frame #445: <AppBinary>.debug.dylib`CALayerChangeAggregator.deliverPendingChanges(now:)
at CALayerChangeAggregator.swift:128:13
frame #446: <AppBinary>.debug.dylib`closure #1 in CALayerChangeAggregator.scheduleDelivery()
at CALayerChangeAggregator.swift:112:18
frame #448: libdispatch.dylib`_dispatch_client_callout
frame #449: libdispatch.dylib`_dispatch_continuation_pop
frame #450: libdispatch.dylib`_dispatch_source_latch_and_call
frame #451: libdispatch.dylib`_dispatch_source_invoke
frame #452: libdispatch.dylib`_dispatch_main_queue_drain.cold.6
frame #453: libdispatch.dylib`_dispatch_main_queue_drain
frame #454: libdispatch.dylib`_dispatch_main_queue_callback_4CF
frame #455: CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
frame #456: CoreFoundation`__CFRunLoopRun
frame #457: CoreFoundation`_CFRunLoopRunSpecificWithOptions
frame #458: GraphicsServices`GSEventRunModal
frame #459: UIKitCore`-[UIApplication _run]
frame #460: UIKitCore`UIApplicationMain
frame #461: <AppBinary>.debug.dylib`main
frame #462: dyld`start
We can provide the full symbolicated crash log privately if needed. The crash is reproducible locally when attached to Xcode after enabling a secure screenshot-protection layer. Please see the attached crashing thread stack trace.
Reproduction steps
Reproduction steps
- Integrate Datadog iOS SDK with RUM enabled.
- Enable automatic UIKit action tracking using DefaultUIKitRUMActionsPredicate.
- Enable Session Replay.
- Add screenshot protection using the common secure UITextField canvas technique:
- create a secure UITextField
- set isSecureTextEntry = true
- find the private secure canvas view/layer
- move/reparent the app UIWindow.layer under that secure canvas layer
- Launch the app from Xcode in Debug.
- Navigate through normal UIKit/SwiftUI screens.
- Toggle the screenshot-protection feature on/off.
- Observe that Datadog RUM tracking becomes unreliable:
- most view tracking stops or becomes inconsistent
- most tap/action tracking stops or becomes inconsistent
- some automatic UIKit actions may still appear
- In some debug/Xcode-attached runs, the app crashes inside or around Datadog SDK behavior.
- Reproduced with Datadog iOS SDK 3.10.0.
Expected behavior
Datadog SDK should not crash or break broad RUM tracking when the app uses secure screenshot protection.
If the secure UITextField canvas technique is unsupported, we would like guidance on the recommended way to:
- keep screenshot protection enabled
- keep RUM view/action tracking working
- avoid Session Replay crashes or invalid view hierarchy traversal
- optionally disable or exclude Session Replay for protected content without breaking RUM
- Actual behavior
After enabling the secure screenshot-protection implementation:
- Datadog RUM view tracking becomes unreliable.
- Datadog automatic action tracking becomes unreliable.
- Manual action tracking for the screen-overlay toggle is also not reliably visible.
- Some older automatic events still appear, for example generic UIKit tap events, but the new screen-overlay action is not consistently visible.
- In Debug while attached to Xcode, the app can crash.
- The same flow appears more stable when launched without Xcode attached.
- Screenshot-protection implementation shape
The implementation uses a secure UITextField as a screenshot-protection container. The app locates the secure text field canvas view/layer and reparents the app window layer under that secure canvas layer so screenshots are blanked by iOS.
Pseudo-code:
let textField = UITextField()
textField.isSecureTextEntry = true
window.addSubview(textField)
let canvas = textField.subviews.first {
String(describing: type(of: $0)).contains("Canvas")
}
window.layer.superlayer?.addSublayer(textField.layer)
canvas?.layer.addSublayer(window.layer)
The app later removes the secure layer and restores the original window layer position when protection is disabled.
Datadog setup
Datadog.initialize(
with: Datadog.Configuration(
clientToken: "<redacted>",
env: "<env>",
service: "<service>"
),
trackingConsent: .granted
)
RUM.enable(
with: RUM.Configuration(
applicationID: "<redacted>",
uiKitViewsPredicate: DefaultUIKitRUMViewsPredicate(),
uiKitActionsPredicate: DefaultUIKitRUMActionsPredicate()
)
)
SessionReplay.enable(
with: SessionReplay.Configuration(
replaySampleRate: <sample-rate>,
textAndInputPrivacyLevel: .maskSensitiveInputs,
imagePrivacyLevel: .maskNone,
touchPrivacyLevel: .show
)
)
Volume
Local/debug reproduction so far. The crash is reproducible when running from Xcode after enabling screenshot protection and interacting with the app.
Not released to production yet. This issue was found during pre-release validation of an upcoming screenshot-protection feature. No production users are currently affected.
Affected SDK versions
3.10.0
We also observed similar behavior while validating older SDK versions 3.7.0 and 3.9.1, but the attached stack trace is from 3.10.0.
Latest working SDK version
We have not identified a Datadog SDK version where this secure screenshot-protection flow works correctly. Tested with 3.10.0; the issue still reproduces.
Does the crash manifest in the latest SDK version?
Yes
Deployment Target
iOS 17+
Device Information
iOS versions:
Tried on both latest 26.4.2 and older version 18.3
Other relevant information
We are implementing product-required screenshot protection. Removing the secure screenshot-protection layer is not currently an option.
The issue appears related to Datadog inspecting/traversing or swizzling UIKit/SwiftUI view hierarchy while the app window layer has been moved under a secure UITextField canvas. We need guidance on whether this setup is unsupported and, if so, what SDK-supported approach is recommended for apps that must protect screenshots while still using RUM and Session Replay.
We also observed that not only the new screen-overlay action is missing. Most view and action tracking becomes unreliable after this implementation.
Stack trace
We can provide the full symbolicated crash log privately if needed. The crash is reproducible locally when attached to Xcode after enabling a secure screenshot-protection layer. Please see the attached crashing thread stack trace.
Reproduction steps
Reproduction steps
Expected behavior
Datadog SDK should not crash or break broad RUM tracking when the app uses secure screenshot protection.
If the secure UITextField canvas technique is unsupported, we would like guidance on the recommended way to:
After enabling the secure screenshot-protection implementation:
The implementation uses a secure UITextField as a screenshot-protection container. The app locates the secure text field canvas view/layer and reparents the app window layer under that secure canvas layer so screenshots are blanked by iOS.
Pseudo-code:
The app later removes the secure layer and restores the original window layer position when protection is disabled.
Datadog setup
Volume
Local/debug reproduction so far. The crash is reproducible when running from Xcode after enabling screenshot protection and interacting with the app.
Not released to production yet. This issue was found during pre-release validation of an upcoming screenshot-protection feature. No production users are currently affected.
Affected SDK versions
3.10.0
We also observed similar behavior while validating older SDK versions 3.7.0 and 3.9.1, but the attached stack trace is from 3.10.0.
Latest working SDK version
We have not identified a Datadog SDK version where this secure screenshot-protection flow works correctly. Tested with 3.10.0; the issue still reproduces.
Does the crash manifest in the latest SDK version?
Yes
Deployment Target
iOS 17+
Device Information
iOS versions:
Tried on both latest 26.4.2 and older version 18.3
Other relevant information
We are implementing product-required screenshot protection. Removing the secure screenshot-protection layer is not currently an option.
The issue appears related to Datadog inspecting/traversing or swizzling UIKit/SwiftUI view hierarchy while the app window layer has been moved under a secure UITextField canvas. We need guidance on whether this setup is unsupported and, if so, what SDK-supported approach is recommended for apps that must protect screenshots while still using RUM and Session Replay.
We also observed that not only the new screen-overlay action is missing. Most view and action tracking becomes unreliable after this implementation.