Skip to content

Commit 2006d0e

Browse files
authored
Merge 276f608 into e3dbde2
2 parents e3dbde2 + 276f608 commit 2006d0e

12 files changed

Lines changed: 154 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
### Features
1111

1212
- Add `showMaskPreview` to `SentrySDK.replay` api to debug replay masking (#4761)
13+
- Session replay masking preview for SwiftUI (#4737)
1314

1415
## 8.44.0-beta.1
1516

Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ struct ContentView: View {
137137

138138
return DataBag.shared.info["lastSpan"] as? Span
139139
}
140-
140+
141141
var body: some View {
142142
return SentryTracedView("Content View Body", waitForFullDisplay: true) {
143143
NavigationView {
@@ -235,7 +235,7 @@ struct ContentView: View {
235235
.background(Color.white)
236236
}
237237
SecondView()
238-
238+
239239
Text(TTDInfo)
240240
.accessibilityIdentifier("TTDInfo")
241241
}
@@ -255,5 +255,6 @@ struct SecondView: View {
255255
struct ContentView_Previews: PreviewProvider {
256256
static var previews: some View {
257257
ContentView()
258+
.sentryReplayPreviewMask(opacity: 0.3)
258259
}
259260
}

Sentry.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,9 @@
874874
D867063F27C3BC2400048851 /* SentryCoreDataTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D867063C27C3BC2400048851 /* SentryCoreDataTracker.h */; };
875875
D86B6835294348A400B8B1FC /* SentryAttachment+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D86B6834294348A400B8B1FC /* SentryAttachment+Private.h */; };
876876
D86F419827C8FEFA00490520 /* SentryCoreDataTrackerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86F419727C8FEFA00490520 /* SentryCoreDataTrackerExtension.swift */; };
877+
D8709AC42D3E9C63006C491E /* SentryReplayMaskPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8709AC32D3E9C5C006C491E /* SentryReplayMaskPreview.swift */; };
878+
D8709ACB2D3F848E006C491E /* SentryReplayMaskPreviewUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8709ACA2D3F8480006C491E /* SentryReplayMaskPreviewUIView.swift */; };
879+
D8709ACD2D3F84CF006C491E /* PreviewRedactOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8709ACC2D3F84C9006C491E /* PreviewRedactOptions.swift */; };
877880
D8739CF32BECF70F007D2F66 /* SentryLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8739CF22BECF70F007D2F66 /* SentryLevel.swift */; };
878881
D8739CF92BECFFB5007D2F66 /* SentryTransactionNameSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8739CF82BECFFB5007D2F66 /* SentryTransactionNameSource.swift */; };
879882
D8739D142BEE5049007D2F66 /* SentryRRWebSpanEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8739D132BEE5049007D2F66 /* SentryRRWebSpanEvent.swift */; };
@@ -1985,6 +1988,9 @@
19851988
D86B6820293F39E000B8B1FC /* TestSentryViewHierarchy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestSentryViewHierarchy.h; sourceTree = "<group>"; };
19861989
D86B6834294348A400B8B1FC /* SentryAttachment+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryAttachment+Private.h"; path = "include/SentryAttachment+Private.h"; sourceTree = "<group>"; };
19871990
D86F419727C8FEFA00490520 /* SentryCoreDataTrackerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCoreDataTrackerExtension.swift; sourceTree = "<group>"; };
1991+
D8709AC32D3E9C5C006C491E /* SentryReplayMaskPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryReplayMaskPreview.swift; sourceTree = "<group>"; };
1992+
D8709ACA2D3F8480006C491E /* SentryReplayMaskPreviewUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryReplayMaskPreviewUIView.swift; sourceTree = "<group>"; };
1993+
D8709ACC2D3F84C9006C491E /* PreviewRedactOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewRedactOptions.swift; sourceTree = "<group>"; };
19881994
D8739CF22BECF70F007D2F66 /* SentryLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLevel.swift; sourceTree = "<group>"; };
19891995
D8739CF82BECFFB5007D2F66 /* SentryTransactionNameSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTransactionNameSource.swift; sourceTree = "<group>"; };
19901996
D8739D132BEE5049007D2F66 /* SentryRRWebSpanEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRRWebSpanEvent.swift; sourceTree = "<group>"; };
@@ -3779,6 +3785,7 @@
37793785
isa = PBXGroup;
37803786
children = (
37813787
D8199DB429376ECC0074249E /* SentryInternal */,
3788+
D8709AC92D3F83A6006C491E /* Preview */,
37823789
D8199DB529376ECC0074249E /* SentrySwiftUI.h */,
37833790
D88D25E92B8E0BAC0073C3D5 /* module.modulemap */,
37843791
D8199DB629376ECC0074249E /* SentryTracedView.swift */,
@@ -3876,6 +3883,16 @@
38763883
name = CoreData;
38773884
sourceTree = "<group>";
38783885
};
3886+
D8709AC92D3F83A6006C491E /* Preview */ = {
3887+
isa = PBXGroup;
3888+
children = (
3889+
D8709ACA2D3F8480006C491E /* SentryReplayMaskPreviewUIView.swift */,
3890+
D8709AC32D3E9C5C006C491E /* SentryReplayMaskPreview.swift */,
3891+
D8709ACC2D3F84C9006C491E /* PreviewRedactOptions.swift */,
3892+
);
3893+
path = Preview;
3894+
sourceTree = "<group>";
3895+
};
38793896
D8739CF62BECFF86007D2F66 /* Log */ = {
38803897
isa = PBXGroup;
38813898
children = (
@@ -5323,7 +5340,10 @@
53235340
isa = PBXSourcesBuildPhase;
53245341
buildActionMask = 2147483647;
53255342
files = (
5343+
D8709AC42D3E9C63006C491E /* SentryReplayMaskPreview.swift in Sources */,
53265344
D8199DC129376EEC0074249E /* SentryTracedView.swift in Sources */,
5345+
D8709ACD2D3F84CF006C491E /* PreviewRedactOptions.swift in Sources */,
5346+
D8709ACB2D3F848E006C491E /* SentryReplayMaskPreviewUIView.swift in Sources */,
53275347
D48E8B9D2D3E82AC0032E35E /* SentrySpanOperation.swift in Sources */,
53285348
D8199DBF29376EE20074249E /* SentryInternal.m in Sources */,
53295349
D48E8B8B2D3E79610032E35E /* SentryTraceOrigin.swift in Sources */,

SentrySwiftUI.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ Pod::Spec.new do |s|
1919
s.watchos.framework = 'WatchKit'
2020

2121
s.source_files = "Sources/SentrySwiftUI/**/*.{swift,h,m}"
22-
s.dependency 'Sentry', "8.44.0-beta.1"
22+
s.dependency 'Sentry/HybridSDK', "8.44.0-beta.1"
2323
end

Sources/Sentry/PrivateSentrySDKOnly.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ + (SentryBreadcrumb *)breadcrumbWithDictionary:(NSDictionary *)dictionary
321321
}
322322

323323
#if SENTRY_TARGET_REPLAY_SUPPORTED
324+
325+
+ (UIView *)sessionReplayMaskingOverlay:(id<SentryRedactOptions>)options
326+
{
327+
return [[SentryMaskingPreviewView alloc] initWithRedactOptions:options];
328+
}
329+
324330
+ (nullable SentrySessionReplayIntegration *)getReplayIntegration
325331
{
326332

Sources/Sentry/SentrySDK.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,14 +203,16 @@ + (void)setStartTimestamp:(NSDate *)value
203203

204204
+ (void)startWithOptions:(SentryOptions *)options
205205
{
206+
// We save the options before checking for Xcode preview because
207+
// we will use this options in the preview
208+
startOption = options;
206209
if ([SentryDependencyContainer.sharedInstance.processInfoWrapper
207210
.environment[SENTRY_XCODE_PREVIEW_ENVIRONMENT_KEY] isEqualToString:@"1"]) {
208211
// Using NSLog because SentryLog was not initialized yet.
209212
NSLog(@"[SENTRY] [WARNING] SentrySDK not started. Running from Xcode preview.");
210213
return;
211214
}
212215

213-
startOption = options;
214216
[SentryLog configure:options.debug diagnosticLevel:options.diagnosticLevel];
215217

216218
// We accept the tradeoff that the SDK might not be fully initialized directly after

Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
@class SentryEnvelope;
2020
@class SentryId;
2121
@class SentrySessionReplayIntegration;
22+
@class UIView;
2223

2324
@protocol SentryReplayBreadcrumbConverter;
2425
@protocol SentryViewScreenshotProvider;
26+
@protocol SentryRedactOptions;
2527

2628
NS_ASSUME_NONNULL_BEGIN
2729

@@ -184,6 +186,13 @@ typedef void (^SentryOnAppStartMeasurementAvailable)(
184186

185187
#if SENTRY_TARGET_REPLAY_SUPPORTED
186188

189+
/**
190+
* Return an instance of SentryRedactOptions with given option
191+
* To be used from SentrySwiftUI, which cannot access the private
192+
* `SentryRedactOptions` class.
193+
*/
194+
+ (UIView *)sessionReplayMaskingOverlay:(id<SentryRedactOptions>)options;
195+
187196
/**
188197
* Configure session replay with different breadcrumb converter
189198
* and screeshot provider. Used by the Hybrid SDKs.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS)
2+
import Sentry
3+
4+
public class PreviewRedactOptions: SentryRedactOptions {
5+
public let maskAllText: Bool
6+
public let maskAllImages: Bool
7+
public let maskedViewClasses: [AnyClass]
8+
public let unmaskedViewClasses: [AnyClass]
9+
10+
public init(maskAllText: Bool = true, maskAllImages: Bool = true, maskedViewClasses: [AnyClass] = [], unmaskedViewClasses: [AnyClass] = []) {
11+
self.maskAllText = maskAllText
12+
self.maskAllImages = maskAllImages
13+
self.maskedViewClasses = maskedViewClasses
14+
self.unmaskedViewClasses = unmaskedViewClasses
15+
}
16+
}
17+
18+
#endif
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS)
2+
import Sentry
3+
import SwiftUI
4+
import UIKit
5+
6+
#if CARTHAGE || SWIFT_PACKAGE
7+
@_implementationOnly import SentryInternal
8+
#endif
9+
10+
@available(iOS 13, macOS 10.15, tvOS 13, *)
11+
struct SentryReplayMaskPreview: ViewModifier {
12+
let redactOptions: SentryRedactOptions
13+
let opacity: Float
14+
func body(content: Content) -> some View {
15+
content.overlay(SentryReplayPreviewView(redactOptions: redactOptions, opacity: opacity).disabled(true))
16+
}
17+
}
18+
19+
@available(iOS 13, macOS 10.15, tvOS 13, *)
20+
public extension View {
21+
func sentryReplayPreviewMask(redactOptions: SentryRedactOptions? = nil, opacity: Float = 1) -> some View {
22+
let options = redactOptions ?? SentrySDK.options?.sessionReplay ?? PreviewRedactOptions()
23+
return modifier(SentryReplayMaskPreview(redactOptions: options, opacity: opacity))
24+
}
25+
}
26+
27+
@available(iOS 13, macOS 10.15, tvOS 13, *)
28+
struct SentryReplayPreviewView: UIViewRepresentable {
29+
let redactOptions: SentryRedactOptions
30+
let opacity: Float
31+
32+
func makeUIView(context: Context) -> SentryReplayMaskPreviewUIView {
33+
return SentryReplayMaskPreviewUIView(redactOptions: redactOptions)
34+
}
35+
36+
func updateUIView(_ uiView: SentryReplayMaskPreviewUIView, context: Context) {
37+
uiView.opacity = CGFloat(opacity)
38+
}
39+
}
40+
41+
#endif
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS)
2+
import Sentry
3+
import UIKit
4+
5+
#if CARTHAGE || SWIFT_PACKAGE
6+
import Sentry._Hybrid
7+
@_implementationOnly import SentryInternal
8+
#endif
9+
10+
class SentryReplayMaskPreviewUIView: UIView {
11+
12+
private let maskingOverlay: UIView
13+
14+
var opacity: CGFloat {
15+
get { maskingOverlay.alpha }
16+
set { maskingOverlay.alpha = newValue }
17+
}
18+
19+
init(redactOptions: SentryRedactOptions) {
20+
maskingOverlay = PrivateSentrySDKOnly.sessionReplayMaskingOverlay(redactOptions)
21+
super.init(frame: .zero)
22+
}
23+
24+
required init?(coder: NSCoder) {
25+
fatalError("init(coder:) has not been implemented")
26+
}
27+
28+
override func didMoveToWindow() {
29+
super.didMoveToWindow()
30+
guard let window = self.window else { return }
31+
maskingOverlay.frame = window.bounds
32+
window.addSubview(maskingOverlay)
33+
}
34+
}
35+
36+
#endif

0 commit comments

Comments
 (0)