Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PLAT-7208] Fix missing sessions when started in background state #1180

Merged
merged 1 commit into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,6 @@
01847D952644140F00ADA4C7 /* BSGInternalErrorReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSGInternalErrorReporter.m; sourceTree = "<group>"; };
01847DAB26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGInternalErrorReporterTests.m; sourceTree = "<group>"; };
01937CF9257A7B4C00F2DE31 /* Bugsnag+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bugsnag+Private.h"; sourceTree = "<group>"; };
01937D09257A7ED000F2DE31 /* BugsnagSessionTracker+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagSessionTracker+Private.h"; sourceTree = "<group>"; };
01937D11257A814D00F2DE31 /* BugsnagMetadata+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagMetadata+Private.h"; sourceTree = "<group>"; };
01937D2E257A83A900F2DE31 /* BugsnagApp+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagApp+Private.h"; sourceTree = "<group>"; };
019480C42625EE9800E833ED /* BSGAppKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGAppKit.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1688,7 +1687,6 @@
01CCAEFF25D4151C0057268D /* BugsnagLastRunInfo+Private.h */,
00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */,
00AD1F012486A17900A27979 /* BugsnagSessionTracker.m */,
01937D09257A7ED000F2DE31 /* BugsnagSessionTracker+Private.h */,
CBB0928B2519F891007698BC /* BugsnagSystemState.h */,
CBB0928A2519F891007698BC /* BugsnagSystemState.m */,
00AD1CF124869EBD00A27979 /* Breadcrumbs */,
Expand Down
23 changes: 0 additions & 23 deletions Bugsnag/BugsnagSessionTracker+Private.h

This file was deleted.

18 changes: 14 additions & 4 deletions Bugsnag/BugsnagSessionTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
#import "BugsnagSession.h"
#import "BugsnagConfiguration.h"

NS_ASSUME_NONNULL_BEGIN

@class BugsnagSessionTrackingApiClient;

typedef void (^SessionTrackerCallback)(BugsnagSession *newSession);
typedef void (^SessionTrackerCallback)(BugsnagSession *_Nullable newSession);

extern NSString *const BSGSessionUpdateNotification;

Expand All @@ -27,8 +29,10 @@ extern NSString *const BSGSessionUpdateNotification;
@return A new session tracker
*/
- (instancetype)initWithConfig:(BugsnagConfiguration *)config
client:(BugsnagClient *)client
postRecordCallback:(void(^)(BugsnagSession *))callback;
client:(nullable BugsnagClient *)client
postRecordCallback:(nullable void(^)(BugsnagSession *))callback;

- (void)startWithNotificationCenter:(NSNotificationCenter *)notificationCenter isInForeground:(BOOL)isInForeground;

/**
Record and send a new session
Expand Down Expand Up @@ -77,12 +81,18 @@ extern NSString *const BSGSessionUpdateNotification;
*/
- (void)incrementEventCountUnhandled:(BOOL)unhandled;

@property (copy, nonatomic) NSString *codeBundleId;

@property (nullable, nonatomic) BugsnagSession *currentSession;

/**
* Retrieves the running session, or nil if the session is stopped or has not yet been started/resumed.
*/
@property (nonatomic, strong, readonly) BugsnagSession *runningSession;
@property (nullable, readonly, nonatomic) BugsnagSession *runningSession;

- (void)addRuntimeVersionInfo:(NSString *)info
withKey:(NSString *)key;

@end

NS_ASSUME_NONNULL_END
51 changes: 50 additions & 1 deletion Bugsnag/BugsnagSessionTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2017 Bugsnag. All rights reserved.
//

#import "BugsnagSessionTracker+Private.h"
#import "BugsnagSessionTracker.h"

#import "BSG_KSSystemInfo.h"
#import "BugsnagApp+Private.h"
Expand All @@ -21,6 +21,12 @@
#import "BugsnagSessionTrackingPayload.h"
#import "BSGFileLocations.h"

#if TARGET_OS_IOS || TARGET_OS_TV
#import "BSGUIKit.h"
#elif TARGET_OS_OSX
#import "BSGAppKit.h"
#endif

/**
Number of seconds in background required to make a new session
*/
Expand Down Expand Up @@ -60,6 +66,49 @@ - (instancetype)initWithConfig:(BugsnagConfiguration *)config
return self;
}

