Skip to content

Commit

Permalink
Fix Session Replay ObjC interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mariedm committed Oct 7, 2024
1 parent 7cdb6e3 commit f23fc1c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 70 deletions.
6 changes: 3 additions & 3 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@
61FDBA15269722B4001D9D43 /* CrashReportMinifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FDBA14269722B4001D9D43 /* CrashReportMinifierTests.swift */; };
61FDBA1726974CA9001D9D43 /* DDCrashReportBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FDBA1626974CA9001D9D43 /* DDCrashReportBuilderTests.swift */; };
61FF282824B8A31E000B3D9B /* RUMEventMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FF282724B8A31E000B3D9B /* RUMEventMatcher.swift */; };
962C41A92CB00FD60050B747 /* DDSessionReplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */; };
969B3B212C33F80500D62400 /* UIActivityIndicatorRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */; };
969B3B232C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */; };
96E414142C2AF56F005A6119 /* UIProgressViewRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E414132C2AF56F005A6119 /* UIProgressViewRecorder.swift */; };
Expand Down Expand Up @@ -1296,7 +1297,6 @@
D2A1EE452886B8B400D28DFB /* UserInfoPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */; };
D2A434A22A8E3F900028E329 /* DatadogSessionReplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6133D1F52A6ED9E100384BEF /* DatadogSessionReplay.framework */; };
D2A434AA2A8E40A20028E329 /* SessionReplay+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A434A82A8E402B0028E329 /* SessionReplay+objc.swift */; };
D2A434AE2A8E426C0028E329 /* DDSessionReplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */; };
D2A783D429A5309F003B03BB /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BBA2423979B00786299 /* SwiftExtensions.swift */; };
D2A783D529A530A0003B03BB /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BBA2423979B00786299 /* SwiftExtensions.swift */; };
D2A783D929A530EF003B03BB /* SwiftExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E36D92124373EA700BFBDB7 /* SwiftExtensionsTests.swift */; };
Expand Down Expand Up @@ -3574,6 +3574,7 @@
children = (
61054F482A6EE1B900AAA894 /* SessionReplayTests.swift */,
61054F3D2A6EE1B900AAA894 /* SessionReplayConfigurationTests.swift */,
D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */,
61054F882A6EE1BA00AAA894 /* Feature */,
61054F922A6EE1BA00AAA894 /* Helpers */,
61054F7D2A6EE1BA00AAA894 /* Mocks */,
Expand Down Expand Up @@ -4327,7 +4328,6 @@
A7DA18062AB0CA4700F76337 /* DDUIKitRUMActionsPredicateTests.swift */,
9EE5AD8126205B82001E699E /* DDNSURLSessionDelegateTests.swift */,
3CCCA5C62ABAF5230029D7BD /* DDURLSessionInstrumentationConfigurationTests.swift */,
D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */,
61D03BDE273404BB00367DE0 /* RUM */,
F603F1282CAEA4E90088E6B7 /* DDInternalLoggerTests.swift */,
);
Expand Down Expand Up @@ -8180,7 +8180,6 @@
615A4A8924A34FD700233986 /* DDTracerTests.swift in Sources */,
6128F58A2BA9860B00D35B08 /* DataStoreFileReaderTests.swift in Sources */,
61A2CC212A443D330000FF25 /* DDRUMConfigurationTests.swift in Sources */,
D2A434AE2A8E426C0028E329 /* DDSessionReplayTests.swift in Sources */,
61D03BE0273404E700367DE0 /* RUMDataModels+objcTests.swift in Sources */,
3CA00B072C2AE52400E6FE01 /* WatchdogTerminationsMonitoringTests.swift in Sources */,
6167E70E2B83502200C3CA2D /* DatadogCore+FeatureDirectoriesTests.swift in Sources */,
Expand Down Expand Up @@ -8449,6 +8448,7 @@
D2BCB2A32B7B9683005C2AAB /* WKWebViewRecorderTests.swift in Sources */,
61054FC62A6EE1BA00AAA894 /* CoreGraphicsMocks.swift in Sources */,
61054FCA2A6EE1BA00AAA894 /* TestScheduler.swift in Sources */,
962C41A92CB00FD60050B747 /* DDSessionReplayTests.swift in Sources */,
61054FBD2A6EE1BA00AAA894 /* UIViewRecorderTests.swift in Sources */,
61054F952A6EE1BA00AAA894 /* SessionReplayConfigurationTests.swift in Sources */,
61054FAC2A6EE1BA00AAA894 /* CGRect+ContentFrameTests.swift in Sources */,
Expand Down
51 changes: 28 additions & 23 deletions DatadogSessionReplay/Sources/SessionReplay+objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import DatadogInternal
#if os(iOS)

