diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 7dd04c6ab6..5b4b2c764b 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -533,6 +533,7 @@ A70ADCD22B583B1300321BC9 /* UIImageResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70ADCD12B583B1300321BC9 /* UIImageResource.swift */; }; A71013D62B178FAD00101E60 /* ResourcesWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71013D52B178FAD00101E60 /* ResourcesWriterTests.swift */; }; A71265862B17980C007D63CE /* MockFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71265852B17980C007D63CE /* MockFeature.swift */; }; + A727C4BB2BADB3AB00707DFD /* DDSessionReplay+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A795069D2B974CAA00AC4814 /* DDSessionReplay+apiTests.m */; }; A728ADAB2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAA2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift */; }; A728ADAC2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAA2934EA2100397996 /* W3CHTTPHeadersWriter+objc.swift */; }; A728ADB02934EB0900397996 /* DDW3CHTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */; }; @@ -542,6 +543,7 @@ A74A72852B10CC6700771FEB /* ResourceRequestBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74A72842B10CC6700771FEB /* ResourceRequestBuilderTests.swift */; }; A74A72872B10CE4100771FEB /* ResourceMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74A72862B10CE4100771FEB /* ResourceMocks.swift */; }; A74A72892B10D95D00771FEB /* MultipartBuilderSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74A72882B10D95D00771FEB /* MultipartBuilderSpy.swift */; }; + A795069C2B974C8200AC4814 /* SessionReplay+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795069B2B974C8100AC4814 /* SessionReplay+objc.swift */; }; A79B0F64292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */; }; A79B0F65292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */; }; A79B0F66292BD7CA008742B3 /* B3HTTPHeadersWriter+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */; }; @@ -1119,7 +1121,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 */; }; - D2A434AC2A8E416F0028E329 /* DDSessionReplay+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */; }; 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 */; }; @@ -2496,6 +2497,8 @@ A74A72842B10CC6700771FEB /* ResourceRequestBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceRequestBuilderTests.swift; sourceTree = ""; }; A74A72862B10CE4100771FEB /* ResourceMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceMocks.swift; sourceTree = ""; }; A74A72882B10D95D00771FEB /* MultipartBuilderSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipartBuilderSpy.swift; sourceTree = ""; }; + A795069B2B974C8100AC4814 /* SessionReplay+objc.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SessionReplay+objc.swift"; sourceTree = ""; }; + A795069D2B974CAA00AC4814 /* DDSessionReplay+apiTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "DDSessionReplay+apiTests.m"; sourceTree = ""; }; A79B0F5A292B7C06008742B3 /* B3HTTPHeadersWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersWriterTests.swift; sourceTree = ""; }; A79B0F5E292BA435008742B3 /* B3HTTPHeadersWriter+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "B3HTTPHeadersWriter+objc.swift"; sourceTree = ""; }; A79B0F60292BB071008742B3 /* B3HTTPHeadersReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = B3HTTPHeadersReaderTests.swift; sourceTree = ""; }; @@ -2711,7 +2714,6 @@ D2A1EE432886B8B400D28DFB /* UserInfoPublisherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoPublisherTests.swift; sourceTree = ""; }; D2A38DDA29C37E1B007C6900 /* TracingURLSessionHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingURLSessionHandlerTests.swift; sourceTree = ""; }; D2A434A82A8E402B0028E329 /* SessionReplay+objc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionReplay+objc.swift"; sourceTree = ""; }; - D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDSessionReplay+apiTests.m"; sourceTree = ""; }; D2A434AD2A8E426C0028E329 /* DDSessionReplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDSessionReplayTests.swift; sourceTree = ""; }; D2A7840129A534F9003B03BB /* DatadogLogsTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "DatadogLogsTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D2A7840229A536AD003B03BB /* PrintFunctionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrintFunctionMock.swift; sourceTree = ""; }; @@ -3186,6 +3188,7 @@ children = ( 61054E0C2A6EE10A00AAA894 /* SessionReplay.swift */, 61054E0B2A6EE10A00AAA894 /* SessionReplayConfiguration.swift */, + A795069B2B974C8100AC4814 /* SessionReplay+objc.swift */, 61054E3B2A6EE10A00AAA894 /* Feature */, 61054E482A6EE10A00AAA894 /* Processor */, 61054E0D2A6EE10A00AAA894 /* Recorder */, @@ -4794,8 +4797,8 @@ D2B3F051282E826A00C2B5EE /* DDHTTPHeadersWriter+apiTests.m */, A79B0F63292BD074008742B3 /* DDB3HTTPHeadersWriter+apiTests.m */, A728ADAD2934EB0300397996 /* DDW3CHTTPHeadersWriter+apiTests.m */, - D2A434AB2A8E416F0028E329 /* DDSessionReplay+apiTests.m */, 3C1890132ABDE99200CE9E73 /* DDURLSessionInstrumentationTests+apiTests.m */, + A795069D2B974CAA00AC4814 /* DDSessionReplay+apiTests.m */, ); path = ObjcAPITests; sourceTree = ""; @@ -6916,7 +6919,7 @@ LastSwiftMigration = 1430; }; 6133D1F62A6EDB7700384BEF = { - LastSwiftMigration = 1430; + LastSwiftMigration = 1520; TestTargetID = 61441C0124616DE9003D8BB8; }; 61441C0124616DE9003D8BB8 = { @@ -7621,10 +7624,10 @@ 618C365F248E85B400520CDE /* DateFormattingTests.swift in Sources */, 61133C5A2423990D00786299 /* FileTests.swift in Sources */, 61133C6B2423990D00786299 /* LogMatcher.swift in Sources */, - D2A434AC2A8E416F0028E329 /* DDSessionReplay+apiTests.m in Sources */, 61DB33B225DEDFC200F7EA71 /* CustomObjcViewController.m in Sources */, D2EFA875286E011900F1FAA6 /* DatadogContextProviderTests.swift in Sources */, 61363D9F24D99BAA0084CD6F /* DDErrorTests.swift in Sources */, + A727C4BB2BADB3AB00707DFD /* DDSessionReplay+apiTests.m in Sources */, 613F9C182BAC3527007C7606 /* DatadogCore+FeatureDataStoreTests.swift in Sources */, 6128F5842BA8CAAB00D35B08 /* DataStoreFileWriterTests.swift in Sources */, D22743DA29DEB8B4001A7EF9 /* VitalMemoryReaderTests.swift in Sources */, @@ -7851,6 +7854,7 @@ 61054E8A2A6EE10A00AAA894 /* WindowViewTreeSnapshotProducer.swift in Sources */, 61054E7A2A6EE10A00AAA894 /* UIImageViewRecorder.swift in Sources */, A7B932FC2B1F6A0A00AE6477 /* SRDataModels.swift in Sources */, + A795069C2B974C8200AC4814 /* SessionReplay+objc.swift in Sources */, 61054E752A6EE10A00AAA894 /* ViewTreeSnapshot.swift in Sources */, 61054EA02A6EE10B00AAA894 /* Colors.swift in Sources */, 61054E7F2A6EE10A00AAA894 /* UISliderRecorder.swift in Sources */, diff --git a/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift b/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift index 630db16e87..9480907419 100644 --- a/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift +++ b/DatadogCore/Tests/DatadogObjc/DDSessionReplayTests.swift @@ -10,7 +10,6 @@ import XCTest import TestUtilities import DatadogInternal -@testable import DatadogObjc @testable import DatadogSessionReplay class DDSessionReplayTests: XCTestCase { diff --git a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m index 19c187969d..4b69f5fa14 100644 --- a/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m +++ b/DatadogCore/Tests/DatadogObjc/ObjcAPITests/DDSessionReplay+apiTests.m @@ -6,9 +6,7 @@ #import -#if TARGET_OS_IOS - -@import DatadogObjc; +@import DatadogSessionReplay; @interface DDSessionReplay_apiTests : XCTestCase @end @@ -24,5 +22,3 @@ - (void)testConfiguration { } @end - -#endif diff --git a/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift b/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift index 65c2c1f535..94402dc243 100644 --- a/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift +++ b/DatadogObjc/Sources/SessionReplay/SessionReplay+objc.swift @@ -10,6 +10,7 @@ import DatadogSessionReplay /// An entry point to Datadog Session Replay feature. @objc +@available(*, deprecated, message: "Obj-C API for Session Replay was moved to the DatadogSessionReplay package.") public final class DDSessionReplay: NSObject { override private init() { } diff --git a/DatadogSessionReplay/Sources/SessionReplay+objc.swift b/DatadogSessionReplay/Sources/SessionReplay+objc.swift new file mode 100644 index 0000000000..2a106f811e --- /dev/null +++ b/DatadogSessionReplay/Sources/SessionReplay+objc.swift @@ -0,0 +1,108 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import Foundation +#if os(iOS) + +/// An entry point to Datadog Session Replay feature. +@objc +public final class DDSessionReplay: NSObject { + override private init() { } + + /// Enables Datadog Session Replay feature. + /// + /// Recording will start automatically after enabling Session Replay. + /// + /// Note: Session Replay requires the RUM feature to be enabled. + /// + /// - Parameters: + /// - configuration: Configuration of the feature. + @objc + public static func enable(with configuration: DDSessionReplayConfiguration) { + SessionReplay.enable(with: configuration._swift) + } +} + +/// Session Replay feature configuration. +@objc +public final class DDSessionReplayConfiguration: 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. + /// + /// It must be a number between 0.0 and 100.0, where 0 means no replays will be recorded + /// and 100 means all RUM sessions will contain replay. + /// + /// Note: This sample rate is applied in addition to the RUM sample rate. For example, if RUM uses a sample rate of 80% + /// and Session Replay uses a sample rate of 20%, it means that out of all user sessions, 80% will be included in RUM, + /// and within those sessions, only 20% will have replays. + @objc public var replaySampleRate: Float { + set { _swift.replaySampleRate = newValue } + get { _swift.replaySampleRate } + } + + /// Defines the way sensitive content (e.g. text) should be masked. + /// + /// Default: `.mask`. + @objc public var defaultPrivacyLevel: DDSessionReplayConfigurationPrivacyLevel { + set { _swift.defaultPrivacyLevel = newValue._swift } + get { .init(_swift.defaultPrivacyLevel) } + } + + /// Custom server url for sending replay data. + /// + /// Default: `nil`. + @objc public var customEndpoint: URL? { + set { _swift.customEndpoint = newValue } + get { _swift.customEndpoint } + } + + /// Creates Session Replay configuration. + /// + /// - Parameters: + /// - replaySampleRate: The sampling rate for Session Replay. It is applied in addition to the RUM session sample rate. + @objc + public required init( + replaySampleRate: Float + ) { + _swift = SessionReplay.Configuration( + replaySampleRate: replaySampleRate + ) + super.init() + } +} + +/// Available privacy levels for content masking. +@objc +public enum DDSessionReplayConfigurationPrivacyLevel: Int { + /// Record all content. + case allow + + /// Mask all content. + case mask + + /// Mask input elements, but record all other content. + case maskUserInput + + internal var _swift: SessionReplay.Configuration.PrivacyLevel { + switch self { + case .allow: return .allow + case .mask: return .mask + case .maskUserInput: return .maskUserInput + default: return .mask + } + } + + internal init(_ swift: SessionReplay.Configuration.PrivacyLevel) { + switch swift { + case .allow: self = .allow + case .mask: self = .mask + case .maskUserInput: self = .maskUserInput + } + } +} + +#endif