- (void)startWithNotificationCenter:(NSNotificationCenter *)notificationCenter isInForeground:(BOOL)isInForeground {
if (isInForeground) {
[self startNewSessionIfAutoCaptureEnabled];
} else {
bsg_log_debug(@"Not starting session because app is not in the foreground");
}

#if TARGET_OS_IOS || TARGET_OS_TV

[notificationCenter addObserver:self
selector:@selector(handleAppForegroundEvent)
name:UIApplicationWillEnterForegroundNotification
object:nil];

[notificationCenter addObserver:self
selector:@selector(handleAppForegroundEvent)
name:UIApplicationDidBecomeActiveNotification
object:nil];

[notificationCenter addObserver:self
selector:@selector(handleAppBackgroundEvent)
name:UIApplicationDidEnterBackgroundNotification
object:nil];

#elif TARGET_OS_OSX

[notificationCenter addObserver:self
selector:@selector(handleAppForegroundEvent)
name:NSApplicationWillBecomeActiveNotification
object:nil];

[notificationCenter addObserver:self
selector:@selector(handleAppForegroundEvent)
name:NSApplicationDidBecomeActiveNotification
object:nil];

[notificationCenter addObserver:self
selector:@selector(handleAppBackgroundEvent)
name:NSApplicationDidResignActiveNotification
object:nil];
#endif
}

