Skip to content

Commit

Permalink
Release 7.1.0 cherry picks (#6917)
Browse files Browse the repository at this point in the history
* FCM: Fix crash on iOS 14 when multiple app delegate completion methods are called (#6863)

* Fix crash on iOS 14 when multiple completion methods are called

* Fixed lint

* Addressed the PR comments & implemented unit tests

* Reverted auto-extracted method

* Re-organized tests

* Added completion handler for FIRAuth

* Manifest: GDT 8.0.1

* Update Changelogs for 7.1.0 (#6913)

* Changelogs

Co-authored-by: Mike Gerasimenko <[email protected]>
Co-authored-by: Paul Beusterien <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2020
1 parent 648ebdf commit 2385785
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Crashlytics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Unreleased
# v7.1.0
- [fixed] Fixed an issue where symbol uploads would fail when there are spaces in the project path, particularly in Unity builds (#6789).
- [changed] Added additional logging when settings requests fail with a 404 status to help customers debug onboarding issues (#6847).

Expand Down
3 changes: 3 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 7.1.0
- [fixed] Fixed completion handler issue in `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` method. (#6863)

# 7.0.0
- [removed] Remove deprecated APIs `dataForKey`,`fetchProvidersForEmail:completion`, `signInAndRetrieveDataWithCredential:completion`, `reauthenticateAndRetrieveDataWithCredential:completion`, `linkAndRetrieveDataWithCredential:completion`. (#6607)
- [added] Add support for the auth emulator. (#6624)
Expand Down
1 change: 1 addition & 0 deletions FirebaseAuth/Sources/Auth/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,7 @@ - (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[self canHandleNotification:userInfo];
completionHandler(UIBackgroundFetchResultNoData);
}

// iOS 10 deprecation
Expand Down
5 changes: 4 additions & 1 deletion FirebaseMessaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# unreleased -- v.7.0.0
# 2020-11 -- v7.1.0
- [fixed] Fixed completion handler issue in `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` method. (#6863)

# 2020-10 -- v7.0.0
- [changed] Remove the deprecated FCM direct channel API and Upstream send API. (#6430)
- [changed] The `messaging:didReceiveRegistrationToken:` should be able to return a null token. Update the API parameter fcmToken to be nullable. (#5339)
- [fixed] Fixed an issue that downloading an image failed when there's no extension in the file name but MIME type is set. (#6590)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ - (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
completionHandler(UIBackgroundFetchResultNoData);
}

- (void)application:(UIApplication *)application
Expand Down
2 changes: 1 addition & 1 deletion Firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Unreleased (v7.0.1)
# v7.1.0
- [changed] Added the original query data to error messages for Queries that
cannot be deserizialized.
- [fixed] Remove explicit MobileCoreServices library linkage from podspec
Expand Down
2 changes: 1 addition & 1 deletion GoogleDataTransport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Unreleased
# v8.0.1
- Remove `GCC_TREAT_WARNINGS_AS_ERRORS` from the podspec.
- Reduce pre-main startup time footprint. (#6855)

Expand Down
53 changes: 51 additions & 2 deletions GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"

#import <dispatch/group.h>
#import <objc/runtime.h>

// Implementations need to be typed before calling the implementation directly to cast the
Expand Down Expand Up @@ -882,24 +883,72 @@ - (void)application:(GULApplication *)application
didReceiveRemoteNotificationWithCompletionIMP =
[didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue];

dispatch_group_t __block callbackGroup = dispatch_group_create();
NSMutableArray<NSNumber *> *__block fetchResults = [NSMutableArray array];

void (^localCompletionHandler)(UIBackgroundFetchResult) =
^void(UIBackgroundFetchResult fetchResult) {
[fetchResults addObject:[NSNumber numberWithInt:(int)fetchResult]];
dispatch_group_leave(callbackGroup);
};

// Notify interceptors.
[GULAppDelegateSwizzler
notifyInterceptorsWithMethodSelector:methodSelector
callback:^(id<GULApplicationDelegate> interceptor) {
dispatch_group_enter(callbackGroup);

NSInvocation *invocation = [GULAppDelegateSwizzler
appDelegateInvocationForSelector:methodSelector];
[invocation setTarget:interceptor];
[invocation setSelector:methodSelector];
[invocation setArgument:(void *)(&application) atIndex:2];
[invocation setArgument:(void *)(&userInfo) atIndex:3];
[invocation setArgument:(void *)(&completionHandler) atIndex:4];
[invocation setArgument:(void *)(&localCompletionHandler)
atIndex:4];
[invocation invoke];
}];
// Call the real implementation if the real App Delegate has any.
if (didReceiveRemoteNotificationWithCompletionIMP) {
dispatch_group_enter(callbackGroup);

didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo,
completionHandler);
localCompletionHandler);
}

dispatch_group_notify(callbackGroup, dispatch_get_main_queue(), ^() {
BOOL allFetchesFailed = YES;
BOOL anyFetchHasNewData = NO;

for (NSNumber *oneResult in fetchResults) {
UIBackgroundFetchResult result = oneResult.intValue;

switch (result) {
case UIBackgroundFetchResultNoData:
allFetchesFailed = NO;
break;
case UIBackgroundFetchResultNewData:
allFetchesFailed = NO;
anyFetchHasNewData = YES;
break;
case UIBackgroundFetchResultFailed:

break;
}
}

UIBackgroundFetchResult finalFetchResult = UIBackgroundFetchResultNoData;

if (allFetchesFailed) {
finalFetchResult = UIBackgroundFetchResultFailed;
} else if (anyFetchHasNewData) {
finalFetchResult = UIBackgroundFetchResultNewData;
} else {
finalFetchResult = UIBackgroundFetchResultNoData;
}

completionHandler(finalFetchResult);
});
}
#endif // !TARGET_OS_WATCH && !TARGET_OS_OSX

Expand Down
2 changes: 2 additions & 0 deletions GoogleUtilities/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# 7.1.0 -- Unreleased
- Added `NSURLSession` promise extension. (#6753)
- `ios_on_mac` option added to `GULAppEnvironmentUtil.applePlatform()`. (#6799)
- Fixed completion handler issue in `GULAppDelegateSwizzler` for
`application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` method. (#6863)

# 7.0.0
- All APIs are now public. All CocoaPods private headers are transitioned to public. Note that
Expand Down
107 changes: 104 additions & 3 deletions GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -1065,12 +1065,12 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInte
id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate));
OCMExpect([interceptor application:application
didReceiveRemoteNotification:notification
fetchCompletionHandler:completion]);
fetchCompletionHandler:[OCMArg isNotNil]]);

id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate));
OCMExpect([interceptor2 application:application
didReceiveRemoteNotification:notification
fetchCompletionHandler:completion]);
fetchCompletionHandler:[OCMArg isNotNil]]);

GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init];
OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate);
Expand All @@ -1087,7 +1087,108 @@ - (void)testApplicationDidReceiveRemoteNotificationWithCompletionIsInvokedOnInte

XCTAssertEqual(testAppDelegate.application, application);
XCTAssertEqual(testAppDelegate.remoteNotification, notification);
XCTAssertEqual(testAppDelegate.remoteNotificationCompletionHandler, completion);
}

