Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ @interface FPRTraceBackgroundActivityTracker ()

@property(nonatomic, readwrite) FPRTraceState traceBackgroundState;

- (void)registerNotificationObservers;

@end

@implementation FPRTraceBackgroundActivityTracker
Expand All @@ -35,21 +37,26 @@ - (instancetype)init {
} else {
_traceBackgroundState = FPRTraceStateForegroundOnly;
}
__strong typeof(self) strongSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:[UIApplication sharedApplication]];
[strongSelf registerNotificationObservers];
});
}
return self;
}

- (void)registerNotificationObservers {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:[UIApplication sharedApplication]];
}

- (void)dealloc {
// Remove all the notification observers registered.
[[NSNotificationCenter defaultCenter] removeObserver:self];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,82 @@ - (void)testBackgroundTracking {
}];
}

/** Tests that synchronous observer registration works correctly and observers are immediately
* available. */
- (void)testObservers_synchronousRegistrationAddsObserver {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
XCTAssertNotNil(tracker);

[notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);

tracker = nil;
XCTAssertNil(tracker);
XCTAssertNoThrow([notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]]);
XCTAssertNoThrow([notificationCenter
postNotificationName:UIApplicationDidEnterBackgroundNotification
object:[UIApplication sharedApplication]]);
}

/** Tests rapid creation and deallocation to verify race condition. */
- (void)testRapidCreationAndDeallocation_noRaceCondition {
for (int i = 0; i < 100; i++) {
@autoreleasepool {
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
XCTAssertNotNil(tracker);

[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];
}
}

XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]]);
XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidEnterBackgroundNotification
object:[UIApplication sharedApplication]]);
}

/** Tests that observers are registered immediately after init on main thread. */
- (void)testObservers_immediateRegistrationOnMainThread {
XCTAssertTrue([NSThread isMainThread]);

FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];

[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];

XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
}

/** Tests observer registration when created from background thread. */
- (void)testObservers_registrationFromBackgroundThread {
XCTestExpectation *expectation = [self expectationWithDescription:@"Background thread creation"];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
XCTAssertNotNil(tracker);

dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:UIApplicationDidBecomeActiveNotification
object:[UIApplication sharedApplication]];

XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
[expectation fulfill];
});
});

[self waitForExpectationsWithTimeout:5.0
handler:^(NSError *error) {
XCTAssertNil(error, @"Test timed out");
}];
}

@end