/// An entry point to Datadog Session Replay feature.
@objc
public final class DDSessionReplay: NSObject {
@objc(DDSessionReplay)
@objcMembers
@_spi(objc)
public final class objc_SessionReplay: NSObject {
override private init() { }

/// Enables Datadog Session Replay feature.
Expand All @@ -23,7 +25,7 @@ public final class DDSessionReplay: NSObject {
/// - Parameters:
/// - configuration: Configuration of the feature.
@objc
public static func enable(with configuration: DDSessionReplayConfiguration) {
public static func enable(with configuration: objc_SessionReplayConfiguration) {
SessionReplay.enable(with: configuration._swift)
}

Expand All @@ -41,8 +43,10 @@ public final class DDSessionReplay: NSObject {
}

/// Session Replay feature configuration.
@objc
public final class DDSessionReplayConfiguration: NSObject {
@objc(DDSessionReplayConfiguration)
@objcMembers
@_spi(objc)
public final class objc_SessionReplayConfiguration: NSObject {
internal var _swift: SessionReplay.Configuration = .init(replaySampleRate: 0)

/// The sampling rate for Session Replay. It is applied in addition to the RUM session sample rate.
Expand All @@ -62,31 +66,31 @@ public final class DDSessionReplayConfiguration: NSObject {
///
/// Default: `.mask`.
@available(*, deprecated, message: "This will be removed in future versions of the SDK. Use the new privacy levels instead.")
@objc public var defaultPrivacyLevel: DDSessionReplayConfigurationPrivacyLevel {
@objc public var defaultPrivacyLevel: objc_SessionReplayConfigurationPrivacyLevel {
set { _swift.defaultPrivacyLevel = newValue._swift }
get { .init(_swift.defaultPrivacyLevel) }
}

/// Defines the way texts and inputs (e.g. labels, textfields, checkboxes) should be masked.
///
/// Default: `.maskAll`.
@objc public var textAndInputPrivacyLevel: DDTextAndInputPrivacyLevel {
@objc public var textAndInputPrivacyLevel: objc_TextAndInputPrivacyLevel {
set { _swift.textAndInputPrivacyLevel = newValue._swift }
get { .init(_swift.textAndInputPrivacyLevel) }
}

/// Defines the way images should be masked.
///
/// Default: `.maskAll`.
@objc public var imagePrivacyLevel: DDImagePrivacyLevel {
@objc public var imagePrivacyLevel: objc_ImagePrivacyLevel {
set { _swift.imagePrivacyLevel = newValue._swift }
get { .init(_swift.imagePrivacyLevel) }
}

/// Defines the way user touches (e.g. tap) should be masked.
///
/// Default: `.mask`.
@objc public var touchPrivacyLevel: DDTouchPrivacyLevel {
@objc public var touchPrivacyLevel: objc_TouchPrivacyLevel {
set { _swift.touchPrivacyLevel = newValue._swift }
get { .init(_swift.touchPrivacyLevel) }
}
Expand All @@ -109,9 +113,9 @@ public final class DDSessionReplayConfiguration: NSObject {
@objc
public required init(
replaySampleRate: Float,
textAndInputPrivacyLevel: DDTextAndInputPrivacyLevel,
imagePrivacyLevel: DDImagePrivacyLevel,
touchPrivacyLevel: DDTouchPrivacyLevel
textAndInputPrivacyLevel: objc_TextAndInputPrivacyLevel,
imagePrivacyLevel: objc_ImagePrivacyLevel,
touchPrivacyLevel: objc_TouchPrivacyLevel
) {
_swift = SessionReplay.Configuration(
replaySampleRate: replaySampleRate,
Expand Down Expand Up @@ -139,8 +143,9 @@ public final class DDSessionReplayConfiguration: NSObject {
}

/// Available privacy levels for content masking.
@objc
public enum DDSessionReplayConfigurationPrivacyLevel: Int {
@objc(DDSessionReplayConfigurationPrivacyLevel)
@_spi(objc)
public enum objc_SessionReplayConfigurationPrivacyLevel: Int {
/// Record all content.
case allow

Expand All @@ -155,7 +160,6 @@ public enum DDSessionReplayConfigurationPrivacyLevel: Int {
case .allow: return .allow
case .mask: return .mask
case .maskUserInput: return .maskUserInput
default: return .mask
}
}

Expand All @@ -169,8 +173,9 @@ public enum DDSessionReplayConfigurationPrivacyLevel: Int {
}

/// Available privacy levels for text and input masking.
@objc
public enum DDTextAndInputPrivacyLevel: Int {
@objc(DDTextAndInputPrivacyLevel)
@_spi(objc)
public enum objc_TextAndInputPrivacyLevel: Int {
/// Show all text except sensitive input (eg. password fields).
case maskSensitiveInputs

Expand All @@ -185,7 +190,6 @@ public enum DDTextAndInputPrivacyLevel: Int {
case .maskSensitiveInputs: return .maskSensitiveInputs
case .maskAllInputs: return .maskAllInputs
case .maskAll: return .maskAll
default: return .maskAll
}
}

Expand All @@ -199,8 +203,9 @@ public enum DDTextAndInputPrivacyLevel: Int {
}

/// Available image privacy levels for image masking.
@objc
public enum DDImagePrivacyLevel: Int {
@objc(DDImagePrivacyLevel)
@_spi(objc)
public enum objc_ImagePrivacyLevel: Int {
/// Only SF Symbols and images loaded using UIImage(named:) that are bundled within the application package will be recorded.
case maskNonBundledOnly
/// No images will be recorded.
Expand All @@ -226,8 +231,9 @@ public enum DDImagePrivacyLevel: Int {
}

/// Available privacy levels for content masking.
@objc
public enum DDTouchPrivacyLevel: Int {
@objc(DDTouchPrivacyLevel)
@_spi(objc)
public enum objc_TouchPrivacyLevel: Int {
/// Show all touches.
case show

Expand All @@ -238,7 +244,6 @@ public enum DDTouchPrivacyLevel: Int {
switch self {
case .show: return .show
case .hide: return .hide
default: return .hide
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import XCTest
import TestUtilities
import DatadogInternal

@_spi(objc)
@testable import DatadogSessionReplay

class DDSessionReplayTests: XCTestCase {
Expand All @@ -18,7 +18,7 @@ class DDSessionReplayTests: XCTestCase {
let sampleRate: Float = .mockRandom(min: 0, max: 100)

// When
let config = DDSessionReplayConfiguration(replaySampleRate: sampleRate)
let config = objc_SessionReplayConfiguration(replaySampleRate: sampleRate)

// Then
XCTAssertEqual(config._swift.replaySampleRate, sampleRate)
Expand All @@ -31,13 +31,13 @@ class DDSessionReplayTests: XCTestCase {

func testConfigurationWithNewApi() {
// Given
let textAndInputPrivacy: DDTextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let touchPrivacy: DDTouchPrivacyLevel = [.show, .hide].randomElement()!
let imagePrivacy: DDImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let textAndInputPrivacy: objc_TextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let touchPrivacy: objc_TouchPrivacyLevel = [.show, .hide].randomElement()!
let imagePrivacy: objc_ImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let sampleRate: Float = .mockRandom(min: 0, max: 100)

// When
let config = DDSessionReplayConfiguration(
let config = objc_SessionReplayConfiguration(
replaySampleRate: sampleRate,
textAndInputPrivacyLevel: textAndInputPrivacy,
imagePrivacyLevel: imagePrivacy,
Expand All @@ -55,14 +55,14 @@ class DDSessionReplayTests: XCTestCase {
func testConfigurationOverrides() {
// Given
let sampleRate: Float = .mockRandom(min: 0, max: 100)
let privacy: DDSessionReplayConfigurationPrivacyLevel = [.allow, .mask, .maskUserInput].randomElement()!
let textAndInputPrivacy: DDTextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: DDImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: DDTouchPrivacyLevel = [.show, .hide].randomElement()!
let privacy: objc_SessionReplayConfigurationPrivacyLevel = [.allow, .mask, .maskUserInput].randomElement()!
let textAndInputPrivacy: objc_TextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: objc_ImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: objc_TouchPrivacyLevel = [.show, .hide].randomElement()!
let url: URL = .mockRandom()

// When
let config = DDSessionReplayConfiguration(replaySampleRate: 100)
let config = objc_SessionReplayConfiguration(replaySampleRate: 100)
config.replaySampleRate = sampleRate
config.defaultPrivacyLevel = privacy
config.textAndInputPrivacyLevel = textAndInputPrivacy
Expand All @@ -82,13 +82,13 @@ class DDSessionReplayTests: XCTestCase {
func testConfigurationOverridesWithNewApi() {
// Given
let sampleRate: Float = .mockRandom(min: 0, max: 100)
let textAndInputPrivacy: DDTextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: DDImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: DDTouchPrivacyLevel = [.show, .hide].randomElement()!
let textAndInputPrivacy: objc_TextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: objc_ImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: objc_TouchPrivacyLevel = [.show, .hide].randomElement()!
let url: URL = .mockRandom()

// When
let config = DDSessionReplayConfiguration(
let config = objc_SessionReplayConfiguration(
replaySampleRate: 100,
textAndInputPrivacyLevel: .maskAll,
imagePrivacyLevel: .maskAll,
Expand All @@ -109,41 +109,41 @@ class DDSessionReplayTests: XCTestCase {
}

func testPrivacyLevelsInterop() {
XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.allow._swift, .allow)
XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.mask._swift, .mask)
XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel.maskUserInput._swift, .maskUserInput)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel.allow._swift, .allow)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel.mask._swift, .mask)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel.maskUserInput._swift, .maskUserInput)

XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.allow), .allow)
XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.mask), .mask)
XCTAssertEqual(DDSessionReplayConfigurationPrivacyLevel(.maskUserInput), .maskUserInput)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel(.allow), .allow)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel(.mask), .mask)
XCTAssertEqual(objc_SessionReplayConfigurationPrivacyLevel(.maskUserInput), .maskUserInput)
}

func testTextAndInputPrivacyLevelsInterop() {
XCTAssertEqual(DDTextAndInputPrivacyLevel.maskAll._swift, .maskAll)
XCTAssertEqual(DDTextAndInputPrivacyLevel.maskAllInputs._swift, .maskAllInputs)
XCTAssertEqual(DDTextAndInputPrivacyLevel.maskSensitiveInputs._swift, .maskSensitiveInputs)
XCTAssertEqual(objc_TextAndInputPrivacyLevel.maskAll._swift, .maskAll)
XCTAssertEqual(objc_TextAndInputPrivacyLevel.maskAllInputs._swift, .maskAllInputs)
XCTAssertEqual(objc_TextAndInputPrivacyLevel.maskSensitiveInputs._swift, .maskSensitiveInputs)

XCTAssertEqual(DDTextAndInputPrivacyLevel(.maskAll), .maskAll)
XCTAssertEqual(DDTextAndInputPrivacyLevel(.maskAllInputs), .maskAllInputs)
XCTAssertEqual(DDTextAndInputPrivacyLevel(.maskSensitiveInputs), .maskSensitiveInputs)
XCTAssertEqual(objc_TextAndInputPrivacyLevel(.maskAll), .maskAll)
XCTAssertEqual(objc_TextAndInputPrivacyLevel(.maskAllInputs), .maskAllInputs)
XCTAssertEqual(objc_TextAndInputPrivacyLevel(.maskSensitiveInputs), .maskSensitiveInputs)
}

func testImagePrivacyLevelsInterop() {
XCTAssertEqual(DDImagePrivacyLevel.maskAll._swift, .maskAll)
XCTAssertEqual(DDImagePrivacyLevel.maskNonBundledOnly._swift, .maskNonBundledOnly)
XCTAssertEqual(DDImagePrivacyLevel.maskNone._swift, .maskNone)
XCTAssertEqual(objc_ImagePrivacyLevel.maskAll._swift, .maskAll)
XCTAssertEqual(objc_ImagePrivacyLevel.maskNonBundledOnly._swift, .maskNonBundledOnly)
XCTAssertEqual(objc_ImagePrivacyLevel.maskNone._swift, .maskNone)

XCTAssertEqual(DDImagePrivacyLevel(.maskAll), .maskAll)
XCTAssertEqual(DDImagePrivacyLevel(.maskNonBundledOnly), .maskNonBundledOnly)
XCTAssertEqual(DDImagePrivacyLevel(.maskNone), .maskNone)
XCTAssertEqual(objc_ImagePrivacyLevel(.maskAll), .maskAll)
XCTAssertEqual(objc_ImagePrivacyLevel(.maskNonBundledOnly), .maskNonBundledOnly)
XCTAssertEqual(objc_ImagePrivacyLevel(.maskNone), .maskNone)
}

func testTouchPrivacyLevelsInterop() {
XCTAssertEqual(DDTouchPrivacyLevel.show._swift, .show)
XCTAssertEqual(DDTouchPrivacyLevel.hide._swift, .hide)
XCTAssertEqual(objc_TouchPrivacyLevel.show._swift, .show)
XCTAssertEqual(objc_TouchPrivacyLevel.hide._swift, .hide)

XCTAssertEqual(DDTouchPrivacyLevel(.show), .show)
XCTAssertEqual(DDTouchPrivacyLevel(.hide), .hide)
XCTAssertEqual(objc_TouchPrivacyLevel(.show), .show)
XCTAssertEqual(objc_TouchPrivacyLevel(.hide), .hide)
}

func testWhenEnabled() throws {
Expand All @@ -152,10 +152,10 @@ class DDSessionReplayTests: XCTestCase {
CoreRegistry.register(default: core)
defer { CoreRegistry.unregisterDefault() }

let config = DDSessionReplayConfiguration(replaySampleRate: 42)
let config = objc_SessionReplayConfiguration(replaySampleRate: 42)

// When
DDSessionReplay.enable(with: config)
objc_SessionReplay.enable(with: config)

// Then
let sr = try XCTUnwrap(core.get(feature: SessionReplayFeature.self))
Expand All @@ -171,20 +171,20 @@ class DDSessionReplayTests: XCTestCase {
// Given
let core = FeatureRegistrationCoreMock()
CoreRegistry.register(default: core)
let textAndInputPrivacy: DDTextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: DDImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: DDTouchPrivacyLevel = [.show, .hide].randomElement()!
let textAndInputPrivacy: objc_TextAndInputPrivacyLevel = [.maskAll, .maskAllInputs, .maskSensitiveInputs].randomElement()!
let imagePrivacy: objc_ImagePrivacyLevel = [.maskAll, .maskNonBundledOnly, .maskNone].randomElement()!
let touchPrivacy: objc_TouchPrivacyLevel = [.show, .hide].randomElement()!
defer { CoreRegistry.unregisterDefault() }

let config = DDSessionReplayConfiguration(
let config = objc_SessionReplayConfiguration(
replaySampleRate: 42,
textAndInputPrivacyLevel: textAndInputPrivacy,
imagePrivacyLevel: imagePrivacy,
touchPrivacyLevel: touchPrivacy
)

// When
DDSessionReplay.enable(with: config)
objc_SessionReplay.enable(with: config)

// Then
let sr = try XCTUnwrap(core.get(feature: SessionReplayFeature.self))
Expand Down

0 comments on commit f23fc1c

Please sign in to comment.