- (void)verifyCompletionCalledForObserverResult:(UIBackgroundFetchResult)observerResult1
anotherObserverResult:(UIBackgroundFetchResult)observerResult2
swizzledResult:(UIBackgroundFetchResult)swizzledResult
expecredResult:(UIBackgroundFetchResult)expectedResult {
NSDictionary *notification = @{};
GULApplication *application = [GULApplication sharedApplication];

XCTestExpectation *completionExpectation =
[[XCTestExpectation alloc] initWithDescription:@"Completion called once"];

void (^completion)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result) {
XCTAssertEqual(result, expectedResult);
[completionExpectation fulfill];
};

void (^onDidReceiveRemoteNotification1)(NSInvocation *invocation) = ^(NSInvocation *invocation) {
void __unsafe_unretained (^localCompletionHandler)(UIBackgroundFetchResult) = nil;
[invocation getArgument:(void *)(&localCompletionHandler) atIndex:4];
XCTAssertNotNil(localCompletionHandler);
localCompletionHandler(observerResult1);
};

id interceptor = OCMProtocolMock(@protocol(GULApplicationDelegate));
OCMExpect([interceptor application:application
didReceiveRemoteNotification:notification
fetchCompletionHandler:[OCMArg isNotNil]])
.andDo(onDidReceiveRemoteNotification1);

