Skip to content

Commit

Permalink
chore: Allows hybrid SDKs to set the current screen (#4110)
Browse files Browse the repository at this point in the history
Added setCurrentScreen: to PrivateSentrySdkOnly
  • Loading branch information
brustolin authored Jun 25, 2024
1 parent 62f06fc commit 6956d43
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 15 deletions.
8 changes: 8 additions & 0 deletions Sources/Sentry/PrivateSentrySDKOnly.mm
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,14 @@ + (SentryScreenFrames *)currentScreenFrames
#endif // SENTRY_HAS_UIKIT
}

#if SENTRY_UIKIT_AVAILABLE
+ (void)setCurrentScreen:(NSString *)screenName
{
[SentrySDK.currentHub
configureScope:^(SentryScope *scope) { scope.currentScreen = screenName; }];
}
#endif // SENTRY_HAS_UIKIT

+ (NSData *)captureViewHierarchy
{
#if SENTRY_HAS_UIKIT
Expand Down
34 changes: 21 additions & 13 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,9 @@ - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event
// the same as when the app crashed.
[self applyExtraDeviceContextToEvent:event];
[self applyCultureContextToEvent:event];
#if SENTRY_HAS_UIKIT
[self applyCurrentViewNamesToEventContext:event withScope:scope];
#endif // SENTRY_HAS_UIKIT
}

// With scope applied, before running callbacks run:
Expand Down Expand Up @@ -880,24 +883,29 @@ - (void)applyExtraDeviceContextToEvent:(SentryEvent *)event
key:@"app"
block:^(NSMutableDictionary *app) {
[app addEntriesFromDictionary:extraContext[@"app"]];
#if SENTRY_HAS_UIKIT
[self addViewNamesToContext:app event:event];
#endif // SENTRY_HAS_UIKIT
}];
}

#if SENTRY_HAS_UIKIT
- (void)addViewNamesToContext:(NSMutableDictionary *)appContext event:(SentryEvent *)event
- (void)applyCurrentViewNamesToEventContext:(SentryEvent *)event withScope:(SentryScope *)scope
{
if ([event isKindOfClass:[SentryTransaction class]]) {
SentryTransaction *transaction = (SentryTransaction *)event;
if ([transaction.viewNames count] > 0) {
appContext[@"view_names"] = transaction.viewNames;
}
} else {
appContext[@"view_names"] =
[SentryDependencyContainer.sharedInstance.application relevantViewControllersNames];
}
[self modifyContext:event
key:@"app"
block:^(NSMutableDictionary *app) {
if ([event isKindOfClass:[SentryTransaction class]]) {
SentryTransaction *transaction = (SentryTransaction *)event;
if ([transaction.viewNames count] > 0) {
app[@"view_names"] = transaction.viewNames;
}
} else {
if (scope.currentScreen != nil) {
app[@"view_names"] = @[ scope.currentScreen ];
} else {
app[@"view_names"] = [SentryDependencyContainer.sharedInstance
.application relevantViewControllersNames];
}
}
}];
}
#endif // SENTRY_HAS_UIKIT

Expand Down
12 changes: 12 additions & 0 deletions Sources/Sentry/SentryScope.m
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,18 @@ - (void)setFingerprint:(NSArray<NSString *> *_Nullable)fingerprint
}
}

- (void)setCurrentScreen:(nullable NSString *)currentScreen
{
_currentScreen = currentScreen;

SEL setCurrentScreen = @selector(setCurrentScreen:);
for (id<SentryScopeObserver> observer in self.observers) {
if ([observer respondsToSelector:setCurrentScreen]) {
[observer setCurrentScreen:currentScreen];
}
}
}

- (void)setLevel:(enum SentryLevel)level
{
self.levelEnum = level;
Expand Down
14 changes: 12 additions & 2 deletions Sources/Sentry/SentryTracer.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#import "SentryRandom.h"
#import "SentrySDK+Private.h"
#import "SentrySamplerDecision.h"
#import "SentryScope.h"
#import "SentryScope+Private.h"
#import "SentrySpan.h"
#import "SentrySpanContext+Private.h"
#import "SentrySpanContext.h"
Expand Down Expand Up @@ -154,7 +154,17 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti
}

