diff --git a/ios/PublicApi/Measure.swiftinterface b/ios/PublicApi/Measure.swiftinterface index 810b2a898..376b34645 100644 --- a/ios/PublicApi/Measure.swiftinterface +++ b/ios/PublicApi/Measure.swiftinterface @@ -1,7 +1,7 @@ // swift-interface-format-version: 1.0 -// swift-compiler-version: Apple Swift version 6.2 effective-5.10 (swiftlang-6.2.0.19.9 clang-1700.3.19.1) -// swift-module-flags: -target arm64-apple-ios12-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -module-name Measure -// swift-module-flags-ignorable: -no-verify-emitted-module-interface -formal-cxx-interoperability-mode=off -interface-compiler-version 6.2 +// swift-compiler-version: Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.10 clang-1600.0.30.1) +// swift-module-flags: -target arm64-apple-ios12-simulator -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -module-name Measure +// swift-module-flags-ignorable: -no-verify-emitted-module-interface import AVKit import CoreData import CoreMotion @@ -37,45 +37,37 @@ extension Measure.AttributeValue : Swift.Codable { public init(from decoder: any Swift.Decoder) throws } public typealias SessionObCoreDataClassSet = Foundation.NSSet -@_inheritsConvenienceInitializers @objc(SessionOb) nonisolated public class SessionOb : CoreData.NSManagedObject { - #if compiler(>=5.3) && $NonescapableTypes - @objc override nonisolated dynamic public init(entity: CoreData.NSEntityDescription, insertInto context: CoreData.NSManagedObjectContext?) - #endif +@_inheritsConvenienceInitializers @objc(SessionOb) public class SessionOb : CoreData.NSManagedObject { + @objc override dynamic public init(entity: CoreData.NSEntityDescription, insertInto context: CoreData.NSManagedObjectContext?) @objc deinit } public typealias SessionObCoreDataPropertiesSet = Foundation.NSSet extension Measure.SessionOb { @nonobjc public class func fetchRequest() -> CoreData.NSFetchRequest - @objc @NSManaged nonisolated dynamic public var crashed: Swift.Bool { + @objc @NSManaged dynamic public var crashed: Swift.Bool { @objc get @objc set } - @objc @NSManaged nonisolated dynamic public var createdAt: Swift.Int64 { + @objc @NSManaged dynamic public var createdAt: Swift.Int64 { @objc get @objc set } - @objc @NSManaged nonisolated dynamic public var needsReporting: Swift.Bool { + @objc @NSManaged dynamic public var needsReporting: Swift.Bool { @objc get @objc set } - @objc @NSManaged nonisolated dynamic public var pid: Swift.Int32 { + @objc @NSManaged dynamic public var pid: Swift.Int32 { @objc get @objc set } - #if compiler(>=5.3) && $NonescapableTypes - @objc @NSManaged nonisolated dynamic public var sessionId: Swift.String? { + @objc @NSManaged dynamic public var sessionId: Swift.String? { @objc get @objc set } - #endif } @objc final public class BaseMeasureConfig : ObjectiveC.NSObject, Swift.Codable { - #if compiler(>=5.3) && $NonescapableTypes public init(enableLogging: Swift.Bool? = nil, autoStart: Swift.Bool? = nil, requestHeadersProvider: (any Measure.MsrRequestHeadersProvider)? = nil, maxDiskUsageInMb: Swift.Int? = nil, enableFullCollectionMode: Swift.Bool? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc convenience public init(enableLogging: Swift.Bool, autoStart: Swift.Bool, requestHeadersProvider: (any Measure.MsrRequestHeadersProvider)?, maxDiskUsageInMb: Foundation.NSNumber?, enableFullCollectionMode: Swift.Bool) - #endif required public init(from decoder: any Swift.Decoder) throws final public func encode(to encoder: any Swift.Encoder) throws @objc deinit @@ -83,9 +75,7 @@ extension Measure.SessionOb { public protocol Span { var traceId: Swift.String { get } var spanId: Swift.String { get } - #if compiler(>=5.3) && $NonescapableTypes var parentId: Swift.String? { get } - #endif var isSampled: Swift.Bool { get } @discardableResult func setStatus(_ status: Measure.SpanStatus) -> any Measure.Span @@ -118,9 +108,7 @@ public enum SpanStatus : Swift.Int64, Swift.Codable { case unset case ok case error - #if compiler(>=5.3) && $NonescapableTypes public init?(rawValue: Swift.Int64) - #endif public typealias RawValue = Swift.Int64 public var rawValue: Swift.Int64 { get @@ -139,9 +127,7 @@ public protocol SpanBuilder { final public let galleryButton: Swift.String final public let exitScreenshotMode: Swift.String public init(reportBugTitle: Swift.String, descriptionPlaceholder: Swift.String, sendButton: Swift.String, screenshotButton: Swift.String, galleryButton: Swift.String, exitScreenshotMode: Swift.String) - #if compiler(>=5.3) && $NonescapableTypes public func update(reportBugTitle: Swift.String? = nil, descriptionPlaceholder: Swift.String? = nil, sendButton: Swift.String? = nil, screenshotButton: Swift.String? = nil, galleryButton: Swift.String? = nil, exitScreenshotMode: Swift.String? = nil) -> Measure.MsrText - #endif @objc deinit } @objc final public class ClientInfo : ObjectiveC.NSObject, Swift.Codable { @@ -155,9 +141,7 @@ public protocol SpanBuilder { final public let button: UIKit.UIFont final public let placeholder: UIKit.UIFont public init(title: UIKit.UIFont, button: UIKit.UIFont, placeholder: UIKit.UIFont) - #if compiler(>=5.3) && $NonescapableTypes public func update(title: UIKit.UIFont? = nil, button: UIKit.UIFont? = nil, placeholder: UIKit.UIFont? = nil) -> Measure.MsrFonts - #endif @objc deinit } @_inheritsConvenienceInitializers @objc(LifecycleManagerInternal) public class LifecycleManagerInternal : ObjectiveC.NSObject { @@ -171,9 +155,7 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl case allText case allTextExceptClickable case sensitiveFieldsOnly - #if compiler(>=5.3) && $NonescapableTypes public init?(rawValue: Swift.String) - #endif public typealias AllCases = [Measure.ScreenshotMaskLevel] public typealias RawValue = Swift.String nonisolated public static var allCases: [Measure.ScreenshotMaskLevel] { @@ -188,9 +170,7 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl case allText case allTextExceptClickable case sensitiveFieldsOnly - #if compiler(>=5.3) && $NonescapableTypes public init?(rawValue: Swift.Int) - #endif public typealias RawValue = Swift.Int public var rawValue: Swift.Int { get @@ -245,9 +225,7 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl get } public init(darkBackground: UIKit.UIColor, lightBackground: UIKit.UIColor, darkButtonBackground: UIKit.UIColor, lightButtonBackground: UIKit.UIColor, darkText: UIKit.UIColor, lightText: UIKit.UIColor, darkPlaceholder: UIKit.UIColor, lightPlaceholder: UIKit.UIColor, darkFloatingButtonBackground: UIKit.UIColor, lightFloatingButtonBackground: UIKit.UIColor, darkFloatingButtonIcon: UIKit.UIColor, lightFloatingButtonIcon: UIKit.UIColor, darkfloatingExitButtonText: UIKit.UIColor, lightfloatingExitButtonText: UIKit.UIColor, badgeColor: UIKit.UIColor, badgeTextColor: UIKit.UIColor, isDarkMode: Swift.Bool) - #if compiler(>=5.3) && $NonescapableTypes public func update(darkBackground: UIKit.UIColor? = nil, lightBackground: UIKit.UIColor? = nil, darkButtonBackground: UIKit.UIColor? = nil, lightButtonBackground: UIKit.UIColor? = nil, darkText: UIKit.UIColor? = nil, lightText: UIKit.UIColor? = nil, darkPlaceholder: UIKit.UIColor? = nil, lightPlaceholder: UIKit.UIColor? = nil, darkFloatingButtonBackground: UIKit.UIColor? = nil, lightFloatingButtonBackground: UIKit.UIColor? = nil, darkFloatingButtonIcon: UIKit.UIColor? = nil, lightFloatingButtonIcon: UIKit.UIColor? = nil, darkfloatingExitButtonText: UIKit.UIColor? = nil, lightfloatingExitButtonText: UIKit.UIColor? = nil, badgeColor: UIKit.UIColor? = nil, badgeTextColor: UIKit.UIColor? = nil, isDarkMode: Swift.Bool? = nil) -> Measure.MsrColors - #endif @objc deinit } @objc public protocol MsrRequestHeadersProvider : ObjectiveC.NSObjectProtocol { @@ -264,9 +242,7 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl case initWithCoder = 7 case loadView = 8 case vcDeinit = 9 - #if compiler(>=5.3) && $NonescapableTypes public init?(rawValue: Swift.Int) - #endif public typealias RawValue = Swift.Int public var rawValue: Swift.Int { get @@ -279,9 +255,7 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl public var path: Swift.String? public var size: Swift.Int64 public var id: Swift.String - #if compiler(>=5.3) && $NonescapableTypes public init(name: Swift.String, type: Measure.AttachmentType, size: Swift.Int64, id: Swift.String, bytes: Foundation.Data? = nil, path: Swift.String? = nil) - #endif @objc deinit public func encode(to encoder: any Swift.Encoder) throws required public init(from decoder: any Swift.Decoder) throws @@ -289,16 +263,12 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl @objc public class MsrDimensions : ObjectiveC.NSObject { final public let topPadding: CoreFoundation.CGFloat public init(topPadding: CoreFoundation.CGFloat) - #if compiler(>=5.3) && $NonescapableTypes public func update(topPadding: CoreFoundation.CGFloat? = nil) -> Measure.MsrDimensions - #endif @objc deinit } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6.0, *) @_Concurrency.MainActor @preconcurrency public struct MsrMoniterView : SwiftUICore.View where Content : SwiftUICore.View { - #if compiler(>=5.3) && $NonescapableTypes @_Concurrency.MainActor @preconcurrency public init(_ viewName: Swift.String? = nil, content: @escaping () -> Content) - #endif @_Concurrency.MainActor @preconcurrency public var body: some SwiftUICore.View { get } @@ -307,87 +277,43 @@ public enum ScreenshotMaskLevel : Swift.String, Swift.Codable, Swift.CaseIterabl } @available(iOS 13, macOS 10.15, tvOS 13, watchOS 6.0, *) extension SwiftUICore.View { - #if compiler(>=5.3) && $NonescapableTypes @_Concurrency.MainActor @preconcurrency public func moniterWithMsr(_ viewName: Swift.String? = nil) -> some SwiftUICore.View - #endif } @_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc final public class Measure : ObjectiveC.NSObject { @objc deinit } extension Measure.Measure { - #if compiler(>=5.3) && $NonescapableTypes @objc public static func initialize(with client: Measure.ClientInfo, config: Measure.BaseMeasureConfig? = nil) - #endif @objc public static func start() @objc public static func stop() - #if compiler(>=5.3) && $NonescapableTypes @objc public static func getSessionId() -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func internalTrackEvent(data: inout [Swift.String : Any?], type: Swift.String, timestamp: Swift.Int64, attributes: [Swift.String : Any?], userDefinedAttrs: [Swift.String : Measure.AttributeValue], userTriggered: Swift.Bool, sessionId: Swift.String?, threadName: Swift.String?, attachments: [Measure.MsrAttachment]) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func internalTrackSpan(name: Swift.String, traceId: Swift.String, spanId: Swift.String, parentId: Swift.String?, startTime: Swift.Int64, endTime: Swift.Int64, duration: Swift.Int64, status: Swift.Int64, attributes: [Swift.String : Any?], userDefinedAttrs: [Swift.String : Measure.AttributeValue], checkpoints: [Swift.String : Swift.Int64], hasEnded: Swift.Bool, isSampled: Swift.Bool) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func trackEvent(name: Swift.String, attributes: [Swift.String : Measure.AttributeValue], timestamp: Swift.Int64? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func trackEvent(_ name: Swift.String, attributes: [Swift.String : Any], timestamp: Foundation.NSNumber? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func trackScreenView(_ screenName: Swift.String, attributes: [Swift.String : Measure.AttributeValue]?) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func trackScreenView(_ screenName: Swift.String, attributes: [Swift.String : Any]?) - #endif @objc public static func setUserId(_ userId: Swift.String) @objc public static func clearUserId() @objc public static func getCurrentTime() -> Swift.Int64 public static func startSpan(name: Swift.String) -> any Measure.Span public static func startSpan(name: Swift.String, timestamp: Swift.Int64) -> any Measure.Span - #if compiler(>=5.3) && $NonescapableTypes public static func createSpanBuilder(name: Swift.String) -> (any Measure.SpanBuilder)? - #endif public static func getTraceParentHeaderValue(span: any Measure.Span) -> Swift.String public static func getTraceParentHeaderKey() -> Swift.String - #if compiler(>=5.3) && $NonescapableTypes public static func launchBugReport(takeScreenshot: Swift.Bool = true, bugReportConfig: Measure.BugReportConfig = .default, attributes: [Swift.String : Measure.AttributeValue]? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func launchBugReport(takeScreenshot: Swift.Bool = true, bugReportConfig: Measure.BugReportConfig = .default, attributes: [Swift.String : Any]? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func onShake(_ handler: (() -> Swift.Void)?) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func trackBugReport(description: Swift.String, attachments: [Measure.MsrAttachment] = [], attributes: [Swift.String : Measure.AttributeValue]? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func trackBugReport(description: Swift.String, attachments: [Measure.MsrAttachment] = [], attributes: [Swift.String : Any]? = nil) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func captureScreenshot(for viewController: UIKit.UIViewController, completion: @escaping (Measure.MsrAttachment?) -> Swift.Void) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func captureLayoutSnapshot(for viewController: UIKit.UIViewController, completion: @escaping (Measure.MsrAttachment?) -> Swift.Void) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func trackError(_ error: any Swift.Error, attributes: [Swift.String : Measure.AttributeValue]? = nil, collectStackTraces: Swift.Bool = false) - #endif - #if compiler(>=5.3) && $NonescapableTypes @objc public static func trackError(_ error: Foundation.NSError, attributes: [Swift.String : Any]? = nil, collectStackTraces: Swift.Bool = false) - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func internalGetAttachmentDirectory() -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func internalGetDynamicConfigPath() -> Swift.String? - #endif - #if compiler(>=5.3) && $NonescapableTypes public static func trackHttpEvent(url: Swift.String, method: Swift.String, startTime: Swift.UInt64, endTime: Swift.UInt64, client: Swift.String = "unknown", statusCode: Swift.Int? = nil, error: (any Swift.Error)? = nil, requestHeaders: [Swift.String : Swift.String]? = nil, responseHeaders: [Swift.String : Swift.String]? = nil, requestBody: Swift.String? = nil, responseBody: Swift.String? = nil) - #endif } @_inheritsConvenienceInitializers @objc public class MSRNetworkInterceptor : ObjectiveC.NSObject { @objc(enableOn:) public static func enable(on sessionConfiguration: Foundation.URLSessionConfiguration) @@ -398,9 +324,7 @@ public enum AttachmentType : Swift.String, Swift.Codable { case screenshot case layoutSnapshot case layoutSnapshotJson - #if compiler(>=5.3) && $NonescapableTypes public init?(rawValue: Swift.String) - #endif public typealias RawValue = Swift.String public var rawValue: Swift.String { get @@ -409,12 +333,8 @@ public enum AttachmentType : Swift.String, Swift.Codable { @objc @_inheritsConvenienceInitializers @_Concurrency.MainActor @preconcurrency open class MsrViewController : UIKit.UIViewController { @_Concurrency.MainActor @preconcurrency @objc override dynamic open func loadView() @objc deinit - #if compiler(>=5.3) && $NonescapableTypes @_Concurrency.MainActor @preconcurrency @objc override dynamic public init(nibName nibNameOrNil: Swift.String?, bundle nibBundleOrNil: Foundation.Bundle?) - #endif - #if compiler(>=5.3) && $NonescapableTypes @_Concurrency.MainActor @preconcurrency @objc required dynamic public init?(coder: Foundation.NSCoder) - #endif } extension Measure.SpanStatus : Swift.Equatable {} extension Measure.SpanStatus : Swift.Hashable {} diff --git a/ios/Sources/MeasureSDK/Swift/Config/ConfigLoader.swift b/ios/Sources/MeasureSDK/Swift/Config/ConfigLoader.swift index b20c7bc47..155da414f 100644 --- a/ios/Sources/MeasureSDK/Swift/Config/ConfigLoader.swift +++ b/ios/Sources/MeasureSDK/Swift/Config/ConfigLoader.swift @@ -46,7 +46,7 @@ struct BaseConfigLoader: ConfigLoader { func loadDynamicConfig(onLoaded: @escaping (DynamicConfig) -> Void) { let cachedConfig = loadConfigFromDisk() - onLoaded(cachedConfig ?? BaseDynamicConfig.default()) + onLoaded(cachedConfig ?? BaseDynamicConfig()) refreshConfigFromServer() } diff --git a/ios/Sources/MeasureSDK/Swift/Config/ConfigProvider.swift b/ios/Sources/MeasureSDK/Swift/Config/ConfigProvider.swift index a10a7f429..70fa2894b 100644 --- a/ios/Sources/MeasureSDK/Swift/Config/ConfigProvider.swift +++ b/ios/Sources/MeasureSDK/Swift/Config/ConfigProvider.swift @@ -42,7 +42,7 @@ final class BaseConfigProvider: ConfigProvider { } private let defaultConfig: Config private let lockQueue = DispatchQueue(label: "sh.measure.config-provider") - private var dynamicConfig: DynamicConfig = BaseDynamicConfig.default() + private var dynamicConfig: DynamicConfig = BaseDynamicConfig() private var httpPatternState = HttpPatternState( disableEventPatterns: [], blocklistPatterns: [], @@ -132,6 +132,7 @@ final class BaseConfigProvider: ConfigProvider { var anrTakeScreenshot: Bool { dynamicConfig.anrTakeScreenshot } var launchSamplingRate: Float { dynamicConfig.launchSamplingRate } var gestureClickTakeSnapshot: Bool { dynamicConfig.gestureClickTakeSnapshot } + var httpSamplingRate: Float { dynamicConfig.httpSamplingRate } var httpDisableEventForUrls: [String] { dynamicConfig.httpDisableEventForUrls } var httpTrackRequestForUrls: [String] { dynamicConfig.httpTrackRequestForUrls } var httpTrackResponseForUrls: [String] { dynamicConfig.httpTrackResponseForUrls } diff --git a/ios/Sources/MeasureSDK/Swift/Config/DefaultConfig.swift b/ios/Sources/MeasureSDK/Swift/Config/DefaultConfig.swift index dd15cd24f..ce5b2cf33 100644 --- a/ios/Sources/MeasureSDK/Swift/Config/DefaultConfig.swift +++ b/ios/Sources/MeasureSDK/Swift/Config/DefaultConfig.swift @@ -20,4 +20,30 @@ struct DefaultConfig { static let journeyEvents: [EventType] = [.lifecycleSwiftUI, .lifecycleViewController, .screenView] + + static let maxEventsInBatch: Number = 10_000 + static let crashTimelineDurationSeconds: Number = 300 + static let anrTimelineDurationSeconds: Number = 300 + static let bugReportTimelineDurationSeconds: Number = 300 + static let traceSamplingRate: Float = 0.01 + static let journeySamplingRate: Float = 0.01 + static let screenshotMaskLevel: ScreenshotMaskLevel = .allTextAndMedia + static let cpuUsageInterval: Number = 5 + static let memoryUsageInterval: Number = 5 + static let crashTakeScreenshot: Bool = true + static let anrTakeScreenshot: Bool = true + static let launchSamplingRate: Float = 0.01 + static let gestureClickTakeSnapshot: Bool = true + static let httpSamplingRate: Float = 0.01 + static let httpDisableEventForUrls: [String] = [] + static let httpTrackRequestForUrls: [String] = [] + static let httpTrackResponseForUrls: [String] = [] + static let httpBlockedHeaders: [String] = [ + "Authorization", + "Cookie", + "Set-Cookie", + "Proxy-Authorization", + "WWW-Authenticate", + "X-Api-Key", + ] } diff --git a/ios/Sources/MeasureSDK/Swift/Config/DynamicConfig.swift b/ios/Sources/MeasureSDK/Swift/Config/DynamicConfig.swift index 687a0c708..2b40bd8b4 100644 --- a/ios/Sources/MeasureSDK/Swift/Config/DynamicConfig.swift +++ b/ios/Sources/MeasureSDK/Swift/Config/DynamicConfig.swift @@ -59,6 +59,10 @@ protocol DynamicConfig { /// Defaults to true. var gestureClickTakeSnapshot: Bool { get } + /// Sampling rate for htto events. + /// Defaults to 0.01%. + var httpSamplingRate: Float { get } + /// URLs for which HTTP events should be disabled. var httpDisableEventForUrls: [String] { get } @@ -86,36 +90,72 @@ struct BaseDynamicConfig: DynamicConfig, Codable { let anrTakeScreenshot: Bool let launchSamplingRate: Float let gestureClickTakeSnapshot: Bool + let httpSamplingRate: Float let httpDisableEventForUrls: [String] let httpTrackRequestForUrls: [String] let httpTrackResponseForUrls: [String] let httpBlockedHeaders: [String] - - static func `default`() -> BaseDynamicConfig { - BaseDynamicConfig(maxEventsInBatch: 10_000, - crashTimelineDurationSeconds: 300, - anrTimelineDurationSeconds: 300, - bugReportTimelineDurationSeconds: 300, - traceSamplingRate: 0.01, - journeySamplingRate: 0.01, - screenshotMaskLevel: .allTextAndMedia, - cpuUsageInterval: 5, - memoryUsageInterval: 5, - crashTakeScreenshot: true, - anrTakeScreenshot: true, - launchSamplingRate: 0.01, - gestureClickTakeSnapshot: true, - httpDisableEventForUrls: [], - httpTrackRequestForUrls: [], - httpTrackResponseForUrls: [], - httpBlockedHeaders: [ - "Authorization", - "Cookie", - "Set-Cookie", - "Proxy-Authorization", - "WWW-Authenticate", - "X-Api-Key", - ]) + + init(maxEventsInBatch: Number = DefaultConfig.maxEventsInBatch, + crashTimelineDurationSeconds: Number = DefaultConfig.crashTimelineDurationSeconds, + anrTimelineDurationSeconds: Number = DefaultConfig.anrTimelineDurationSeconds, + bugReportTimelineDurationSeconds: Number = DefaultConfig.bugReportTimelineDurationSeconds, + traceSamplingRate: Float = DefaultConfig.traceSamplingRate, + journeySamplingRate: Float = DefaultConfig.journeySamplingRate, + screenshotMaskLevel: ScreenshotMaskLevel = DefaultConfig.screenshotMaskLevel, + cpuUsageInterval: Number = DefaultConfig.cpuUsageInterval, + memoryUsageInterval: Number = DefaultConfig.memoryUsageInterval, + crashTakeScreenshot: Bool = DefaultConfig.crashTakeScreenshot, + anrTakeScreenshot: Bool = DefaultConfig.anrTakeScreenshot, + launchSamplingRate: Float = DefaultConfig.launchSamplingRate, + gestureClickTakeSnapshot: Bool = DefaultConfig.gestureClickTakeSnapshot, + httpSamplingRate: Float = DefaultConfig.httpSamplingRate, + httpDisableEventForUrls: [String] = DefaultConfig.httpDisableEventForUrls, + httpTrackRequestForUrls: [String] = DefaultConfig.httpTrackRequestForUrls, + httpTrackResponseForUrls: [String] = DefaultConfig.httpTrackResponseForUrls, + httpBlockedHeaders: [String] = DefaultConfig.httpBlockedHeaders + ) { + self.maxEventsInBatch = maxEventsInBatch + self.crashTimelineDurationSeconds = crashTimelineDurationSeconds + self.anrTimelineDurationSeconds = anrTimelineDurationSeconds + self.bugReportTimelineDurationSeconds = bugReportTimelineDurationSeconds + self.traceSamplingRate = traceSamplingRate + self.journeySamplingRate = journeySamplingRate + self.screenshotMaskLevel = screenshotMaskLevel + self.cpuUsageInterval = cpuUsageInterval + self.memoryUsageInterval = memoryUsageInterval + self.crashTakeScreenshot = crashTakeScreenshot + self.anrTakeScreenshot = anrTakeScreenshot + self.launchSamplingRate = launchSamplingRate + self.gestureClickTakeSnapshot = gestureClickTakeSnapshot + self.httpSamplingRate = httpSamplingRate + self.httpDisableEventForUrls = httpDisableEventForUrls + self.httpTrackRequestForUrls = httpTrackRequestForUrls + self.httpTrackResponseForUrls = httpTrackResponseForUrls + self.httpBlockedHeaders = httpBlockedHeaders + } + + init(from decoder: Decoder) throws { + let c = try decoder.container(keyedBy: CodingKeys.self) + + maxEventsInBatch = try c.decodeIfPresent(Number.self, forKey: .maxEventsInBatch) ?? DefaultConfig.maxEventsInBatch + crashTimelineDurationSeconds = try c.decodeIfPresent(Number.self, forKey: .crashTimelineDurationSeconds) ?? DefaultConfig.crashTimelineDurationSeconds + anrTimelineDurationSeconds = try c.decodeIfPresent(Number.self, forKey: .anrTimelineDurationSeconds) ?? DefaultConfig.anrTimelineDurationSeconds + bugReportTimelineDurationSeconds = try c.decodeIfPresent(Number.self, forKey: .bugReportTimelineDurationSeconds) ?? DefaultConfig.bugReportTimelineDurationSeconds + traceSamplingRate = try c.decodeIfPresent(Float.self, forKey: .traceSamplingRate) ?? DefaultConfig.traceSamplingRate + journeySamplingRate = try c.decodeIfPresent(Float.self, forKey: .journeySamplingRate) ?? DefaultConfig.journeySamplingRate + screenshotMaskLevel = try c.decodeIfPresent(ScreenshotMaskLevel.self, forKey: .screenshotMaskLevel) ?? DefaultConfig.screenshotMaskLevel + cpuUsageInterval = try c.decodeIfPresent(Number.self, forKey: .cpuUsageInterval) ?? DefaultConfig.cpuUsageInterval + memoryUsageInterval = try c.decodeIfPresent(Number.self, forKey: .memoryUsageInterval) ?? DefaultConfig.memoryUsageInterval + crashTakeScreenshot = try c.decodeIfPresent(Bool.self, forKey: .crashTakeScreenshot) ?? DefaultConfig.crashTakeScreenshot + anrTakeScreenshot = try c.decodeIfPresent(Bool.self, forKey: .anrTakeScreenshot) ?? DefaultConfig.anrTakeScreenshot + launchSamplingRate = try c.decodeIfPresent(Float.self, forKey: .launchSamplingRate) ?? DefaultConfig.launchSamplingRate + gestureClickTakeSnapshot = try c.decodeIfPresent(Bool.self, forKey: .gestureClickTakeSnapshot) ?? DefaultConfig.gestureClickTakeSnapshot + httpSamplingRate = try c.decodeIfPresent(Float.self, forKey: .httpSamplingRate) ?? DefaultConfig.httpSamplingRate + httpDisableEventForUrls = try c.decodeIfPresent([String].self, forKey: .httpDisableEventForUrls) ?? DefaultConfig.httpDisableEventForUrls + httpTrackRequestForUrls = try c.decodeIfPresent([String].self, forKey: .httpTrackRequestForUrls) ?? DefaultConfig.httpTrackRequestForUrls + httpTrackResponseForUrls = try c.decodeIfPresent([String].self, forKey: .httpTrackResponseForUrls) ?? DefaultConfig.httpTrackResponseForUrls + httpBlockedHeaders = try c.decodeIfPresent([String].self, forKey: .httpBlockedHeaders) ?? DefaultConfig.httpBlockedHeaders } private enum CodingKeys: String, CodingKey { @@ -132,6 +172,7 @@ struct BaseDynamicConfig: DynamicConfig, Codable { case anrTakeScreenshot = "anr_take_screenshot" case launchSamplingRate = "launch_sampling_rate" case gestureClickTakeSnapshot = "gesture_click_take_snapshot" + case httpSamplingRate = "http_sampling_rate" case httpDisableEventForUrls = "http_disable_event_for_urls" case httpTrackRequestForUrls = "http_track_request_for_urls" case httpTrackResponseForUrls = "http_track_response_for_urls" diff --git a/ios/Sources/MeasureSDK/Swift/Events/InternalSignalCollector.swift b/ios/Sources/MeasureSDK/Swift/Events/InternalSignalCollector.swift index 2b9a2d55c..ee0730d1f 100644 --- a/ios/Sources/MeasureSDK/Swift/Events/InternalSignalCollector.swift +++ b/ios/Sources/MeasureSDK/Swift/Events/InternalSignalCollector.swift @@ -173,7 +173,7 @@ final class BaseInternalSignalCollector: InternalSignalCollector { attachments: nil, userDefinedAttributes: serializedUserDefinedAttributes, threadName: threadName, - needsReporting: false + needsReporting: signalSampler.shouldSampleHttpEvent() ) case EventType.bugReport.rawValue: diff --git a/ios/Sources/MeasureSDK/Swift/Events/SignalSampler.swift b/ios/Sources/MeasureSDK/Swift/Events/SignalSampler.swift index 00a0bda5f..b6dbb9979 100644 --- a/ios/Sources/MeasureSDK/Swift/Events/SignalSampler.swift +++ b/ios/Sources/MeasureSDK/Swift/Events/SignalSampler.swift @@ -11,6 +11,7 @@ protocol SignalSampler { func shouldTrackLaunchEvents() -> Bool func shouldSampleTrace(_ traceId: String) -> Bool func shouldTrackJourneyForSession(sessionId: String) -> Bool + func shouldSampleHttpEvent() -> Bool } final class BaseSignalSampler: SignalSampler { @@ -69,6 +70,14 @@ final class BaseSignalSampler: SignalSampler { return stableSamplingValue(sessionId: sessionId) < samplingRate } + + func shouldSampleHttpEvent() -> Bool { + if configProvider.enableFullCollectionMode { + return true + } + return shouldTrack(configProvider.httpSamplingRate / 100) + } + /// Generates a stable sampling value in [0, 1] from a session ID. /// diff --git a/ios/Sources/MeasureSDK/Swift/Events/UserTriggeredEventCollector.swift b/ios/Sources/MeasureSDK/Swift/Events/UserTriggeredEventCollector.swift index 5ce646ad6..099488b3e 100644 --- a/ios/Sources/MeasureSDK/Swift/Events/UserTriggeredEventCollector.swift +++ b/ios/Sources/MeasureSDK/Swift/Events/UserTriggeredEventCollector.swift @@ -178,7 +178,7 @@ final class BaseUserTriggeredEventCollector: UserTriggeredEventCollector { responseBody: shouldTrackResponseHttpBody ? responseBody : nil, client: client) - track(data, type: .http, needsReporting: false) + track(data, type: .http, needsReporting: signalSampler.shouldSampleHttpEvent()) } private func track(_ data: Codable, type: EventType, userDefinedAttributes: String? = nil, needsReporting: Bool?) { @@ -190,7 +190,7 @@ final class BaseUserTriggeredEventCollector: UserTriggeredEventCollector { attachments: nil, userDefinedAttributes: userDefinedAttributes, threadName: nil, - needsReporting: needsReporting) + needsReporting: signalSampler.shouldSampleHttpEvent()) } private func getContentType(headers: [String: String]?) -> String? { diff --git a/ios/Sources/MeasureSDK/Swift/Http/HttpEventCollector.swift b/ios/Sources/MeasureSDK/Swift/Http/HttpEventCollector.swift index 0c1914497..68e2a7df8 100644 --- a/ios/Sources/MeasureSDK/Swift/Http/HttpEventCollector.swift +++ b/ios/Sources/MeasureSDK/Swift/Http/HttpEventCollector.swift @@ -20,6 +20,7 @@ final class BaseHttpEventCollector: HttpEventCollector { private let httpInterceptorCallbacks: HttpInterceptorCallbacks private let client: Client private let configProvider: ConfigProvider + private let signalSampler: SignalSampler private let httpEventValidator: HttpEventValidator private var isEnabled = AtomicBool(false) @@ -30,6 +31,7 @@ final class BaseHttpEventCollector: HttpEventCollector { httpInterceptorCallbacks: HttpInterceptorCallbacks, client: Client, configProvider: ConfigProvider, + signalSampler: SignalSampler, httpEventValidator: HttpEventValidator) { self.logger = logger self.signalProcessor = signalProcessor @@ -38,6 +40,7 @@ final class BaseHttpEventCollector: HttpEventCollector { self.httpInterceptorCallbacks = httpInterceptorCallbacks self.client = client self.configProvider = configProvider + self.signalSampler = signalSampler self.httpEventValidator = httpEventValidator self.httpInterceptorCallbacks.httpDataCallback = onHttpCompletion(data:) } @@ -51,6 +54,7 @@ final class BaseHttpEventCollector: HttpEventCollector { URLSessionTaskInterceptor.shared.setHttpInterceptorCallbacks(httpInterceptorCallbacks) URLSessionTaskInterceptor.shared.setTimeProvider(timeProvider) URLSessionTaskInterceptor.shared.setConfigProvider(configProvider) + URLSessionTaskInterceptor.shared.setSignalSampler(signalSampler) logger.log(level: .info, message: "HttpEventCollector enabled.", error: nil, data: nil) } } diff --git a/ios/Sources/MeasureSDK/Swift/Http/URLSessionTaskInterceptor.swift b/ios/Sources/MeasureSDK/Swift/Http/URLSessionTaskInterceptor.swift index b4b816efd..a94b0bddc 100644 --- a/ios/Sources/MeasureSDK/Swift/Http/URLSessionTaskInterceptor.swift +++ b/ios/Sources/MeasureSDK/Swift/Http/URLSessionTaskInterceptor.swift @@ -15,6 +15,7 @@ final class URLSessionTaskInterceptor { private var configProvider: ConfigProvider? private var recentRequests: [String: UInt64] = [:] private let dedupeWindowMs: UInt64 = 300 + private var signalSampler: SignalSampler? private init() {} @@ -29,6 +30,10 @@ final class URLSessionTaskInterceptor { func setConfigProvider(_ configProvider: ConfigProvider) { self.configProvider = configProvider } + + func setSignalSampler(_ signalSampler: SignalSampler) { + self.signalSampler = signalSampler + } func urlSessionTask(_ task: URLSessionTask, setState state: URLSessionTask.State) { // swiftlint:disable:this function_body_length guard !MSRNetworkInterceptor.isEnabled else { return } @@ -36,11 +41,16 @@ final class URLSessionTaskInterceptor { guard let httpInterceptorCallbacks = self.httpInterceptorCallbacks, let timeProvider = self.timeProvider, let configProvider = self.configProvider, + let signalSampler = self.signalSampler, let url = task.currentRequest?.url?.absoluteString else { return } guard configProvider.shouldTrackHttpUrl(url: url) else { return } + + guard signalSampler.shouldTrackLaunchEvents() else { + return + } if state == .running, taskStartTimes[task] == nil { taskStartTimes[task] = UnsignedNumber(timeProvider.millisTime) diff --git a/ios/Sources/MeasureSDK/Swift/MeasureInitializer.swift b/ios/Sources/MeasureSDK/Swift/MeasureInitializer.swift index 6f381fa40..a6f104b68 100644 --- a/ios/Sources/MeasureSDK/Swift/MeasureInitializer.swift +++ b/ios/Sources/MeasureSDK/Swift/MeasureInitializer.swift @@ -418,6 +418,7 @@ final class BaseMeasureInitializer: MeasureInitializer { httpInterceptorCallbacks: HttpInterceptorCallbacks(), client: client, configProvider: configProvider, + signalSampler: signalSampler, httpEventValidator: httpEventValidator) self.internalSignalCollector = BaseInternalSignalCollector(logger: logger, timeProvider: timeProvider, diff --git a/ios/Tests/MeasureSDKTests/Config/BaseConfigProviderTests.swift b/ios/Tests/MeasureSDKTests/Config/BaseConfigProviderTests.swift index 8f0478c0d..8532d927c 100644 --- a/ios/Tests/MeasureSDKTests/Config/BaseConfigProviderTests.swift +++ b/ios/Tests/MeasureSDKTests/Config/BaseConfigProviderTests.swift @@ -29,7 +29,7 @@ final class ConfigProviderTests: XCTestCase { } func testShouldTrackHttpUrl_exactMatchBlocked() { - var config = BaseDynamicConfig.default() + var config = BaseDynamicConfig() config = BaseDynamicConfig( maxEventsInBatch: config.maxEventsInBatch, crashTimelineDurationSeconds: config.crashTimelineDurationSeconds, @@ -56,7 +56,7 @@ final class ConfigProviderTests: XCTestCase { } func testShouldTrackHttpUrl_wildcardMatch() { - var config = BaseDynamicConfig.default() + var config = BaseDynamicConfig() config = copy(config, disable: ["https://api.example.com/*"]) provider.setDynamicConfig(config) @@ -67,14 +67,14 @@ final class ConfigProviderTests: XCTestCase { } func testWildcardInMiddle() { - provider.setDynamicConfig(copy(.default(), disable: ["https://api.example.com/*/users"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: ["https://api.example.com/*/users"])) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://api.example.com/data/users")) XCTAssertTrue(provider.shouldTrackHttpUrl(url: "https://api.example.com/data/nomatch")) } func testMultipleWildcardPatterns() { - provider.setDynamicConfig(copy(.default(), disable: [ + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: [ "https://analytics.example.com/*", "https://tracking.example.com/*" ])) @@ -89,21 +89,21 @@ final class ConfigProviderTests: XCTestCase { } func testRequestBodyMatch() { - provider.setDynamicConfig(copy(.default(), request: ["https://api.example.com/*"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), request: ["https://api.example.com/*"])) XCTAssertTrue(provider.shouldTrackHttpBody(url: "https://api.example.com/users", contentType: nil)) XCTAssertFalse(provider.shouldTrackHttpBody(url: "https://other.example.com/data", contentType: nil)) } func testResponseBodyMatch() { - provider.setDynamicConfig(copy(.default(), response: ["https://api.example.com/*"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), response: ["https://api.example.com/*"])) XCTAssertTrue(provider.shouldTrackHttpBody(url: "https://api.example.com/users", contentType: nil)) XCTAssertFalse(provider.shouldTrackHttpBody(url: "https://other.example.com/data", contentType: nil)) } func testRequestAndResponseIndependent() { - provider.setDynamicConfig(copy(.default(), + provider.setDynamicConfig(copy(BaseDynamicConfig(), request: ["https://request.example.com/*"], response: ["https://response.example.com/*"])) @@ -122,7 +122,7 @@ final class ConfigProviderTests: XCTestCase { } func testDynamicBlockedHeader() { - provider.setDynamicConfig(copy(.default(), blockedHeaders: ["X-Custom-Header"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), blockedHeaders: ["X-Custom-Header"])) XCTAssertFalse(provider.shouldTrackHttpHeader(key: "X-Custom-Header")) } @@ -139,14 +139,14 @@ final class ConfigProviderTests: XCTestCase { let measureUrl = "https://measure.sh/api/v1" provider.setMeasureUrl(url: measureUrl) - provider.setDynamicConfig(copy(.default(), disable: ["https://analytics.example.com/*"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: ["https://analytics.example.com/*"])) XCTAssertFalse(provider.shouldTrackHttpUrl(url: measureUrl)) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://analytics.example.com/event")) } func testSetDynamicConfigUpdatesValues() { - provider.setDynamicConfig(copy(.default(), + provider.setDynamicConfig(copy(BaseDynamicConfig(), traceSamplingRate: 0.5, crashTakeScreenshot: false, cpuUsageInterval: 5000)) @@ -157,20 +157,20 @@ final class ConfigProviderTests: XCTestCase { } func testWildcardAtBeginning() { - provider.setDynamicConfig(copy(.default(), disable: ["*/api/v1/health"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: ["*/api/v1/health"])) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://example.com/api/v1/health")) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://other.com/api/v1/health")) } func testWildcardInMiddleHost() { - provider.setDynamicConfig(copy(.default(), disable: ["https://*/api/health"])) + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: ["https://*/api/health"])) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://example.com/api/health")) } func testRegexCharactersEscaped() { - provider.setDynamicConfig(copy(.default(), + provider.setDynamicConfig(copy(BaseDynamicConfig(), disable: ["https://api.example.com/path?query=value"])) XCTAssertFalse(provider.shouldTrackHttpUrl(url: "https://api.example.com/path?query=value")) diff --git a/ios/Tests/MeasureSDKTests/Config/ConfigLoaderTests.swift b/ios/Tests/MeasureSDKTests/Config/ConfigLoaderTests.swift index 8ab073573..1b7b4115f 100644 --- a/ios/Tests/MeasureSDKTests/Config/ConfigLoaderTests.swift +++ b/ios/Tests/MeasureSDKTests/Config/ConfigLoaderTests.swift @@ -72,7 +72,7 @@ final class ConfigLoaderTests: XCTestCase { } func testLoadDynamicConfig_returnsConfig_whenValidFileExists() throws { - let expected = BaseDynamicConfig.default() + let expected = BaseDynamicConfig() let data = try JSONEncoder().encode(expected) mockFileManager.savedFiles["config/config.json"] = data @@ -108,7 +108,7 @@ final class ConfigLoaderTests: XCTestCase { func testLoadConfig_success_updatesFileAndPrefs() throws { setupCacheExpired() - let config = BaseDynamicConfig.default() + let config = BaseDynamicConfig() let eTag = "new-etag" let cache: Number = 3600 @@ -130,7 +130,7 @@ final class ConfigLoaderTests: XCTestCase { func testLoadConfig_doesNotUpdateEtag_whenNil() { setupCacheExpired() - let config = BaseDynamicConfig.default() + let config = BaseDynamicConfig() mockNetworkClient.stubConfig(.success(config: config, eTag: nil, cacheControl: 3600)) configLoader.loadDynamicConfig { _ in } diff --git a/ios/Tests/MeasureSDKTests/Mocks/MockConfigLoader.swift b/ios/Tests/MeasureSDKTests/Mocks/MockConfigLoader.swift index 763b3360a..aff3cc9b6 100644 --- a/ios/Tests/MeasureSDKTests/Mocks/MockConfigLoader.swift +++ b/ios/Tests/MeasureSDKTests/Mocks/MockConfigLoader.swift @@ -13,7 +13,7 @@ final class MockConfigLoader: ConfigLoader { private(set) var didLoadConfig: Bool = false - init(config: DynamicConfig = BaseDynamicConfig.default()) { + init(config: DynamicConfig = BaseDynamicConfig()) { self.config = config } diff --git a/ios/Tests/MeasureSDKTests/Mocks/MockConfigProvider.swift b/ios/Tests/MeasureSDKTests/Mocks/MockConfigProvider.swift index eb6b49ec6..9fe6516f0 100644 --- a/ios/Tests/MeasureSDKTests/Mocks/MockConfigProvider.swift +++ b/ios/Tests/MeasureSDKTests/Mocks/MockConfigProvider.swift @@ -62,6 +62,7 @@ final class MockConfigProvider: ConfigProvider { var anrTakeScreenshot: Bool var launchSamplingRate: Float var gestureClickTakeSnapshot: Bool + var httpSamplingRate: Float var httpDisableEventForUrls: [String] var httpTrackRequestForUrls: [String] var httpTrackResponseForUrls: [String] @@ -133,7 +134,7 @@ final class MockConfigProvider: ConfigProvider { maxAttachmentSizeInEventsBatchInBytes: Number = 123_123, timeoutIntervalForRequest: TimeInterval = 123_123, httpContentTypeAllowlist: [String] = ["application/json"], - dynamicConfig: DynamicConfig = BaseDynamicConfig.default(), + dynamicConfig: DynamicConfig = BaseDynamicConfig(), combinedHttpUrlBlocklist: [String] = [], maxEventsInBatch: Number = 10_000, crashTimelineDurationSeconds: Number = 300, @@ -148,6 +149,7 @@ final class MockConfigProvider: ConfigProvider { anrTakeScreenshot: Bool = true, launchSamplingRate: Float = 0.01, gestureClickTakeSnapshot: Bool = true, + httpSamplingRate: Float = 0.01, httpDisableEventForUrls: [String] = [], httpTrackRequestForUrls: [String] = [], httpTrackResponseForUrls: [String] = [], @@ -216,6 +218,7 @@ final class MockConfigProvider: ConfigProvider { self.anrTakeScreenshot = anrTakeScreenshot self.launchSamplingRate = launchSamplingRate self.gestureClickTakeSnapshot = gestureClickTakeSnapshot + self.httpSamplingRate = httpSamplingRate self.httpDisableEventForUrls = httpDisableEventForUrls self.httpTrackRequestForUrls = httpTrackRequestForUrls self.httpTrackResponseForUrls = httpTrackResponseForUrls diff --git a/ios/Tests/MeasureSDKTests/Mocks/MockMeasureInitializer.swift b/ios/Tests/MeasureSDKTests/Mocks/MockMeasureInitializer.swift index 4ecea6e72..d1f1c1435 100644 --- a/ios/Tests/MeasureSDKTests/Mocks/MockMeasureInitializer.swift +++ b/ios/Tests/MeasureSDKTests/Mocks/MockMeasureInitializer.swift @@ -327,6 +327,7 @@ final class MockMeasureInitializer: MeasureInitializer { httpInterceptorCallbacks: HttpInterceptorCallbacks(), client: self.client, configProvider: self.configProvider, + signalSampler: self.signalSampler, httpEventValidator: self.httpEventValidator) self.internalSignalCollector = internalSignalCollector ?? BaseInternalSignalCollector(logger: self.logger, timeProvider: self.timeProvider, diff --git a/ios/Tests/MeasureSDKTests/Mocks/MockSignalSampler.swift b/ios/Tests/MeasureSDKTests/Mocks/MockSignalSampler.swift index 4a258d8c8..efc277ea6 100644 --- a/ios/Tests/MeasureSDKTests/Mocks/MockSignalSampler.swift +++ b/ios/Tests/MeasureSDKTests/Mocks/MockSignalSampler.swift @@ -13,6 +13,7 @@ final class MockSignalSampler: SignalSampler { var shouldTrackTraceReturnValue: Bool = false var shouldTrackJourneyEventsReturnValue: Bool = false var shouldSampleTraceReturnValue: Bool = false + var shouldSampleTHttpEventValue: Bool = false func shouldTrackTrace() -> Bool { return shouldTrackTraceReturnValue @@ -29,4 +30,8 @@ final class MockSignalSampler: SignalSampler { func shouldSampleTrace(_ traceId: String) -> Bool { return shouldSampleTraceReturnValue } + + func shouldSampleHttpEvent() -> Bool { + return shouldSampleTHttpEventValue + } }