Skip to content

Commit

Permalink
Merge eb9c28f into 728804f
Browse files Browse the repository at this point in the history
  • Loading branch information
brustolin authored Oct 31, 2023
2 parents 728804f + eb9c28f commit 2c15262
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 3 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- Add thread id and name to span data (#3359)
- Add screen name to app context (#3346)

### Fixes

Expand All @@ -15,11 +16,10 @@

### Features

- Improve OOM detection by ignoring system reboot (#3352)
- Enrich error events with any underlying NSErrors reported by Cocoa APIs (#3230)

### Features

- Improve OOM detection by ignoring system reboot (#3352)
## 8.14.2

### Fixes

Expand Down
32 changes: 32 additions & 0 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#import "SentryTransport.h"
#import "SentryTransportAdapter.h"
#import "SentryTransportFactory.h"
#import "SentryUIApplication.h"
#import "SentryUser.h"
#import "SentryUserFeedback.h"
#import "SentryWatchdogTerminationTracker.h"
Expand Down Expand Up @@ -808,9 +809,40 @@ - (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
{
if ([event isKindOfClass:[SentryTransaction class]]) {
SentryTransaction *transaction = (SentryTransaction *)event;
if ([transaction.screens count] > 0) {
appContext[@"view_names"] = transaction.screens;
}
}

if (appContext[@"view_names"] == nil) {
void (^addViewName)(void) = ^{
NSArray *viewControllers
= SentryDependencyContainer.sharedInstance.application.relevantViewControllers;
NSMutableArray *vcsNames = [NSMutableArray array];
for (id vc in viewControllers) {
[vcsNames addObject:[SwiftDescriptor getObjectClassName:vc]];
}
appContext[@"view_names"] = vcsNames;
};

[[SentryDependencyContainer.sharedInstance dispatchQueueWrapper]
dispatchSyncOnMainQueue:addViewName
timeout:0.001];
}
}
#endif // SENTRY_HAS_UIKIT

- (void)removeExtraDeviceContextFromEvent:(SentryEvent *)event
{
[self modifyContext:event
Expand Down
19 changes: 19 additions & 0 deletions Sources/Sentry/SentryDispatchQueueWrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ - (void)dispatchSyncOnMainQueue:(void (^)(void))block
}
}

- (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout
{
if ([NSThread isMainThread]) {
block();
} else {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

dispatch_async(dispatch_get_main_queue(), ^{
block();
dispatch_semaphore_signal(semaphore);
});

dispatch_time_t timeout_t
= dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
return dispatch_semaphore_wait(semaphore, timeout_t) == 0;
}
return YES;
}

- (void)dispatchAfter:(NSTimeInterval)interval block:(dispatch_block_t)block
{
dispatch_time_t delta = (int64_t)(interval * NSEC_PER_SEC);
Expand Down
35 changes: 35 additions & 0 deletions Sources/Sentry/SentryTracer.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
#import "SentryTracer+Private.h"
#import "SentryTransaction.h"
#import "SentryTransactionContext.h"
#import "SentryUIApplication.h"
#import <NSMutableDictionary+Sentry.h>
#import <SentryDispatchQueueWrapper.h>
#import <SentryMeasurementValue.h>
#import <SentrySpanOperations.h>
@import SentryPrivate;

#if SENTRY_TARGET_PROFILING_SUPPORTED
# import "SentryProfiledTracerConcurrency.h"
Expand Down Expand Up @@ -93,6 +95,7 @@ @implementation SentryTracer {
NSUInteger initTotalFrames;
NSUInteger initSlowFrames;
NSUInteger initFrozenFrames;
NSMutableArray<NSString *> *viewNames;
#endif // SENTRY_HAS_UIKIT
}

Expand Down Expand Up @@ -138,6 +141,7 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti

#if SENTRY_HAS_UIKIT
appStartMeasurement = [self getAppStartMeasurement];
[self getCurrentScreen];
#endif // SENTRY_HAS_UIKIT

_idleTimeoutLock = [[NSObject alloc] init];
Expand Down Expand Up @@ -384,6 +388,33 @@ - (SentryTraceContext *)traceContext
return _traceContext;
}

#if SENTRY_HAS_UIKIT
- (void)getCurrentScreen
{
__weak SentryTracer *weakSelf = self;
void (^saveViewNames)(void) = ^{
NSArray *vcs = SentryDependencyContainer.sharedInstance.application.relevantViewControllers;
NSMutableArray *vcsNames = [NSMutableArray array];
for (UIViewController *vc in vcs) {
[vcsNames addObject:[SwiftDescriptor getObjectClassName:vc]];
}

// Since viewNames is a field, we can't access a field from a weak reference that could be
// dereferenced. Thats why we need this auxiliary variable.
SentryTracer *_self = weakSelf;
if (_self) {
_self->viewNames = vcsNames;
}
};

// We need to retrieve the current view controller on the main thread.
// If the transaction is called from a background thread and finishes
// before retrieving the current view controller, the event will not have this information.
[SentryDependencyContainer.sharedInstance.dispatchQueueWrapper
dispatchAsyncOnMainQueue:saveViewNames];
}
#endif

- (NSArray<id<SentrySpan>> *)children
{
return [_children copy];
Expand Down Expand Up @@ -641,6 +672,10 @@ - (SentryTransaction *)toTransaction

#if SENTRY_HAS_UIKIT
[self addMeasurements:transaction];

if ([viewNames count] > 0) {
transaction.screens = viewNames;
}
#endif // SENTRY_HAS_UIKIT

return transaction;
Expand Down
110 changes: 110 additions & 0 deletions Sources/Sentry/SentryUIApplication.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,116 @@ - (UIApplication *)sharedApplication
return result;
}

- (NSArray<UIViewController *> *)relevantViewControllers
{
NSArray<UIWindow *> *windows = [self windows];
if ([windows count] == 0) {
return nil;
}

NSMutableArray *result = [NSMutableArray array];

for (UIWindow *window in windows) {
NSArray<UIViewController *> *vcs = [self relevantViewControllerFromWindow:window];
if (vcs != nil) {
[result addObjectsFromArray:vcs];
}
}

return result;
}

- (NSArray<UIViewController *> *)relevantViewControllerFromWindow:(UIWindow *)window
{
UIViewController *rootViewController = window.rootViewController;
if (rootViewController == nil) {
return nil;
}

NSMutableArray<UIViewController *> *result =
[NSMutableArray<UIViewController *> arrayWithObject:rootViewController];
NSUInteger index = 0;

while (index < result.count) {
UIViewController *topVC = result[index];
// If the view controller is presenting another one, usually in a modal form.
if (topVC.presentedViewController != nil) {

if ([topVC.presentationController isKindOfClass:UIAlertController.class]) {
//If the view controller being presented is an Alert, we know that
//we reached the end of the view controller stack and the presenter is
//the top view controller.
break;
}

[result replaceObjectAtIndex:index withObject:topVC.presentedViewController];

continue;
}

// The top view controller is meant for navigation and not content
if ([self isContainerViewController:topVC]) {
[result removeObjectAtIndex:index];
[result addObjectsFromArray:[self relevantViewControllerFromContainer:topVC]];
continue;
}

UIViewController *relevantChild = nil;
for (UIViewController *childVC in topVC.childViewControllers) {
// Sometimes a view controller is used as container for a navigation controller
// If the navigation is occupying the whole view controller we will consider this the
// case.
if ([self isContainerViewController:childVC]
&& CGRectEqualToRect(childVC.view.frame, topVC.view.bounds)) {
relevantChild = childVC;
break;
}
}

if (relevantChild != nil) {
[result replaceObjectAtIndex:index withObject:topVC];
continue;
}

index++;
}

return result;
}

- (BOOL)isContainerViewController:(UIViewController *)viewController
{
return [viewController isKindOfClass:UINavigationController.class] ||
[viewController isKindOfClass:UITabBarController.class] ||
[viewController isKindOfClass:UISplitViewController.class] ||
[viewController isKindOfClass:UIPageViewController.class];
}

- (nullable NSArray<UIViewController *> *)relevantViewControllerFromContainer:
(UIViewController *)containerVC
{
if ([containerVC isKindOfClass:UINavigationController.class]) {
return @[ [(UINavigationController *)containerVC topViewController] ];
}
if ([containerVC isKindOfClass:UITabBarController.class]) {
UITabBarController *tbController = (UITabBarController *)containerVC;
return @[ [tbController.viewControllers objectAtIndex:tbController.selectedIndex] ];
}
if ([containerVC isKindOfClass:UISplitViewController.class]) {
UISplitViewController *splitVC = (UISplitViewController *)containerVC;
if (splitVC.viewControllers.count > 1) {
return [splitVC viewControllers];
}
}
if ([containerVC isKindOfClass:UIPageViewController.class]) {
UIPageViewController *pageVC = (UIPageViewController *)containerVC;
if (pageVC.viewControllers.count > 0) {
return @[ [[pageVC viewControllers] objectAtIndex:0] ];
}
}
return nil;
}

- (UIApplicationState)applicationState
{
return self.sharedApplication.applicationState;
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryDispatchQueueWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ NS_ASSUME_NONNULL_BEGIN

- (void)dispatchSyncOnMainQueue:(void (^)(void))block;

- (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout;

- (void)dispatchAfter:(NSTimeInterval)interval block:(dispatch_block_t)block;

- (void)dispatchCancel:(dispatch_block_t)block;
Expand Down
1 change: 1 addition & 0 deletions Sources/Sentry/include/SentryTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ NS_SWIFT_NAME(Transaction)
SENTRY_NO_INIT

@property (nonatomic, strong) SentryTracer *trace;
@property (nonatomic, copy, nullable) NSArray<NSString *> *screens;

- (instancetype)initWithTrace:(SentryTracer *)trace children:(NSArray<id<SentrySpan>> *)children;

Expand Down
10 changes: 10 additions & 0 deletions Sources/Sentry/include/SentryUIApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@class UIApplication;
@class UIScene;
@class UIWindow;
@class UIViewController;
@protocol UIApplicationDelegate;

typedef NS_ENUM(NSInteger, UIApplicationState);
Expand Down Expand Up @@ -41,6 +42,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSArray<UIScene *> *)getApplicationConnectedScenes:(UIApplication *)application
API_AVAILABLE(ios(13.0), tvos(13.0));

/**
* Retrieve the topmost view controllers currently presented in the app.
* In the case of a multi-screen iPad app, it will contain the most relevant
* view controller for each screen. If the topmost view controller
* is a split view controller, all of its sides are reported.
*/
- (nullable NSArray<UIViewController *> *)relevantViewControllers;

@end

NS_ASSUME_NONNULL_END
Expand Down
Loading

0 comments on commit 2c15262

Please sign in to comment.