From debc6abfcacae93b5b77f34882d9b05b5b9565b4 Mon Sep 17 00:00:00 2001 From: Delisa Mason Date: Tue, 19 Jul 2016 11:48:04 -0700 Subject: [PATCH] Wrap automatic collection --- Source/BugsnagConfiguration.h | 7 ++ Source/BugsnagConfiguration.m | 17 ++- Source/BugsnagNotifier.h | 5 + Source/BugsnagNotifier.m | 194 ++++++++++++++++++++++++---------- 4 files changed, 167 insertions(+), 56 deletions(-) diff --git a/Source/BugsnagConfiguration.h b/Source/BugsnagConfiguration.h index fdeed4616..ddb30065c 100644 --- a/Source/BugsnagConfiguration.h +++ b/Source/BugsnagConfiguration.h @@ -90,6 +90,7 @@ typedef NSDictionary *_Nullable (^BugsnagBeforeNotifyHook)( * The version of the application */ @property(nonatomic, readwrite, retain, nullable) NSString *appVersion; + /** * Additional information about the state of the app or environment at the * time the report was generated @@ -104,6 +105,12 @@ typedef NSDictionary *_Nullable (^BugsnagBeforeNotifyHook)( */ @property(nonatomic, readonly, strong, nullable) BugsnagBreadcrumbs *breadcrumbs; + +/** + * Whether to allow collection of automatic breadcrumbs for notable events + */ +@property(nonatomic, readwrite) BOOL automaticallyCollectBreadcrumbs; + /** * Hooks for modifying crash reports before it is sent to Bugsnag */ diff --git a/Source/BugsnagConfiguration.m b/Source/BugsnagConfiguration.m index e59997bfb..bfae933e6 100644 --- a/Source/BugsnagConfiguration.m +++ b/Source/BugsnagConfiguration.m @@ -29,6 +29,12 @@ #import "BugsnagBreadcrumb.h" #import "BugsnagConfiguration.h" #import "BugsnagMetaData.h" +#import "Bugsnag.h" +#import "BugsnagNotifier.h" + +@interface Bugsnag () ++ (BugsnagNotifier*)notifier; +@end @interface BugsnagConfiguration () @property(nonatomic, readwrite, strong) NSMutableArray *beforeNotifyHooks; @@ -42,12 +48,13 @@ - (id)init { _metaData = [[BugsnagMetaData alloc] init]; _config = [[BugsnagMetaData alloc] init]; _apiKey = @""; - _autoNotify = true; + _autoNotify = YES; _notifyURL = [NSURL URLWithString:@"https://notify.bugsnag.com/"]; _beforeNotifyHooks = [NSMutableArray new]; _BugsnagBeforeSendBlock = [NSMutableArray new]; _notifyReleaseStages = nil; _breadcrumbs = [BugsnagBreadcrumbs new]; + _automaticallyCollectBreadcrumbs = YES; #if DEBUG _releaseStage = @"development"; #else @@ -96,6 +103,14 @@ - (void)setNotifyReleaseStages:(NSArray *)newNotifyReleaseStages; toTabWithName:@"config"]; } +- (void)setAutomaticallyCollectBreadcrumbs:(BOOL)automaticallyCollectBreadcrumbs { + if (automaticallyCollectBreadcrumbs == _automaticallyCollectBreadcrumbs) + return; + + _automaticallyCollectBreadcrumbs = automaticallyCollectBreadcrumbs; + [[Bugsnag notifier] updateAutomaticBreadcrumbDetectionSettings]; +} + - (void)setContext:(NSString *)newContext { _context = newContext; [self.config addAttribute:@"context" diff --git a/Source/BugsnagNotifier.h b/Source/BugsnagNotifier.h index 70002f802..305e1a65d 100644 --- a/Source/BugsnagNotifier.h +++ b/Source/BugsnagNotifier.h @@ -81,4 +81,9 @@ * @param notificationName name of the notification */ - (void)crumbleNotification:(NSString *_Nonnull)notificationName; + +/** + * Enable or disable automatic breadcrumb collection based on configuration + */ +- (void)updateAutomaticBreadcrumbDetectionSettings; @end diff --git a/Source/BugsnagNotifier.m b/Source/BugsnagNotifier.m index 878224684..f14e27005 100644 --- a/Source/BugsnagNotifier.m +++ b/Source/BugsnagNotifier.m @@ -36,6 +36,8 @@ #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE #import #include +#elif TARGET_OS_MAC +#import #endif NSString *const NOTIFIER_VERSION = @"5.3.0"; @@ -150,7 +152,7 @@ - (void) start { } [self performSelectorInBackground:@selector(sendPendingReports) withObject:nil]; - + [self updateAutomaticBreadcrumbDetectionSettings]; #if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE [self.details setValue:@"iOS Bugsnag Notifier" forKey:@"name"]; @@ -174,23 +176,6 @@ - (void) start { selector:@selector(lowMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; - [self crumbleNotification:UIWindowDidBecomeHiddenNotification]; - [self crumbleNotification:UIWindowDidBecomeVisibleNotification]; - [self crumbleNotification:UIApplicationWillTerminateNotification]; - [self crumbleNotification:UIApplicationWillEnterForegroundNotification]; - [self crumbleNotification:UIApplicationDidEnterBackgroundNotification]; - [self crumbleNotification:UIApplicationUserDidTakeScreenshotNotification]; - [self crumbleNotification:UIKeyboardDidShowNotification]; - [self crumbleNotification:UIKeyboardDidHideNotification]; - [self crumbleNotification:UITextFieldTextDidBeginEditingNotification]; - [self crumbleNotification:UITextViewTextDidBeginEditingNotification]; - [self crumbleNotification:UITextFieldTextDidEndEditingNotification]; - [self crumbleNotification:UITextViewTextDidEndEditingNotification]; - [self crumbleNotification:UIMenuControllerDidShowMenuNotification]; - [self crumbleNotification:UIMenuControllerDidHideMenuNotification]; - [self crumbleNotification:NSUndoManagerDidUndoChangeNotification]; - [self crumbleNotification:NSUndoManagerDidRedoChangeNotification]; - [self crumbleNotification:UITableViewSelectionDidChangeNotification]; [UIDevice currentDevice].batteryMonitoringEnabled = YES; [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; @@ -199,26 +184,6 @@ - (void) start { [self orientationChanged:nil]; #elif TARGET_OS_MAC [self.details setValue:@"OSX Bugsnag Notifier" forKey:@"name"]; - [self crumbleNotification:NSApplicationDidBecomeActiveNotification]; - [self crumbleNotification:NSApplicationDidResignActiveNotification]; - [self crumbleNotification:NSApplicationDidHideNotification]; - [self crumbleNotification:NSApplicationDidUnhideNotification]; - [self crumbleNotification:NSApplicationWillTerminateNotification]; - [self crumbleNotification:NSWorkspaceScreensDidSleepNotification]; - [self crumbleNotification:NSWorkspaceScreensDidWakeNotification]; - [self crumbleNotification:NSWindowWillCloseNotification]; - [self crumbleNotification:NSWindowDidBecomeKeyNotification]; - [self crumbleNotification:NSWindowWillMiniaturizeNotification]; - [self crumbleNotification:NSWindowDidEnterFullScreenNotification]; - [self crumbleNotification:NSWindowDidExitFullScreenNotification]; - [self crumbleNotification:NSControlTextDidBeginEditingNotification]; - [self crumbleNotification:NSControlTextDidEndEditingNotification]; - [self crumbleNotification:NSTableViewSelectionDidChangeNotification]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(didReceiveMenuAction:) - name:NSMenuWillSendActionNotification - object:nil]; #endif } @@ -358,11 +323,13 @@ - (void)orientationChanged:(NSNotification *)notif { [[self state] addAttribute:@"orientation" withValue:orientation toTabWithName:@"deviceState"]; - [Bugsnag leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { - breadcrumb.type = BSGBreadcrumbTypeState; - breadcrumb.name = [self breadcrumbNameForNotificationName:notif.name]; - breadcrumb.metadata = @{ @"orientation" : orientation }; - }]; + if ([self.configuration automaticallyCollectBreadcrumbs]) { + [Bugsnag leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { + breadcrumb.type = BSGBreadcrumbTypeState; + breadcrumb.name = [self breadcrumbNameForNotificationName:notif.name]; + breadcrumb.metadata = @{ @"orientation" : orientation }; + }]; + } } - (void)lowMemoryWarning:(NSNotification *)notif { @@ -370,23 +337,100 @@ - (void)lowMemoryWarning:(NSNotification *)notif { withValue:[[Bugsnag payloadDateFormatter] stringFromDate:[NSDate date]] toTabWithName:@"deviceState"]; - [self sendBreadcrumbForNotification:notif]; + if ([self.configuration automaticallyCollectBreadcrumbs]) { + [self sendBreadcrumbForNotification:notif]; + } +} +#endif + +- (void)updateAutomaticBreadcrumbDetectionSettings { + if ([self.configuration automaticallyCollectBreadcrumbs]) { + for (NSString *name in [self automaticBreadcrumbStateEvents]) { + [self crumbleNotification:name]; + } + for (NSString *name in [self automaticBreadcrumbControlEvents]) { + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(sendBreadcrumbForControlNotification:) + name:name + object:nil]; + } + for (NSString *name in [self automaticBreadcrumbMenuItemEvents]) { + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(sendBreadcrumbForMenuItemNotification:) + name:name + object:nil]; + } + } else { + NSArray* eventNames = [[[self automaticBreadcrumbStateEvents] + arrayByAddingObjectsFromArray:[self automaticBreadcrumbControlEvents]] + arrayByAddingObjectsFromArray:[self automaticBreadcrumbMenuItemEvents]]; + for (NSString *name in eventNames) { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:name + object:nil]; + } + } } +- (NSArray *)automaticBreadcrumbStateEvents { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE + return @[UIWindowDidBecomeHiddenNotification, + UIWindowDidBecomeVisibleNotification, + UIApplicationWillTerminateNotification, + UIApplicationWillEnterForegroundNotification, + UIApplicationDidEnterBackgroundNotification, + UIApplicationUserDidTakeScreenshotNotification, + UIKeyboardDidShowNotification, + UIKeyboardDidHideNotification, + UIMenuControllerDidShowMenuNotification, + UIMenuControllerDidHideMenuNotification, + NSUndoManagerDidUndoChangeNotification, + NSUndoManagerDidRedoChangeNotification, + UITableViewSelectionDidChangeNotification]; #elif TARGET_OS_MAC -- (void)didReceiveMenuAction:(NSNotification *)notif { - NSMenuItem *menuItem = [[notif userInfo] valueForKey:@"MenuItem"]; - if ([menuItem isKindOfClass:[NSMenuItem class]]) { - [Bugsnag - leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { - breadcrumb.type = BSGBreadcrumbTypeState; - breadcrumb.name = [self breadcrumbNameForNotificationName:notif.name]; - if (menuItem.title.length > 0) - breadcrumb.metadata = @{ @"action" : menuItem.title }; - }]; - } + return @[NSApplicationDidBecomeActiveNotification, + NSApplicationDidResignActiveNotification, + NSApplicationDidHideNotification, + NSApplicationDidUnhideNotification, + NSApplicationWillTerminateNotification, + NSWorkspaceScreensDidSleepNotification, + NSWorkspaceScreensDidWakeNotification, + NSWindowWillCloseNotification, + NSWindowDidBecomeKeyNotification, + NSWindowWillMiniaturizeNotification, + NSWindowDidEnterFullScreenNotification, + NSWindowDidExitFullScreenNotification, + NSTableViewSelectionDidChangeNotification]; +#else + return nil; +#endif } + +- (NSArray *)automaticBreadcrumbControlEvents { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE + return @[UITextFieldTextDidBeginEditingNotification, + UITextViewTextDidBeginEditingNotification, + UITextFieldTextDidEndEditingNotification, + UITextViewTextDidEndEditingNotification]; +#elif TARGET_OS_MAC + return @[NSControlTextDidBeginEditingNotification, + NSControlTextDidEndEditingNotification]; +#else + return nil; +#endif +} + +- (NSArray *)automaticBreadcrumbMenuItemEvents { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE + return nil; +#elif TARGET_OS_MAC + return @[NSMenuWillSendActionNotification]; +#else + return nil; #endif +} - (void)crumbleNotification:(NSString *)notificationName { [[NSNotificationCenter defaultCenter] @@ -408,6 +452,46 @@ - (void)sendBreadcrumbForNotification:(NSNotification *)note { [self serializeBreadcrumbs]; } +- (void)sendBreadcrumbForMenuItemNotification:(NSNotification *)notif { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE +#elif TARGET_OS_MAC + NSMenuItem *menuItem = [[notif userInfo] valueForKey:@"MenuItem"]; + if ([menuItem isKindOfClass:[NSMenuItem class]]) { + [Bugsnag + leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { + breadcrumb.type = BSGBreadcrumbTypeState; + breadcrumb.name = [self breadcrumbNameForNotificationName:notif.name]; + if (menuItem.title.length > 0) + breadcrumb.metadata = @{ @"action" : menuItem.title }; + }]; + } +#endif +} + +- (void)sendBreadcrumbForControlNotification:(NSNotification *)note { +#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE + UIControl* control = note.object; + [Bugsnag leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { + breadcrumb.type = BSGBreadcrumbTypeUser; + breadcrumb.name = [self breadcrumbNameForNotificationName:note.name]; + NSString *label = control.accessibilityLabel; + if (label.length > 0) { + breadcrumb.metadata = @{ @"label": label }; + } + }]; +#elif TARGET_OS_MAC + NSControl *control = note.object; + [Bugsnag leaveBreadcrumbWithBlock:^(BugsnagBreadcrumb *_Nonnull breadcrumb) { + breadcrumb.type = BSGBreadcrumbTypeUser; + breadcrumb.name = [self breadcrumbNameForNotificationName:note.name]; + NSString *label = control.accessibilityLabel; + if (label.length > 0) { + breadcrumb.metadata = @{ @"label": label }; + } + }]; +#endif +} + - (NSString *)breadcrumbNameForNotificationName:(NSString *)name { return [name stringByReplacingOccurrencesOfString:@"Notification" withString:@""];