void (^onDidReceiveRemoteNotification2)(NSInvocation *invocation) = ^(NSInvocation *invocation) {
void __unsafe_unretained (^localCompletionHandler)(UIBackgroundFetchResult) = nil;
[invocation getArgument:(void *)(&localCompletionHandler) atIndex:4];
XCTAssertNotNil(localCompletionHandler);
localCompletionHandler(observerResult2);
};

id interceptor2 = OCMProtocolMock(@protocol(GULApplicationDelegate));
OCMExpect([interceptor2 application:application
didReceiveRemoteNotification:notification
fetchCompletionHandler:[OCMArg isNotNil]])
.andDo(onDidReceiveRemoteNotification2);

GULTestAppDelegate *testAppDelegate = [[GULTestAppDelegate alloc] init];
OCMStub([self.mockSharedApplication delegate]).andReturn(testAppDelegate);
[GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods];

[GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor];
[GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor2];

[testAppDelegate application:application
didReceiveRemoteNotification:notification
fetchCompletionHandler:completion];
testAppDelegate.remoteNotificationCompletionHandler(swizzledResult);
OCMVerifyAll(interceptor);
OCMVerifyAll(interceptor2);
[self waitForExpectations:@[ completionExpectation ] timeout:0.1];
}

- (void)testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultNoData
anotherObserverResult:UIBackgroundFetchResultNoData
swizzledResult:UIBackgroundFetchResultNoData
expecredResult:UIBackgroundFetchResultNoData];
}

- (void)
testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce_HandleFailedState {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultFailed
anotherObserverResult:UIBackgroundFetchResultFailed
swizzledResult:UIBackgroundFetchResultFailed
expecredResult:UIBackgroundFetchResultFailed];
}

- (void)testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce_NoData {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultNoData
anotherObserverResult:UIBackgroundFetchResultFailed
swizzledResult:UIBackgroundFetchResultFailed
expecredResult:UIBackgroundFetchResultNoData];
}
- (void)
testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce_HandleNewDataState_OthersFailed {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultNewData
anotherObserverResult:UIBackgroundFetchResultFailed
swizzledResult:UIBackgroundFetchResultFailed
expecredResult:UIBackgroundFetchResultNewData];
}

- (void)
testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce_HandleNewDataState_OthersNoData {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultNewData
anotherObserverResult:UIBackgroundFetchResultNoData
swizzledResult:UIBackgroundFetchResultNoData
expecredResult:UIBackgroundFetchResultNewData];
}

- (void)
testApplicationDidReceiveRemoteNotificationWithCompletionCompletionIsCalledOnce_HandleNewDataState_OthersNoDataFailed {
[self verifyCompletionCalledForObserverResult:UIBackgroundFetchResultNewData
anotherObserverResult:UIBackgroundFetchResultNoData
swizzledResult:UIBackgroundFetchResultFailed
expecredResult:UIBackgroundFetchResultNewData];
}

- (void)testApplicationDidReceiveRemoteNotificationWithCompletionImplementationIsNotAdded {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public let shared = Manifest(
version: "7.1.0",
pods: [
Pod("GoogleUtilities", isFirebase: false, podVersion: "7.1.0", releasing: true),
Pod("GoogleDataTransport", isFirebase: false, podVersion: "8.0.0", releasing: false),
Pod("GoogleDataTransport", isFirebase: false, podVersion: "8.0.1", releasing: true),

Pod("FirebaseCoreDiagnostics"),
Pod("FirebaseCore"),
Expand Down

0 comments on commit 2385785

Please sign in to comment.