- (void)setCodeBundleId:(NSString *)codeBundleId {
_codeBundleId = codeBundleId;
self.apiClient.codeBundleId = codeBundleId;
Expand Down
66 changes: 20 additions & 46 deletions Bugsnag/Client/BugsnagClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
#import "BugsnagNotifier.h"
#import "BugsnagPluginClient.h"
#import "BugsnagSession+Private.h"
#import "BugsnagSessionTracker+Private.h"
#import "BugsnagSessionTracker.h"
#import "BugsnagSessionTrackingApiClient.h"
#import "BugsnagStackframe+Private.h"
#import "BugsnagStateEvent.h"
Expand Down Expand Up @@ -313,7 +313,6 @@ - (void)start {
[self.notificationBreadcrumbs start];

NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[self watchLifecycleEvents:center];

#if BSG_PLATFORM_IOS
[center addObserver:self
Expand Down Expand Up @@ -349,13 +348,18 @@ - (void)start {
object:nil];
}

[center addObserver:self
selector:@selector(applicationWillTerminate:)
#if BSG_PLATFORM_IOS || BSG_PLATFORM_TVOS
name:UIApplicationWillTerminateNotification
#elif BSG_PLATFORM_OSX
name:NSApplicationWillTerminateNotification
#endif
object:nil];

self.started = YES;

if (bsg_kscrashstate_currentState()->applicationIsInForeground) {
[self.sessionTracker startNewSessionIfAutoCaptureEnabled];
} else {
bsg_log_debug(@"Not starting session because app is not in the foreground");
}
[self.sessionTracker startWithNotificationCenter:center isInForeground:bsg_kscrashstate_currentState()->applicationIsInForeground];

// Record a "Bugsnag Loaded" message
[self addAutoBreadcrumbOfType:BSGBreadcrumbTypeState withMessage:@"Bugsnag loaded" andMetadata:nil];
Expand Down Expand Up @@ -486,6 +490,7 @@ - (void)setCodeBundleId:(NSString *)codeBundleId {
* Removes observers and listeners to prevent allocations when the app is terminated
*/
- (void)applicationWillTerminate:(__unused NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self.sessionTracker];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[BSGConnectivity stopMonitoring];

Expand All @@ -495,45 +500,6 @@ - (void)applicationWillTerminate:(__unused NSNotification *)notification {
#endif
}

- (void)watchLifecycleEvents:(NSNotificationCenter *)center {
NSNotificationName foregroundName;
NSNotificationName backgroundName;
NSNotificationName terminateName;

#if BSG_PLATFORM_IOS || BSG_PLATFORM_TVOS
foregroundName = UIApplicationWillEnterForegroundNotification;
backgroundName = UIApplicationDidEnterBackgroundNotification;
terminateName = UIApplicationWillTerminateNotification;
#elif BSG_PLATFORM_OSX
foregroundName = NSApplicationWillBecomeActiveNotification;
backgroundName = NSApplicationDidResignActiveNotification;
terminateName = NSApplicationWillTerminateNotification;
#endif

[center addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:foregroundName
object:nil];

[center addObserver:self
selector:@selector(applicationWillEnterBackground:)
name:backgroundName
object:nil];

[center addObserver:self
selector:@selector(applicationWillTerminate:)
name:terminateName
object:nil];
}

- (void)applicationWillEnterForeground:(__unused NSNotification *)notification {
[self.sessionTracker handleAppForegroundEvent];
}

- (void)applicationWillEnterBackground:(__unused NSNotification *)notification {
[self.sessionTracker handleAppBackgroundEvent];
}

- (void)thermalStateDidChange:(NSNotification *)notification API_AVAILABLE(ios(11.0), tvos(11.0)) {
NSProcessInfo *processInfo = notification.object;

Expand All @@ -556,6 +522,10 @@ - (void)thermalStateDidChange:(NSNotification *)notification API_AVAILABLE(ios(1
self.lastThermalState = processInfo.thermalState;
}

// =============================================================================
// MARK: - Session Tracking
// =============================================================================

- (void)startSession {
[self.sessionTracker startNewSession];
}
Expand All @@ -568,6 +538,10 @@ - (BOOL)resumeSession {
return [self.sessionTracker resumeSession];
}

// =============================================================================
// MARK: - Connectivity Listener
// =============================================================================

/**
* Monitor the Bugsnag endpoint to detect changes in connectivity,
* flush pending events when (re)connected and report connectivity
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Changelog
=========

## TBD

### Bug fixes

* Fix a regression where the session was not captured at launch if Bugsnag was started before
`willFinishLaunchingWithOptions` in iOS apps that do not adopt the UIScene life-cycle.
[#1180](https://github.com/bugsnag/bugsnag-cocoa/pull/1180)

## 6.12.0 (2021-09-01)

### Enhancements
Expand Down
5 changes: 0 additions & 5 deletions Tests/BugsnagClientMirrorTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ - (void)setUp {
@"appendNSErrorInfo:block:event: B40@0:8@16@?24@32",
@"appendNSErrorInfo:block:event: c40@0:8@16@?24@32",
@"applicationDidReceiveMemoryWarning: v24@0:8@16",
@"applicationWillEnterBackground: v24@0:8@16",
@"applicationWillEnterForeground: v24@0:8@16",
@"applicationWillTerminate: v24@0:8@16",
@"autoNotify B16@0:8",
@"autoNotify c16@0:8",
Expand Down Expand Up @@ -139,8 +137,6 @@ - (void)setUp {
@"setupConnectivityListener v16@0:8",
@"start v16@0:8",
@"startAppHangDetector v16@0:8",
@"startListeningForStateChangeNotification: v24@0:8@16",
@"startListeningForWorkspaceStateChangeNotifications: v24@0:8@16",
@"started B16@0:8",
@"started c16@0:8",
@"state @16@0:8",
Expand All @@ -152,7 +148,6 @@ - (void)setUp {
@"unsubscribeFromNotifications: v24@0:8@16",
@"updateAutomaticBreadcrumbDetectionSettings v16@0:8",
@"updateCrashDetectionSettings v16@0:8",
@"watchLifecycleEvents: v24@0:8@16",
@"workspaceBreadcrumbStateEvents @16@0:8",
]];

Expand Down
26 changes: 25 additions & 1 deletion Tests/BugsnagSessionTrackerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#import "BugsnagUser.h"
#import "BugsnagConfiguration+Private.h"
#import "BugsnagSession+Private.h"
#import "BugsnagSessionTracker+Private.h"
#import "BugsnagSessionTracker.h"
#import "BugsnagSessionTrackingApiClient.h"
#import "BugsnagTestConstants.h"

Expand Down Expand Up @@ -153,5 +153,29 @@ - (void)testOnSendBlockTrue {
XCTAssertNotNil(self.sessionTracker.currentSession);
}

- (void)testHandleAppForegroundEvent {
[self.sessionTracker handleAppForegroundEvent];
XCTAssertNotNil(self.sessionTracker.runningSession, @"There should be a running session after calling handleAppForegroundEvent");

NSString *sessionId = [self.sessionTracker.runningSession.id copy];
[self.sessionTracker handleAppForegroundEvent];
XCTAssertEqualObjects(self.sessionTracker.runningSession.id, sessionId, @"A new session should not be started if previous session did not end");
}

- (void)testStartInBackground {
[self.sessionTracker startWithNotificationCenter:NSNotificationCenter.defaultCenter isInForeground:NO];
XCTAssertNil(self.sessionTracker.runningSession, @"There should be no running session after starting tracker in background");
#if TARGET_OS_IOS || TARGET_OS_TV
[NSNotificationCenter.defaultCenter postNotificationName:UIApplicationDidBecomeActiveNotification object:nil];
#elif TARGET_OS_OSX
[NSNotificationCenter.defaultCenter postNotificationName:NSApplicationDidBecomeActiveNotification object:nil];
#endif
XCTAssertNotNil(self.sessionTracker.runningSession, @"There should be a running session after receiving didBecomeActiveNotification");
}

- (void)testStartInForeground {
[self.sessionTracker startWithNotificationCenter:NSNotificationCenter.defaultCenter isInForeground:YES];
XCTAssertNotNil(self.sessionTracker.runningSession, @"There should be a running session after starting tracker in foreground");
}

@end