#if SENTRY_HAS_UIKIT
viewNames = [SentryDependencyContainer.sharedInstance.application relevantViewControllersNames];
[hub configureScope:^(SentryScope *scope) {
if (scope.currentScreen != nil) {
self->viewNames = @[ scope.currentScreen ];
}
}];

if (viewNames == nil) {
viewNames =
[SentryDependencyContainer.sharedInstance.application relevantViewControllersNames];
}

#endif // SENTRY_HAS_UIKIT

_idleTimeoutLock = [[NSObject alloc] init];
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ typedef void (^SentryOnAppStartMeasurementAvailable)(
*/
+ (NSData *)captureViewHierarchy;

/**
* Allow Hybrids SDKs to set the current Screen.
*/
+ (void)setCurrentScreen:(NSString *)screenName;

#endif // SENTRY_UIKIT_AVAILABLE

#if SENTRY_TARGET_REPLAY_SUPPORTED
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryScope+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ SentryScope ()

@property (atomic, strong) SentryPropagationContext *propagationContext;

@property (nonatomic, nullable, copy) NSString *currentScreen;

- (NSArray<SentryBreadcrumb *> *)breadcrumbs;

/**
Expand Down
3 changes: 3 additions & 0 deletions Sources/Sentry/include/SentryScopeObserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ NS_ASSUME_NONNULL_BEGIN

- (void)clear;

@optional
- (void)setCurrentScreen:(NSString *)currentScreen;

@end

NS_ASSUME_NONNULL_END
33 changes: 33 additions & 0 deletions Tests/SentryTests/SentryClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,22 @@ class SentryClientTest: XCTestCase {
let viewName = sentEvent.context?["app"]?["view_names"] as? [String]
XCTAssertEqual(viewName?.first, "ClientTestViewController")
}

func testCaptureEventWithCurrentScreenInTheScope() throws {
SentryDependencyContainer.sharedInstance().application = TestSentryUIApplication()

let event = Event()
event.exceptions = [ Exception(value: "", type: "")]

let scope = fixture.scope
scope.currentScreen = "TestScreen"

fixture.getSut().capture(event: event, scope: scope)

let sentEvent = try lastSentEventWithAttachment()
let viewName = sentEvent.context?["app"]?["view_names"] as? [String]
XCTAssertEqual(viewName?.first, "TestScreen")
}

func testCaptureEventWithNoCurrentScreenMainIsLocked() throws {
SentryDependencyContainer.sharedInstance().application = TestSentryUIApplication()
Expand Down Expand Up @@ -338,6 +354,23 @@ class SentryClientTest: XCTestCase {
XCTAssertEqual(viewName?.first, "ClientTestViewController")
}

func testCaptureTransactionWithScreenInScope() throws {
SentryDependencyContainer.sharedInstance().application = TestSentryUIApplication()
let scope = fixture.scope
scope.currentScreen = "TransactionScreen"
let hub = SentryHub(client: SentryClient(options: Options()), andScope: scope)

let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Operation"), hub: hub)

scope.currentScreen = "SecondScreen"
let event = try XCTUnwrap(Dynamic(tracer).toTransaction() as Transaction?, "Could not get transaction from tracer")
fixture.getSut().capture(event: event, scope: scope)

let lastEvent = try lastSentEventWithAttachment()
let viewName = lastEvent.context?["app"]?["view_names"] as? [String]
XCTAssertEqual(viewName?.first, "TransactionScreen")
}

func testCaptureTransactionWithChangeScreen() throws {
SentryDependencyContainer.sharedInstance().application = TestSentryUIApplication()
let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Operation"), hub: nil)
Expand Down

0 comments on commit 6956d43

Please sign in to comment.