diff --git a/CHANGELOG.md b/CHANGELOG.md index c126bb64d59..514569f9f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Custom redact modifier for SwiftUI (#4362) + ### Improvements - Speed up HTTP tracking for multiple requests in parallel (#4366) @@ -22,7 +26,7 @@ - Resumes replay when the app becomes active (#4303) - Session replay redact view with transformation (#4308) -- Correct redact UIView with higher zPosition (#4309) +- Correct redact UIView with higher zPosition (#4309) - Don't redact clipped views (#4325) - Session replay for crash not created because of a race condition (#4314) - Double-quoted include, expected angle-bracketed instead (#4298) diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift index 2ab138631df..280f2c20efb 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift @@ -163,7 +163,6 @@ struct ContentView: View { Button(action: captureTransactionAction) { Text("Capture Transaction") } - } VStack(spacing: 16) { Button(action: { @@ -200,6 +199,7 @@ struct ContentView: View { Text("Form Screen") } } + .sentryReplayRedact() } SecondView() } diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift index 7ff18b26f10..502e719f66e 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift @@ -10,6 +10,9 @@ struct SwiftUIApp: App { options.debug = true options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.experimental.sessionReplay.sessionSampleRate = 1.0 + options.experimental.sessionReplay.redactAllImages = false + options.experimental.sessionReplay.redactAllText = false options.initialScope = { scope in scope.injectGitInformation() return scope diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 79fbb794e18..0b5a30c2bab 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -839,6 +839,9 @@ D84F833E2A1CC401005828E0 /* SentrySwiftAsyncIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = D84F833C2A1CC401005828E0 /* SentrySwiftAsyncIntegration.m */; }; D851527F2C9971020070F669 /* SentryStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D851527E2C9971020070F669 /* SentryStringUtils.h */; }; D85152832C997A280070F669 /* SentryStringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85152822C997A1F0070F669 /* SentryStringUtils.swift */; }; + D85153002CA2B5F60070F669 /* SentrySwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */; }; + D85153012CA2B5F60070F669 /* SentrySwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D8199DAA29376E9B0074249E /* SentrySwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D851530C2CA2B7B00070F669 /* SentryRedactModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */; }; D85596F3280580F10041FF8B /* SentryScreenshotIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = D85596F1280580F10041FF8B /* SentryScreenshotIntegration.m */; }; D855AD62286ED6A4002573E1 /* SentryCrashTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D855AD61286ED6A4002573E1 /* SentryCrashTests.m */; }; D855B3E827D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D855B3E727D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift */; }; @@ -882,6 +885,7 @@ D8918B222849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */; }; D8A3649C2C91AA3300AC569B /* SentryReplayApi.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A3649B2C91AA3300AC569B /* SentryReplayApi.m */; }; D8A3649D2C91AA3300AC569B /* SentryReplayApi.h in Headers */ = {isa = PBXBuildFile; fileRef = D8A3649A2C91AA3300AC569B /* SentryReplayApi.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8A65B5D2C98656800974B74 /* SentryReplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A65B5C2C98656000974B74 /* SentryReplayView.swift */; }; D8AB40DB2806EC1900E5E9F7 /* SentryScreenshotIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8AB40DA2806EC1900E5E9F7 /* SentryScreenshotIntegration.h */; }; D8ACE3C72762187200F5A213 /* SentryNSDataSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D8ACE3C42762187200F5A213 /* SentryNSDataSwizzling.m */; }; D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = D8ACE3C52762187200F5A213 /* SentryNSDataTracker.m */; }; @@ -982,6 +986,13 @@ remoteGlobalIDString = D84DAD4C2B17428D003CF120; remoteInfo = SentryTestUtilsDynamic; }; + D85153022CA2B5F60070F669 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 6327C5CA1EB8A783004E799B /* Project object */; + proxyType = 1; + remoteGlobalIDString = D8199DA929376E9B0074249E; + remoteInfo = SentrySwiftUI; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -992,6 +1003,7 @@ dstSubfolderSpec = 10; files = ( D84DAD5A2B1742C1003CF120 /* SentryTestUtilsDynamic.framework in Embed Frameworks */, + D85153012CA2B5F60070F669 /* SentrySwiftUI.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -1916,6 +1928,7 @@ D8511F722BAC8F750015E6FD /* Sentry.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Sentry.modulemap; sourceTree = ""; }; D851527E2C9971020070F669 /* SentryStringUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryStringUtils.h; sourceTree = ""; }; D85152822C997A1F0070F669 /* SentryStringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryStringUtils.swift; sourceTree = ""; }; + D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRedactModifierTests.swift; sourceTree = ""; }; D85596F1280580F10041FF8B /* SentryScreenshotIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryScreenshotIntegration.m; sourceTree = ""; }; D855AD61286ED6A4002573E1 /* SentryCrashTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCrashTests.m; sourceTree = ""; }; D855B3E727D652AF00BCED76 /* SentryCoreDataTrackingIntegrationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCoreDataTrackingIntegrationTest.swift; sourceTree = ""; }; @@ -1964,6 +1977,7 @@ D8918B212849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySDKIntegrationTestsBase.swift; sourceTree = ""; }; D8A3649A2C91AA3300AC569B /* SentryReplayApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryReplayApi.h; path = Public/SentryReplayApi.h; sourceTree = ""; }; D8A3649B2C91AA3300AC569B /* SentryReplayApi.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryReplayApi.m; sourceTree = ""; }; + D8A65B5C2C98656000974B74 /* SentryReplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryReplayView.swift; sourceTree = ""; }; D8AB40DA2806EC1900E5E9F7 /* SentryScreenshotIntegration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryScreenshotIntegration.h; path = include/SentryScreenshotIntegration.h; sourceTree = ""; }; D8ACE3C42762187200F5A213 /* SentryNSDataSwizzling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryNSDataSwizzling.m; sourceTree = ""; }; D8ACE3C52762187200F5A213 /* SentryNSDataTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentryNSDataTracker.m; sourceTree = ""; }; @@ -2054,6 +2068,7 @@ 8431F01C29B2854200D8DC56 /* libSentryTestUtils.a in Frameworks */, D84DAD592B1742C1003CF120 /* SentryTestUtilsDynamic.framework in Frameworks */, 63AA766A1EB8CB2F00D153DE /* Sentry.framework in Frameworks */, + D85153002CA2B5F60070F669 /* SentrySwiftUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2588,6 +2603,7 @@ 7BF536D224BEF240004FA6A2 /* TestUtils */, D81FDF0F280E9FEC0045E0E4 /* Tools */, 7B6C5ED4264E62B60010D138 /* Transaction */, + D851530A2CA2B7960070F669 /* SwiftUI */, ); path = SentryTests; sourceTree = ""; @@ -3727,6 +3743,7 @@ D8199DB529376ECC0074249E /* SentrySwiftUI.h */, D88D25E92B8E0BAC0073C3D5 /* module.modulemap */, D8199DB629376ECC0074249E /* SentryTracedView.swift */, + D8A65B5C2C98656000974B74 /* SentryReplayView.swift */, ); path = SentrySwiftUI; sourceTree = ""; @@ -3770,6 +3787,14 @@ path = SentryTestUtilsDynamic; sourceTree = ""; }; + D851530A2CA2B7960070F669 /* SwiftUI */ = { + isa = PBXGroup; + children = ( + D851530B2CA2B7A30070F669 /* SentryRedactModifierTests.swift */, + ); + path = SwiftUI; + sourceTree = ""; + }; D85596EF280580BE0041FF8B /* Screenshot */ = { isa = PBXGroup; children = ( @@ -4370,6 +4395,7 @@ dependencies = ( 63AA766C1EB8CB2F00D153DE /* PBXTargetDependency */, D84DAD5C2B1742C1003CF120 /* PBXTargetDependency */, + D85153032CA2B5F60070F669 /* PBXTargetDependency */, ); name = SentryTests; packageProductDependencies = ( @@ -4483,8 +4509,6 @@ }; D8199DA929376E9B0074249E = { CreatedOnToolsVersion = 14.1; - DevelopmentTeam = 97JCY7859U; - ProvisioningStyle = Automatic; }; D84DAD4C2B17428D003CF120 = { CreatedOnToolsVersion = 15.0.1; @@ -5083,6 +5107,7 @@ D8AE48C12C57B1550092A2A6 /* SentryLevelTests.swift in Sources */, 63FE721820DA66EC00CDBAE8 /* TestThread.m in Sources */, 7B4D308A26FC616B00C94DE9 /* SentryHttpTransportTests.swift in Sources */, + D851530C2CA2B7B00070F669 /* SentryRedactModifierTests.swift in Sources */, 7B4E23B6251A07BD00060D68 /* SentryDispatchQueueWrapperTests.swift in Sources */, 63FE720720DA66EC00CDBAE8 /* SentryCrashReportFilter_Tests.m in Sources */, 8F73BC312B02B87E00C3CEF4 /* SentryInstallationTests.swift in Sources */, @@ -5209,6 +5234,7 @@ files = ( D8199DC129376EEC0074249E /* SentryTracedView.swift in Sources */, D8199DBF29376EE20074249E /* SentryInternal.m in Sources */, + D8A65B5D2C98656800974B74 /* SentryReplayView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5248,6 +5274,11 @@ target = D84DAD4C2B17428D003CF120 /* SentryTestUtilsDynamic */; targetProxy = D84DAD5B2B1742C1003CF120 /* PBXContainerItemProxy */; }; + D85153032CA2B5F60070F669 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D8199DA929376E9B0074249E /* SentrySwiftUI */; + targetProxy = D85153022CA2B5F60070F669 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -5763,10 +5794,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -5786,6 +5817,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; @@ -6241,10 +6273,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -6263,6 +6295,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; @@ -6472,10 +6505,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -6495,6 +6528,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; @@ -6520,10 +6554,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -6542,6 +6576,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; @@ -6566,10 +6601,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -6588,6 +6623,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; @@ -6612,10 +6648,10 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 97JCY7859U; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -6634,6 +6670,7 @@ MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "-DCARTHAGE"; PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySwiftUI; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = Sources/SentrySwiftUI/; diff --git a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h index 3cf25d689c8..ce265ced396 100644 --- a/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h +++ b/Sources/SentrySwiftUI/SentryInternal/SentryInternal.h @@ -20,8 +20,6 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); @property (nonatomic, class, readonly) SentryPerformanceTracker *shared; -- (SentrySpanId *)startSpanWithName:(NSString *)name operation:(NSString *)operation; - - (SentrySpanId *)startSpanWithName:(NSString *)name nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation @@ -30,11 +28,13 @@ typedef NS_ENUM(NSUInteger, SentrySpanStatus); - (void)activateSpan:(SentrySpanId *)spanId duringBlock:(void (^)(void))block; - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation origin:(NSString *)origin inBlock:(void (^)(void))block; - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation origin:(NSString *)origin parentSpanId:(SentrySpanId *)parentSpanId diff --git a/Sources/SentrySwiftUI/SentryReplayView.swift b/Sources/SentrySwiftUI/SentryReplayView.swift new file mode 100644 index 00000000000..fa24d3f4e2f --- /dev/null +++ b/Sources/SentrySwiftUI/SentryReplayView.swift @@ -0,0 +1,44 @@ +#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS) +import Sentry +import SwiftUI +import UIKit + +@available(iOS 13, macOS 10.15, tvOS 13, *) +struct SentryReplayView: UIViewRepresentable { + class SentryRedactView: UIView { + } + + func makeUIView(context: Context) -> UIView { + let result = SentryRedactView() + result.sentryReplayRedact() + return result + } + + func updateUIView(_ uiView: UIView, context: Context) { + // This is blank on purpose. UIViewRepresentable requires this function. + } +} + +@available(iOS 13, macOS 10.15, tvOS 13, *) +struct SentryReplayModifier: ViewModifier { + func body(content: Content) -> some View { + content.background(SentryReplayView()) + } +} + +@available(iOS 13, macOS 10.15, tvOS 13, *) +public extension View { + + /// Marks the view as containing sensitive information that should be redacted during replays. + /// + /// When this modifier is applied, any sensitive content within the view will be hidden or masked + /// during session replays to ensure user privacy. This is useful for views containing personal + /// data or confidential information that shouldn't be visible when the replay is reviewed. + /// + /// - Returns: A modifier that redacts sensitive information during session replays. + /// - Experiment: This is an experimental feature and may still have bugs. + func sentryReplayRedact() -> some View { + modifier(SentryReplayModifier()) + } +} +#endif diff --git a/Sources/SentrySwiftUI/SentryTracedView.swift b/Sources/SentrySwiftUI/SentryTracedView.swift index c983fea1ae8..97e94ea9367 100644 --- a/Sources/SentrySwiftUI/SentryTracedView.swift +++ b/Sources/SentrySwiftUI/SentryTracedView.swift @@ -1,3 +1,4 @@ +#if canImport(SwiftUI) import Foundation #if SENTRY_NO_UIKIT import SentryWithoutUIKit @@ -104,3 +105,4 @@ public extension View { } } } +#endif diff --git a/Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift b/Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift new file mode 100644 index 00000000000..a6023705e57 --- /dev/null +++ b/Tests/SentryTests/SwiftUI/SentryRedactModifierTests.swift @@ -0,0 +1,18 @@ +#if canImport(UIKit) && canImport(SwiftUI) +@testable import Sentry +@testable import SentrySwiftUI +import SwiftUI +import XCTest + +class SentryRedactModifierTests: XCTestCase { + + func testViewRedacted() throws { + let text = Text("Hello, World!") + let redactedText = text.sentryReplayRedact() + + XCTAssertTrue(redactedText is ModifiedContent) + } + +} + +#endif