diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfceaa04..42845ed48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ Changelog ## TBD +### Enhancements + +* Support disabling crash reporting after initialization by setting + `Bugsnag.configuration.autoNotify`. Previously this value was ignored after + `Bugsnag.start()` was called, but is now used to update whether crash reports + will be detected and sent. This interface can be used for crash reporting + opt-out flows. + [#410](https://github.com/bugsnag/bugsnag-cocoa/issues/410) + ### Bug fixes * Ensure UIKit APIs are not called from background threads if diff --git a/Source/BugsnagConfiguration.m b/Source/BugsnagConfiguration.m index 484237045..57554ce96 100644 --- a/Source/BugsnagConfiguration.m +++ b/Source/BugsnagConfiguration.m @@ -143,6 +143,22 @@ - (void)setReleaseStage:(NSString *)newReleaseStage { } } +@synthesize autoNotify = _autoNotify; + +- (BOOL)autoNotify { + return _autoNotify; +} + +- (void)setAutoNotify:(BOOL)shouldAutoNotify { + if (shouldAutoNotify == _autoNotify) { + return; + } + [self willChangeValueForKey:NSStringFromSelector(@selector(autoNotify))]; + _autoNotify = shouldAutoNotify; + [[Bugsnag notifier] updateCrashDetectionSettings]; + [self didChangeValueForKey:NSStringFromSelector(@selector(autoNotify))]; +} + @synthesize notifyReleaseStages = _notifyReleaseStages; - (NSArray *)notifyReleaseStages { diff --git a/Source/BugsnagNotifier.h b/Source/BugsnagNotifier.h index 049a275d2..e892015ab 100644 --- a/Source/BugsnagNotifier.h +++ b/Source/BugsnagNotifier.h @@ -121,4 +121,8 @@ */ - (void)updateAutomaticBreadcrumbDetectionSettings; +/** + * Enable or disable crash reporting based on configuration state + */ +- (void)updateCrashDetectionSettings; @end diff --git a/Source/BugsnagNotifier.m b/Source/BugsnagNotifier.m index cc0ca54b9..58ee6ba95 100644 --- a/Source/BugsnagNotifier.m +++ b/Source/BugsnagNotifier.m @@ -35,6 +35,7 @@ #import "BugsnagSessionTracker.h" #import "BSGOutOfMemoryWatchdog.h" #import "BSG_RFC3339DateTool.h" +#import "BSG_KSCrashC.h" #import "BSG_KSCrashType.h" #import "BSG_KSCrashState.h" #import "BSG_KSSystemInfo.h" @@ -804,6 +805,21 @@ - (void)lowMemoryWarning:(NSNotification *)notif { } #endif +- (void)updateCrashDetectionSettings { + if (self.configuration.autoNotify) { + // Enable all crash detection + bsg_kscrash_setHandlingCrashTypes(BSG_KSCrashTypeAll); + if (self.configuration.reportOOMs) { + [self.oomWatchdog enable]; + } + } else { + // Only enable support for notify()-based reports + bsg_kscrash_setHandlingCrashTypes(BSG_KSCrashTypeUserReported); + // autoNotify gates all unhandled report detection + [self.oomWatchdog disable]; + } +} + - (void)updateAutomaticBreadcrumbDetectionSettings { if ([self.configuration automaticallyCollectBreadcrumbs]) { for (NSString *name in [self automaticBreadcrumbStateEvents]) { diff --git a/features/config_changes_after_start.feature b/features/config_changes_after_start.feature new file mode 100644 index 000000000..0953ccd4e --- /dev/null +++ b/features/config_changes_after_start.feature @@ -0,0 +1,23 @@ +Feature: Modifying configuration settings after start() is called + In some cases, such as within opt-in/opt-out flows, the configuration + of the library should be allowed to be changed to reflect user preferences. + + Background: + Given I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa" + + Scenario: Turning on crash detection after start() + When I crash the app using "TurnOnCrashDetectionAfterStartScenario" + And I relaunch the app + And I wait for a request + Then the request is valid for the error reporting API + And the "Bugsnag-API-Key" header equals "a35a2a72bd230ac0aa0f52715bbdc6aa" + And the payload field "notifier.name" equals "iOS Bugsnag Notifier" + And the payload field "events" is an array with 1 element + And the exception "errorClass" equals "EXC_BAD_INSTRUCTION" + And the "method" of stack frame 0 equals "-[TurnOnCrashDetectionAfterStartScenario run]" + + Scenario: Turning off crash detection after start() + When I crash the app using "TurnOffCrashDetectionAfterStartScenario" + And I relaunch the app + And I wait for 10 seconds + Then I should receive 0 requests diff --git a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj index 1439a5d13..67e799657 100644 --- a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 8A840FBA21AF5C450041DBFA /* SwiftAssertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A840FB921AF5C450041DBFA /* SwiftAssertion.swift */; }; 8A98400320FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A98400220FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m */; }; 8AB1081923301FE600672818 /* ReleaseStageScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */; }; + 8AA7C2D72327DD3D002255B2 /* ConfigChangesAfterStartScenarios.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AA7C2D52327DD3D002255B2 /* ConfigChangesAfterStartScenarios.m */; }; 8AB8866420404DD30003E444 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8866320404DD30003E444 /* AppDelegate.swift */; }; 8AB8866620404DD30003E444 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8866520404DD30003E444 /* ViewController.swift */; }; 8AB8866920404DD30003E444 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8AB8866720404DD30003E444 /* Main.storyboard */; }; @@ -91,6 +92,8 @@ 8A98400120FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoSessionCustomVersionScenario.h; sourceTree = ""; }; 8A98400220FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AutoSessionCustomVersionScenario.m; sourceTree = ""; }; 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReleaseStageScenarios.swift; sourceTree = ""; }; + 8AA7C2D52327DD3D002255B2 /* ConfigChangesAfterStartScenarios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConfigChangesAfterStartScenarios.m; sourceTree = ""; }; + 8AA7C2D62327DD3D002255B2 /* ConfigChangesAfterStartScenarios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigChangesAfterStartScenarios.h; sourceTree = ""; }; 8AB8866020404DD30003E444 /* iOSTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8AB8866320404DD30003E444 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 8AB8866520404DD30003E444 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -241,6 +244,8 @@ isa = PBXGroup; children = ( 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */, + 8AA7C2D62327DD3D002255B2 /* ConfigChangesAfterStartScenarios.h */, + 8AA7C2D52327DD3D002255B2 /* ConfigChangesAfterStartScenarios.m */, 8A56EE8022E22ED80066B9DC /* OOMWillTerminateScenario.h */, 8A56EE7F22E22ED80066B9DC /* OOMWillTerminateScenario.m */, 8A14F0F42282D4AE00337B05 /* ReportBackgroundOOMsEnabledScenario.h */, @@ -487,6 +492,7 @@ 8AEFC73420F8D1BB00A78779 /* ManualSessionWithUserScenario.m in Sources */, 8A14F0F72282D4AE00337B05 /* ReportOOMsDisabledReportBackgroundOOMsEnabledScenario.m in Sources */, F4295B56219D228FAA99BC14 /* ObjCExceptionScenario.m in Sources */, + 8AA7C2D72327DD3D002255B2 /* ConfigChangesAfterStartScenarios.m in Sources */, F4295218A62E41518DC3C057 /* AccessNonObjectScenario.m in Sources */, F4295262625F84A80282E520 /* CorruptMallocScenario.m in Sources */, E7767F11221C21D90006648C /* StoppedSessionScenario.swift in Sources */, diff --git a/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.h b/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.h new file mode 100644 index 000000000..963046f86 --- /dev/null +++ b/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.h @@ -0,0 +1,9 @@ +#import +#import "Scenario.h" + + +@interface TurnOnCrashDetectionAfterStartScenario : Scenario +@end + +@interface TurnOffCrashDetectionAfterStartScenario : Scenario +@end diff --git a/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.m b/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.m new file mode 100644 index 000000000..039c8683e --- /dev/null +++ b/features/fixtures/ios-swift-cocoapods/iOSTestApp/scenarios/ConfigChangesAfterStartScenarios.m @@ -0,0 +1,30 @@ +#import "ConfigChangesAfterStartScenarios.h" + +@implementation TurnOnCrashDetectionAfterStartScenario + +- (void)startBugsnag { + self.config.shouldAutoCaptureSessions = NO; + self.config.autoNotify = NO; + [super startBugsnag]; +} + +- (void)run { + self.config.autoNotify = YES; + __builtin_trap(); +} +@end + + +@implementation TurnOffCrashDetectionAfterStartScenario + +- (void)startBugsnag { + self.config.shouldAutoCaptureSessions = NO; + [super startBugsnag]; +} + +- (void)run { + self.config.autoNotify = NO; + __builtin_trap(); +} + +@end