diff --git a/Sources/Sentry/SentryUIApplication.m b/Sources/Sentry/SentryUIApplication.m index d87b5333082..c0dc9d786c7 100644 --- a/Sources/Sentry/SentryUIApplication.m +++ b/Sources/Sentry/SentryUIApplication.m @@ -1,10 +1,40 @@ #import "SentryUIApplication.h" +#import "SentryDependencyContainer.h" +#import "SentryNSNotificationCenterWrapper.h" #if SENTRY_HAS_UIKIT # import -@implementation SentryUIApplication +@implementation SentryUIApplication { + UIApplicationState appState; +} + +- (instancetype)init +{ + if (self = [super init]) { + + [SentryDependencyContainer.sharedInstance.notificationCenterWrapper + addObserver:self + selector:@selector(didEnterBackground) + name:UIApplicationDidEnterBackgroundNotification]; + + [SentryDependencyContainer.sharedInstance.notificationCenterWrapper + addObserver:self + selector:@selector(didBecomeActive) + name:UIApplicationDidBecomeActiveNotification]; + // We store the application state when the app is initialized + // and we keep track of its changes by the notifications + // this way we avoid calling sharedApplication in a background thread + appState = self.sharedApplication.applicationState; + } + return self; +} + +- (void)dealloc +{ + [SentryDependencyContainer.sharedInstance.notificationCenterWrapper removeObserver:self]; +} - (UIApplication *)sharedApplication { @@ -58,7 +88,17 @@ - (UIApplication *)sharedApplication - (UIApplicationState)applicationState { - return self.sharedApplication.applicationState; + return appState; +} + +- (void)didEnterBackground +{ + appState = UIApplicationStateBackground; +} + +- (void)didBecomeActive +{ + appState = UIApplicationStateActive; } @end diff --git a/Tests/SentryTests/Helper/TestNSNotificationCenterWrapper.swift b/Tests/SentryTests/Helper/TestNSNotificationCenterWrapper.swift index 0083abf0a17..670c8e28a9b 100644 --- a/Tests/SentryTests/Helper/TestNSNotificationCenterWrapper.swift +++ b/Tests/SentryTests/Helper/TestNSNotificationCenterWrapper.swift @@ -2,6 +2,9 @@ import Foundation import SentryTestUtils @objcMembers public class TestNSNotificationCenterWrapper: SentryNSNotificationCenterWrapper { + + var ignoreRemoveObserver = false + var addObserverInvocations = Invocations<(observer: Any, selector: Selector, name: NSNotification.Name)>() public var addObserverInvocationsCount: Int { return addObserverInvocations.count @@ -24,6 +27,8 @@ import SentryTestUtils return removeObserverInvocations.count } public override func removeObserver(_ observer: Any) { - removeObserverInvocations.record(observer) + if ignoreRemoveObserver == false { + removeObserverInvocations.record(observer) + } } } diff --git a/Tests/SentryTests/SentryUIApplicationTests.swift b/Tests/SentryTests/SentryUIApplicationTests.swift index 3cd73abd278..c0a0751c407 100644 --- a/Tests/SentryTests/SentryUIApplicationTests.swift +++ b/Tests/SentryTests/SentryUIApplicationTests.swift @@ -45,6 +45,32 @@ class SentryUIApplicationTests: XCTestCase { XCTAssertEqual(sut.windows?.count, 0) } + + @available(iOS 13.0, tvOS 13.0, *) + func test_ApplicationState() { + let notificationCenter = TestNSNotificationCenterWrapper() + notificationCenter.ignoreRemoveObserver = true + SentryDependencyContainer.sharedInstance().notificationCenterWrapper = notificationCenter + + let sut = MockSentryUIApplicationTests() + XCTAssertEqual(sut.applicationState, .active) + + notificationCenter.addObserverInvocations.invocations.forEach { (observer: Any, selector: Selector, name: NSNotification.Name) in + if name == UIApplication.didEnterBackgroundNotification { + sut.perform(selector, with: observer) + } + } + + XCTAssertEqual(sut.applicationState, .background) + + notificationCenter.addObserverInvocations.invocations.forEach { (observer: Any, selector: Selector, name: NSNotification.Name) in + if name == UIApplication.didBecomeActiveNotification { + sut.perform(selector, with: observer) + } + } + + XCTAssertEqual(sut.applicationState, .active) + } private class TestApplicationDelegate: NSObject, UIApplicationDelegate { var window: UIWindow?