Skip to content

Commit bac47c9

Browse files
Merge pull request #2237 from DataDog/mariedm/rum-8416-add-auto-view-tracking-option-in-rum-config
RUM-8416 Add SwiftUI predicate to RUM Configuration Co-authored-by: mariedm <marie.denis@datadoghq.com>
2 parents 09490e5 + 7999267 commit bac47c9

File tree

7 files changed

+316
-139
lines changed

7 files changed

+316
-139
lines changed

DatadogRUM/Sources/Feature/RUMFeature.swift

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ internal final class RUMFeature: DatadogRemoteFeature {
160160
featureScope: featureScope,
161161
uiKitRUMViewsPredicate: configuration.uiKitViewsPredicate,
162162
uiKitRUMActionsPredicate: configuration.uiKitActionsPredicate,
163+
swiftUIRUMViewsPredicate: configuration.swiftUIViewsPredicate,
163164
longTaskThreshold: configuration.longTaskThreshold,
164165
appHangThreshold: configuration.appHangThreshold,
165166
mainQueue: configuration.mainQueue,

DatadogRUM/Sources/Instrumentation/RUMInstrumentation.swift

+4-7
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ internal final class RUMInstrumentation: RUMCommandPublisher {
4848
featureScope: FeatureScope,
4949
uiKitRUMViewsPredicate: UIKitRUMViewsPredicate?,
5050
uiKitRUMActionsPredicate: UIKitRUMActionsPredicate?,
51+
swiftUIRUMViewsPredicate: SwiftUIRUMViewsPredicate?,
5152
longTaskThreshold: TimeInterval?,
5253
appHangThreshold: TimeInterval?,
5354
mainQueue: DispatchQueue,
@@ -59,20 +60,16 @@ internal final class RUMInstrumentation: RUMCommandPublisher {
5960
watchdogTermination: WatchdogTerminationMonitor?,
6061
memoryWarningMonitor: MemoryWarningMonitor
6162
) {
62-
// MARK: TODO: RUM-8416 - Remove after we add SwiftUI view instrumentation option
63-
// Always create views handler (we can't know if it will be used by SwiftUI instrumentation)
64-
// and only swizzle `UIViewController` if UIKit instrumentation is configured:
6563
let viewsHandler = RUMViewsHandler(
6664
dateProvider: dateProvider,
6765
uiKitPredicate: uiKitRUMViewsPredicate,
68-
swiftUIPredicate: nil,
69-
swiftUIViewNameExtractor: nil,
66+
swiftUIPredicate: swiftUIRUMViewsPredicate,
67+
swiftUIViewNameExtractor: SwiftUIReflectionBasedViewNameExtractor(),
7068
notificationCenter: notificationCenter
7169
)
7270
let viewControllerSwizzler: UIViewControllerSwizzler? = {
7371
do {
74-
// MARK: TODO: RUM-8416 - Check both predicates after we add SwiftUI view instrumentation option
75-
if uiKitRUMViewsPredicate != nil {
72+
if uiKitRUMViewsPredicate != nil || swiftUIRUMViewsPredicate != nil {
7673
return try UIViewControllerSwizzler(handler: viewsHandler)
7774
}
7875
} catch {

DatadogRUM/Sources/RUMConfiguration.swift

+18-2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ extension RUM {
8484
/// Default: `nil` - which means automatic RUM action tracking is not enabled by default.
8585
public var uiKitActionsPredicate: UIKitRUMActionsPredicate?
8686

87+
/// The predicate for automatically tracking `UIViewControllers` as RUM views.
88+
///
89+
/// RUM will query this predicate for each `UIViewController` presented in the app. The predicate implementation
90+
/// should return RUM view parameters if the given controller should start a view, or `nil` to ignore it.
91+
///
92+
/// You can use `DefaultSwiftUIRUMViewsPredicate` or create your own predicate by implementing `SwiftUIRUMViewsPredicate`.
93+
///
94+
/// Note: Automatic RUM views tracking involves swizzling the `UIViewController` lifecycle methods.
95+
///
96+
/// Default: `nil` - which means automatic RUM view tracking is not enabled by default.
97+
@available(*, message: "This API is experimental and may change in future releases")
98+
public var swiftUIViewsPredicate: SwiftUIRUMViewsPredicate?
99+
87100
/// The configuration for automatic RUM resources tracking.
88101
///
89102
/// RUM resources tracking requires enabling `URLSessionInstrumentation`. See
@@ -179,7 +192,7 @@ extension RUM {
179192
/// and do not make any assumptions on the thread used to run it.
180193
///
181194
/// Note: This mapper ensures that all views are sent by preventing the return of `nil`. To drop certain automatically
182-
/// collected RUM views, adjust the implementation of the view predicate (see the `uiKitViewsPredicate` option).
195+
/// collected RUM views, adjust the implementations of the view predicates (see the `uiKitViewsPredicate` and `swiftUIViewsPredicate` options).
183196
///
184197
/// Default: `nil`.
185198
public var viewEventMapper: RUM.ViewEventMapper?
@@ -377,8 +390,9 @@ extension RUM.Configuration {
377390
/// - Parameters:
378391
/// - applicationID: The RUM application identifier.
379392
/// - sessionSampleRate: The sampling rate for RUM sessions. Must be a value between `0` and `100`. Default: `100`.
380-
/// - uiKitViewsPredicate: The predicate for automatically tracking `UIViewControllers` as RUM views. Default: `nil`.
393+
/// - uiKitViewsPredicate: The predicate for automatically tracking `UIViewControllers` in `UIKit` as RUM views. Default: `nil`.
381394
/// - uiKitActionsPredicate: The predicate for automatically tracking `UITouch` events as RUM actions. Default: `nil`.
395+
/// - swiftUIViewsPredicate: The predicate for automatically tracking `UIViewControllers` in `SwiftUI` as RUM views. Default: `nil`.
382396
/// - urlSessionTracking: The configuration for automatic RUM resources tracking. Default: `nil`.
383397
/// - trackFrustrations: Determines whether automatic tracking of user frustrations should be enabled. Default: `true`.
384398
/// - trackBackgroundEvents: Determines whether RUM events should be tracked when no view is active. Default: `false`.
@@ -404,6 +418,7 @@ extension RUM.Configuration {
404418
sessionSampleRate: SampleRate = .maxSampleRate,
405419
uiKitViewsPredicate: UIKitRUMViewsPredicate? = nil,
406420
uiKitActionsPredicate: UIKitRUMActionsPredicate? = nil,
421+
swiftUIViewsPredicate: SwiftUIRUMViewsPredicate? = nil,
407422
urlSessionTracking: URLSessionTracking? = nil,
408423
trackFrustrations: Bool = true,
409424
trackBackgroundEvents: Bool = false,
@@ -428,6 +443,7 @@ extension RUM.Configuration {
428443
self.sessionSampleRate = sessionSampleRate
429444
self.uiKitViewsPredicate = uiKitViewsPredicate
430445
self.uiKitActionsPredicate = uiKitActionsPredicate
446+
self.swiftUIViewsPredicate = swiftUIViewsPredicate
431447
self.urlSessionTracking = urlSessionTracking
432448
self.trackFrustrations = trackFrustrations
433449
self.trackBackgroundEvents = trackBackgroundEvents

DatadogRUM/Tests/Instrumentation/RUMInstrumentationTests.swift

+36
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class RUMInstrumentationTests: XCTestCase {
1818
featureScope: NOPFeatureScope(),
1919
uiKitRUMViewsPredicate: UIKitRUMViewsPredicateMock(),
2020
uiKitRUMActionsPredicate: nil,
21+
swiftUIRUMViewsPredicate: nil,
2122
longTaskThreshold: nil,
2223
appHangThreshold: .mockAny(),
2324
mainQueue: .main,
@@ -46,6 +47,7 @@ class RUMInstrumentationTests: XCTestCase {
4647
featureScope: NOPFeatureScope(),
4748
uiKitRUMViewsPredicate: nil,
4849
uiKitRUMActionsPredicate: UIKitRUMActionsPredicateMock(),
50+
swiftUIRUMViewsPredicate: nil,
4951
longTaskThreshold: nil,
5052
appHangThreshold: .mockAny(),
5153
mainQueue: .main,
@@ -65,12 +67,42 @@ class RUMInstrumentationTests: XCTestCase {
6567
}
6668
}
6769

70+
func testWhenOnlySwiftUIViewsPredicateIsConfigured_itInstrumentsUIViewController() throws {
71+
// When
72+
let instrumentation = RUMInstrumentation(
73+
featureScope: NOPFeatureScope(),
74+
uiKitRUMViewsPredicate: nil,
75+
uiKitRUMActionsPredicate: nil,
76+
swiftUIRUMViewsPredicate: SwiftUIRUMViewsPredicateMock(),
77+
longTaskThreshold: nil,
78+
appHangThreshold: .mockAny(),
79+
mainQueue: .main,
80+
dateProvider: SystemDateProvider(),
81+
backtraceReporter: BacktraceReporterMock(),
82+
fatalErrorContext: FatalErrorContextNotifierMock(),
83+
processID: .mockAny(),
84+
notificationCenter: .default,
85+
watchdogTermination: .mockRandom(),
86+
memoryWarningMonitor: .mockRandom()
87+
)
88+
89+
// Then
90+
withExtendedLifetime(instrumentation) {
91+
DDAssertActiveSwizzlings([
92+
"viewDidAppear:",
93+
"viewDidDisappear:",
94+
])
95+
XCTAssertNil(instrumentation.longTasks)
96+
}
97+
}
98+
6899
func testWhenOnlyLongTasksThresholdIsConfigured_itInstrumentsRunLoop() throws {
69100
// When
70101
let instrumentation = RUMInstrumentation(
71102
featureScope: NOPFeatureScope(),
72103
uiKitRUMViewsPredicate: nil,
73104
uiKitRUMActionsPredicate: nil,
105+
swiftUIRUMViewsPredicate: nil,
74106
longTaskThreshold: 0.5,
75107
appHangThreshold: .mockAny(),
76108
mainQueue: .main,
@@ -99,6 +131,7 @@ class RUMInstrumentationTests: XCTestCase {
99131
featureScope: NOPFeatureScope(),
100132
uiKitRUMViewsPredicate: nil,
101133
uiKitRUMActionsPredicate: nil,
134+
swiftUIRUMViewsPredicate: nil,
102135
longTaskThreshold: .mockRandom(min: -100, max: 0),
103136
appHangThreshold: .mockAny(),
104137
mainQueue: .main,
@@ -123,6 +156,7 @@ class RUMInstrumentationTests: XCTestCase {
123156
featureScope: NOPFeatureScope(),
124157
uiKitRUMViewsPredicate: nil,
125158
uiKitRUMActionsPredicate: nil,
159+
swiftUIRUMViewsPredicate: nil,
126160
longTaskThreshold: .mockRandom(min: -100, max: 0),
127161
appHangThreshold: 2,
128162
mainQueue: .main,
@@ -147,6 +181,7 @@ class RUMInstrumentationTests: XCTestCase {
147181
featureScope: NOPFeatureScope(),
148182
uiKitRUMViewsPredicate: nil,
149183
uiKitRUMActionsPredicate: nil,
184+
swiftUIRUMViewsPredicate: nil,
150185
longTaskThreshold: .mockRandom(min: -100, max: 0),
151186
appHangThreshold: nil,
152187
mainQueue: .main,
@@ -171,6 +206,7 @@ class RUMInstrumentationTests: XCTestCase {
171206
featureScope: NOPFeatureScope(),
172207
uiKitRUMViewsPredicate: UIKitRUMViewsPredicateMock(),
173208
uiKitRUMActionsPredicate: UIKitRUMActionsPredicateMock(),
209+
swiftUIRUMViewsPredicate: nil,
174210
longTaskThreshold: 0.5,
175211
appHangThreshold: 2,
176212
mainQueue: .main,

0 commit comments

Comments
 (0)