Skip to content

Commit

Permalink
Fix currentAppState when called before UIApplicationMain (#1248)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickdowell authored Dec 7, 2021
1 parent 0f7291b commit 185098b
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Bugsnag.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@
01847DAD26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DAB26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m */; };
01847DAE26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DAB26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m */; };
0187D464255BD7B800C503D9 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; };
01935AE2275E68E9007498B3 /* UIApplicationStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 01935AE1275E68E9007498B3 /* UIApplicationStub.m */; };
01935AE3275E68E9007498B3 /* UIApplicationStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 01935AE1275E68E9007498B3 /* UIApplicationStub.m */; };
019480D42625F3EB00E833ED /* BSGAppKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 019480D32625F3EB00E833ED /* BSGAppKitTests.m */; };
01A2C542271EB9B400A27B23 /* BSG_Symbolicate.c in Sources */ = {isa = PBXBuildFile; fileRef = 01A2C540271EB9B300A27B23 /* BSG_Symbolicate.c */; };
01A2C543271EB9B400A27B23 /* BSG_Symbolicate.c in Sources */ = {isa = PBXBuildFile; fileRef = 01A2C540271EB9B300A27B23 /* BSG_Symbolicate.c */; };
Expand Down Expand Up @@ -1336,6 +1338,8 @@
01847D942644140F00ADA4C7 /* BSGInternalErrorReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSGInternalErrorReporter.h; sourceTree = "<group>"; };
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>"; };
01935AE0275E68E9007498B3 /* UIApplicationStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIApplicationStub.h; sourceTree = "<group>"; };
01935AE1275E68E9007498B3 /* UIApplicationStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIApplicationStub.m; sourceTree = "<group>"; };
01937CF9257A7B4C00F2DE31 /* Bugsnag+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bugsnag+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>"; };
Expand Down Expand Up @@ -1795,6 +1799,8 @@
004E35392487B375007FBAE4 /* Tests-Bridging-Header.h */,
CBA22499251E429C00B87416 /* TestSupport.h */,
CBA2249A251E429C00B87416 /* TestSupport.m */,
01935AE0275E68E9007498B3 /* UIApplicationStub.h */,
01935AE1275E68E9007498B3 /* UIApplicationStub.m */,
013D9CCF26C5262F0077F0AD /* UISceneStub.h */,
013D9CD026C5262F0077F0AD /* UISceneStub.m */,
01E8765C256684E700F4B70A /* URLSessionMock.h */,
Expand Down Expand Up @@ -2780,6 +2786,7 @@
008967782486D43700DC48C2 /* BSG_KSMachHeadersTests.m in Sources */,
0089673F2486D43700DC48C2 /* BugsnagAppTest.m in Sources */,
0089675A2486D43700DC48C2 /* BugsnagEnabledBreadcrumbTest.m in Sources */,
01935AE2275E68E9007498B3 /* UIApplicationStub.m in Sources */,
008967422486D43700DC48C2 /* BugsnagSessionTrackerStopTest.m in Sources */,
E701FAAF2490EFE8008D842F /* ConfigurationApiValidationTest.m in Sources */,
008967452486D43700DC48C2 /* BugsnagTests.m in Sources */,
Expand Down Expand Up @@ -3138,6 +3145,7 @@
01847DAE26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m in Sources */,
004E35372487AFF2007FBAE4 /* BugsnagHandledStateTest.m in Sources */,
016875C8258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */,
01935AE3275E68E9007498B3 /* UIApplicationStub.m in Sources */,
01C17AE92542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */,
00896A422486DBDD00DC48C2 /* BSGConfigurationBuilderTests.m in Sources */,
008967682486D43700DC48C2 /* BugsnagNotifierTest.m in Sources */,
Expand Down
24 changes: 17 additions & 7 deletions Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -453,31 +453,40 @@ + (BOOL)isRunningInAppExtension {
}

#if BSG_PLATFORM_IOS || BSG_PLATFORM_TVOS

