Skip to content

Commit

Permalink
Merge 51f6f0e into 1ef804d
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinrenskers authored Nov 29, 2022
2 parents 1ef804d + 51f6f0e commit 49fd801
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 46 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ This version adds a dependency on Swift.

### Fixes

- Errors shortly after SentrySDK.init now affect the session (#2430)
- Errors shortly after `SentrySDK.init` now affect the session (#2430)
- Use the same default environment for events and sessions (#2447)
- `SentryAppStateManager` correctly unsubscribes from `NSNotificationCenter` when closing the SDK (#2460)

### Breaking Changes

Expand Down
60 changes: 32 additions & 28 deletions Sources/Sentry/SentryAppStateManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import <SentryCurrentDateProvider.h>
#import <SentryDispatchQueueWrapper.h>
#import <SentryFileManager.h>
#import <SentryNSNotificationCenterWrapper.h>
#import <SentryOptions.h>

#if SENTRY_HAS_UIKIT
Expand All @@ -24,6 +25,7 @@
@property (nonatomic, strong) id<SentryCurrentDateProvider> currentDate;
@property (nonatomic, strong) SentrySysctl *sysctl;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
@property (nonatomic, strong) SentryNSNotificationCenterWrapper *notificationCenterWrapper;
@property (nonatomic) NSInteger startCount;

@end
Expand All @@ -36,6 +38,7 @@ - (instancetype)initWithOptions:(SentryOptions *)options
currentDateProvider:(id<SentryCurrentDateProvider>)currentDateProvider
sysctl:(SentrySysctl *)sysctl
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
notificationCenterWrapper:(SentryNSNotificationCenterWrapper *)notificationCenterWrapper
{
if (self = [super init]) {
self.options = options;
Expand All @@ -44,6 +47,7 @@ - (instancetype)initWithOptions:(SentryOptions *)options
self.currentDate = currentDateProvider;
self.sysctl = sysctl;
self.dispatchQueue = dispatchQueueWrapper;
self.notificationCenterWrapper = notificationCenterWrapper;
self.startCount = 0;
}
return self;
Expand All @@ -54,29 +58,24 @@ - (instancetype)initWithOptions:(SentryOptions *)options
- (void)start
{
if (self.startCount == 0) {
[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
addObserver:self
selector:@selector(didBecomeActive)
name:SentryNSNotificationCenterWrapper.didBecomeActiveNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.didBecomeActiveNotificationName];

[NSNotificationCenter.defaultCenter
addObserver:self
selector:@selector(didBecomeActive)
name:SentryHybridSdkDidBecomeActiveNotificationName
object:nil];
[self.notificationCenterWrapper addObserver:self
selector:@selector(didBecomeActive)
name:SentryHybridSdkDidBecomeActiveNotificationName];

[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
addObserver:self
selector:@selector(willResignActive)
name:SentryNSNotificationCenterWrapper.willResignActiveNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.willResignActiveNotificationName];

[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
addObserver:self
selector:@selector(willTerminate)
name:SentryNSNotificationCenterWrapper.willTerminateNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.willTerminateNotificationName];

[self storeCurrentAppState];
}
Expand All @@ -85,43 +84,48 @@ - (void)start
}

- (void)stop
{
[self stop:NO];
}

- (void)stop:(BOOL)forceStop
{
if (self.startCount <= 0) {
return;
}

self.startCount -= 1;
if (forceStop) {
self.startCount = 0;
} else {
self.startCount -= 1;
}

if (self.startCount == 0) {
// Remove the observers with the most specific detail possible, see
// https://developer.apple.com/documentation/foundation/nsnotificationcenter/1413994-removeobserver
[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
removeObserver:self
name:SentryNSNotificationCenterWrapper.didBecomeActiveNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.didBecomeActiveNotificationName];

[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
removeObserver:self
name:SentryHybridSdkDidBecomeActiveNotificationName
object:nil];
name:SentryHybridSdkDidBecomeActiveNotificationName];

[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
removeObserver:self
name:SentryNSNotificationCenterWrapper.willResignActiveNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.willResignActiveNotificationName];

[NSNotificationCenter.defaultCenter
[self.notificationCenterWrapper
removeObserver:self
name:SentryNSNotificationCenterWrapper.willTerminateNotificationName
object:nil];
name:SentryNSNotificationCenterWrapper.willTerminateNotificationName];
}
}

- (void)dealloc
{
// In dealloc it's safe to unsubscribe for all, see
// https://developer.apple.com/documentation/foundation/nsnotificationcenter/1413994-removeobserver
[NSNotificationCenter.defaultCenter removeObserver:self];
[self.notificationCenterWrapper removeObserver:self];
}

/**
Expand Down
13 changes: 7 additions & 6 deletions Sources/Sentry/SentryDependencyContainer.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@ - (SentryAppStateManager *)appStateManager
if (_appStateManager == nil) {
SentryOptions *options = [[[SentrySDK currentHub] getClient] options];
_appStateManager = [[SentryAppStateManager alloc]
initWithOptions:options
crashWrapper:self.crashWrapper
fileManager:self.fileManager
currentDateProvider:[SentryDefaultCurrentDateProvider sharedInstance]
sysctl:[[SentrySysctl alloc] init]
dispatchQueueWrapper:self.dispatchQueueWrapper];
initWithOptions:options
crashWrapper:self.crashWrapper
fileManager:self.fileManager
currentDateProvider:[SentryDefaultCurrentDateProvider sharedInstance]
sysctl:[[SentrySysctl alloc] init]
dispatchQueueWrapper:self.dispatchQueueWrapper
notificationCenterWrapper:self.notificationCenterWrapper];
}
return _appStateManager;
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/Sentry/SentrySDK.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "SentrySDK.h"
#import "PrivateSentrySDKOnly.h"
#import "SentryAppStartMeasurement.h"
#import "SentryAppStateManager.h"
#import "SentryBreadcrumb.h"
#import "SentryClient+Private.h"
#import "SentryCrash.h"
Expand Down Expand Up @@ -403,6 +404,12 @@ + (void)close
}
[hub removeAllIntegrations];

#if SENTRY_HAS_UIKIT
// force the AppStateManager to unsubscribe, see
// https://github.com/getsentry/sentry-cocoa/issues/2455
[[SentryDependencyContainer sharedInstance].appStateManager stop:YES];
#endif

// close the client
SentryClient *client = [hub getClient];
client.options.enabled = NO;
Expand Down
8 changes: 6 additions & 2 deletions Sources/Sentry/include/SentryAppStateManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@
#import "SentryDefines.h"

@class SentryOptions, SentryCrashWrapper, SentryAppState, SentryFileManager, SentrySysctl,
SentryDispatchQueueWrapper;
SentryDispatchQueueWrapper, SentryNSNotificationCenterWrapper;

NS_ASSUME_NONNULL_BEGIN

@interface SentryAppStateManager : NSObject
SENTRY_NO_INIT

@property (nonatomic, readonly) NSInteger startCount;

- (instancetype)initWithOptions:(SentryOptions *)options
crashWrapper:(SentryCrashWrapper *)crashWrapper
fileManager:(SentryFileManager *)fileManager
currentDateProvider:(id<SentryCurrentDateProvider>)currentDateProvider
sysctl:(SentrySysctl *)sysctl
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper;
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
notificationCenterWrapper:(SentryNSNotificationCenterWrapper *)notificationCenterWrapper;

#if SENTRY_HAS_UIKIT

- (void)start;
- (void)stop;
- (void)stop:(BOOL)forceStop;

/**
* Builds the current app state.
Expand Down
20 changes: 19 additions & 1 deletion Tests/SentryTests/Helper/SentryAppStateManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class SentryAppStateManagerTests: XCTestCase {
let fileManager: SentryFileManager
let currentDate = TestCurrentDateProvider()
let dispatchQueue = TestSentryDispatchQueueWrapper()
let notificationCenterWrapper = TestNSNotificationCenterWrapper()

init() {
options = Options()
Expand All @@ -27,7 +28,8 @@ class SentryAppStateManagerTests: XCTestCase {
fileManager: fileManager,
currentDateProvider: currentDate,
sysctl: TestSysctl(),
dispatchQueueWrapper: TestSentryDispatchQueueWrapper()
dispatchQueueWrapper: TestSentryDispatchQueueWrapper(),
notificationCenterWrapper: notificationCenterWrapper
)
}
}
Expand Down Expand Up @@ -76,6 +78,22 @@ class SentryAppStateManagerTests: XCTestCase {
XCTAssertNotNil(fixture.fileManager.readAppState())
}

func testForcedStop() {
XCTAssertNil(fixture.fileManager.readAppState())

sut.start()
sut.start()
sut.start()

sut.stop()
XCTAssertEqual(sut.startCount, 2)

sut.stop(true)
XCTAssertEqual(sut.startCount, 0)

XCTAssertEqual(fixture.notificationCenterWrapper.removeObserverWithNameInvocations.count, 4)
}

func testUpdateAppState() {
sut.storeCurrentAppState()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ class TestNSNotificationCenterWrapper: SentryNSNotificationCenterWrapper {
addObserverInvocations.record((observer, aSelector, aName))
}

var addObserverWithNotificationInvocations = Invocations<(observer: Any, name: NSNotification.Name)>()
var removeObserverWithNameInvocations = Invocations<(observer: Any, name: NSNotification.Name)>()
override func removeObserver(_ observer: Any, name aName: NSNotification.Name) {
addObserverWithNotificationInvocations.record((observer, aName))
removeObserverWithNameInvocations.record((observer, aName))
}

var removeObserverInvocations = Invocations<Any>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,27 @@ class SentryOutOfMemoryTrackerTests: NotificationCenterTestCase {
}

func getSut(fileManager: SentryFileManager) -> SentryOutOfMemoryTracker {
let appStateManager = SentryAppStateManager(options: options, crashWrapper: crashWrapper, fileManager: fileManager, currentDateProvider: currentDate, sysctl: sysctl, dispatchQueueWrapper: self.dispatchQueue)
let logic = SentryOutOfMemoryLogic(options: options, crashAdapter: crashWrapper, appStateManager: appStateManager)
return SentryOutOfMemoryTracker(options: options, outOfMemoryLogic: logic, appStateManager: appStateManager, dispatchQueueWrapper: dispatchQueue, fileManager: fileManager)
let appStateManager = SentryAppStateManager(
options: options,
crashWrapper: crashWrapper,
fileManager: fileManager,
currentDateProvider: currentDate,
sysctl: sysctl,
dispatchQueueWrapper: self.dispatchQueue,
notificationCenterWrapper: SentryNSNotificationCenterWrapper()
)
let logic = SentryOutOfMemoryLogic(
options: options,
crashAdapter: crashWrapper,
appStateManager: appStateManager
)
return SentryOutOfMemoryTracker(
options: options,
outOfMemoryLogic: logic,
appStateManager: appStateManager,
dispatchQueueWrapper: dispatchQueue,
fileManager: fileManager
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,29 @@ class SentryAppStartTrackerTests: NotificationCenterTestCase {

fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate, dispatchQueueWrapper: dispatchQueue)

appStateManager = SentryAppStateManager(options: options, crashWrapper: crashWrapper, fileManager: fileManager, currentDateProvider: currentDate, sysctl: sysctl, dispatchQueueWrapper: dispatchQueue)
appStateManager = SentryAppStateManager(
options: options,
crashWrapper: crashWrapper,
fileManager: fileManager,
currentDateProvider: currentDate,
sysctl: sysctl,
dispatchQueueWrapper: dispatchQueue,
notificationCenterWrapper: SentryNSNotificationCenterWrapper()
)

runtimeInitTimestamp = currentDate.date().addingTimeInterval(0.2)
moduleInitializationTimestamp = currentDate.date().addingTimeInterval(0.1)
didFinishLaunchingTimestamp = currentDate.date().addingTimeInterval(0.3)
}

var sut: SentryAppStartTracker {
let sut = SentryAppStartTracker(currentDateProvider: currentDate, dispatchQueueWrapper: TestSentryDispatchQueueWrapper(), appStateManager: appStateManager, sysctl: sysctl, enablePreWarmedAppStartTracing: enablePreWarmedAppStartTracing)
let sut = SentryAppStartTracker(
currentDateProvider: currentDate,
dispatchQueueWrapper: TestSentryDispatchQueueWrapper(),
appStateManager: appStateManager,
sysctl: sysctl,
enablePreWarmedAppStartTracing: enablePreWarmedAppStartTracing
)
return sut
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ class SentrySessionTrackerTests: XCTestCase {
func testStop_RemovesObservers() {
sut.stop()

let invocations = fixture.notificationCenter.addObserverWithNotificationInvocations
let invocations = fixture.notificationCenter.removeObserverWithNameInvocations
let notificationNames = invocations.invocations.map { $0.name }

assertNotificationNames(notificationNames)
Expand Down
23 changes: 23 additions & 0 deletions Tests/SentryTests/SentrySDKTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,29 @@ class SentrySDKTests: XCTestCase {
XCTAssertEqual(0, hub.installedIntegrations.count)
assertIntegrationsInstalled(integrations: [])
}

#if SENTRY_HAS_UIKIT
func testClose_StopsAppStateManager() {
SentrySDK.start { options in
options.dsn = SentrySDKTests.dsnAsString
options.tracesSampleRate = 1
}

let appStateManager = SentryDependencyContainer.sharedInstance().appStateManager
XCTAssertEqual(appStateManager.startCount, 1)

SentrySDK.start { options in
options.dsn = SentrySDKTests.dsnAsString
options.tracesSampleRate = 1
}

XCTAssertEqual(appStateManager.startCount, 2)

SentrySDK.close()

XCTAssertEqual(appStateManager.startCount, 0)
}
#endif

func testFlush_CallsFlushCorrectlyOnTransport() {
SentrySDK.start { options in
Expand Down

0 comments on commit 49fd801

Please sign in to comment.