+ (UIApplicationState)currentAppState {
// Only checked outside of app extensions since sharedApplication is
// unavailable to extension UIKit APIs
if ([self isRunningInAppExtension]) {
return UIApplicationStateActive;
}

UIApplicationState(^getState)(void) = ^() {
UIApplication * (^ getSharedApplication)(void) = ^() {
// Calling this API indirectly to avoid a compile-time check that
// [UIApplication sharedApplication] is not called from app extensions
// (which is handled above)
UIApplication *app = [UIAPPLICATION performSelector:@selector(sharedApplication)];
return [app applicationState];
return [UIAPPLICATION performSelector:@selector(sharedApplication)];
};

__block UIApplication *application = nil;
if ([[NSThread currentThread] isMainThread]) {
return getState();
application = getSharedApplication();
} else {
// [UIApplication sharedApplication] is a main thread-only API
__block UIApplicationState state;
dispatch_sync(dispatch_get_main_queue(), ^{
state = getState();
application = getSharedApplication();
});
return state;
}

// There will be no UIApplication if UIApplicationMain() has not yet been
// called. This happens if started from a SwiftUI app's init() function or
// UIKit app's main() function. Returning UIApplicationStateActive (0) would
// be higly misleading, so we must check for this condition.
if (!application) {
return UIApplicationStateBackground;
}

return application.applicationState;
}

+ (BOOL)isInForeground:(UIApplicationState)state {
Expand All @@ -494,6 +503,7 @@ + (BOOL)isInForeground:(UIApplicationState)state {
return state == UIApplicationStateInactive
|| state == UIApplicationStateActive;
}

#endif

@end
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 `UIApplicationState` detection when started from a SwiftUI app's `init()` function.
This fixes false positive OOMs on iOS 15 for apps that have been prewarmed without transitioning to the foreground.
[#1248](https://github.com/bugsnag/bugsnag-cocoa/pull/1248)

## 6.15.0 (2021-12-01)

### Enhancements
Expand Down
2 changes: 2 additions & 0 deletions Tests/BugsnagTests/BugsnagConfigurationTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ - (void)testAddOnSessionBlock {
// Call onSession blocks
BugsnagClient *client = [[BugsnagClient alloc] initWithConfiguration:config];
[client start];
[client resumeSession];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}

Expand Down Expand Up @@ -183,6 +184,7 @@ - (void)testAddOnSessionBlockThenRemove {
// Call onSession blocks
BugsnagClient *client = [[BugsnagClient alloc] initWithConfiguration:config];
[client start];
[client resumeSession];
[self waitForExpectations:@[expectation1] timeout:1.0];

// Check it's called on new session start
Expand Down
26 changes: 26 additions & 0 deletions Tests/BugsnagTests/UIApplicationStub.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// UIApplicationStub.h
// Bugsnag
//
// Created by Nick Dowell on 06/12/2021.
// Copyright © 2021 Bugsnag Inc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIApplicationStub : NSObject

@property (nonatomic) UIApplicationState applicationState;

@end

@interface XCTestCase (UIApplicationStub)

- (void)setUpUIApplicationStub;

@end

NS_ASSUME_NONNULL_END
38 changes: 38 additions & 0 deletions Tests/BugsnagTests/UIApplicationStub.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// UIApplicationStub.m
// Bugsnag
//
// Created by Nick Dowell on 06/12/2021.
// Copyright © 2021 Bugsnag Inc. All rights reserved.
//

#import "UIApplicationStub.h"

#import <objc/runtime.h>


@implementation UIApplicationStub

- (BOOL)isKindOfClass:(Class)aClass {
return aClass == [UIApplication class] || [super isKindOfClass:aClass];
}

@end


@implementation XCTestCase (UIApplicationStub)

- (void)setUpUIApplicationStub {
Method method = class_getClassMethod([UIApplication class], @selector(sharedApplication));
NSParameterAssert(method != NULL);

void *originalImplementation = method_setImplementation(method, imp_implementationWithBlock(^(){
return [[UIApplicationStub alloc] init];
}));

[self addTeardownBlock:^{
method_setImplementation(method, originalImplementation);
}];
}

@end
15 changes: 15 additions & 0 deletions Tests/KSCrashTests/KSCrashState_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,28 @@
#import "BSG_KSCrashState.h"
#import "BSG_KSCrashC.h"

#if TARGET_OS_TV
#import "UIApplicationStub.h"
#endif


@interface bsg_kscrashstate_Tests : FileBasedTestCase
@end


@implementation bsg_kscrashstate_Tests

#if TARGET_OS_TV // Not needed on iOS because there the tests are injected into a host app

- (void)setUp
{
[self setUpUIApplicationStub]; // These tests assume applicationState == .active

[super setUp];
}

#endif

- (void) testInitRelaunch
{
BSG_KSCrash_State context = {0};
Expand Down
8 changes: 7 additions & 1 deletion Tests/KSCrashTests/KSSystemInfo_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
#import "BSG_KSSystemInfo.h"
#import "BSG_KSSystemInfoC.h"

#if TARGET_OS_TV
#import "UIApplicationStub.h"
#endif


@interface KSSystemInfo_Tests : XCTestCase @end

Expand Down Expand Up @@ -61,7 +65,9 @@ - (void) testCopyProcessName

#if BSG_PLATFORM_TVOS || BSG_PLATFORM_IOS
- (void)testCurrentAppState {
// Should default to active as tests aren't in an app bundle
#if TARGET_OS_TV
[self setUpUIApplicationStub];
#endif
XCTAssertEqual(UIApplicationStateActive, [BSG_KSSystemInfo currentAppState]);
}

Expand Down

0 comments on commit 185098b

Please sign in to comment.