diff --git a/Crashlytics/CHANGELOG.md b/Crashlytics/CHANGELOG.md index d0ea48fa84e..83017e161c6 100644 --- a/Crashlytics/CHANGELOG.md +++ b/Crashlytics/CHANGELOG.md @@ -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). diff --git a/FirebaseAuth/CHANGELOG.md b/FirebaseAuth/CHANGELOG.md index b2e19c0689a..f6f5b953a54 100644 --- a/FirebaseAuth/CHANGELOG.md +++ b/FirebaseAuth/CHANGELOG.md @@ -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) diff --git a/FirebaseAuth/Sources/Auth/FIRAuth.m b/FirebaseAuth/Sources/Auth/FIRAuth.m index 6984583193e..a0ea4d6b1b6 100644 --- a/FirebaseAuth/Sources/Auth/FIRAuth.m +++ b/FirebaseAuth/Sources/Auth/FIRAuth.m @@ -1499,6 +1499,7 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [self canHandleNotification:userInfo]; + completionHandler(UIBackgroundFetchResultNoData); } // iOS 10 deprecation diff --git a/FirebaseMessaging/CHANGELOG.md b/FirebaseMessaging/CHANGELOG.md index 42198a39238..b6712b2036b 100644 --- a/FirebaseMessaging/CHANGELOG.md +++ b/FirebaseMessaging/CHANGELOG.md @@ -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) diff --git a/FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.m b/FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.m index 1ebe92b8a35..028c3e48d07 100644 --- a/FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.m +++ b/FirebaseMessaging/Sources/FIRMessagingRemoteNotificationsProxy.m @@ -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 diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index ec3c651398e..e9bbdbea834 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -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 diff --git a/GoogleDataTransport/CHANGELOG.md b/GoogleDataTransport/CHANGELOG.md index 08100a3ba63..ae4775f8fc6 100644 --- a/GoogleDataTransport/CHANGELOG.md +++ b/GoogleDataTransport/CHANGELOG.md @@ -1,4 +1,4 @@ -# Unreleased +# v8.0.1 - Remove `GCC_TREAT_WARNINGS_AS_ERRORS` from the podspec. - Reduce pre-main startup time footprint. (#6855) diff --git a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m index 3fd4b6683ff..88ea0690a0f 100644 --- a/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m +++ b/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m @@ -21,6 +21,7 @@ #import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h" #import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h" +#import #import // Implementations need to be typed before calling the implementation directly to cast the @@ -882,24 +883,72 @@ - (void)application:(GULApplication *)application didReceiveRemoteNotificationWithCompletionIMP = [didReceiveRemoteNotificationWithCompletionIMPPointer pointerValue]; + dispatch_group_t __block callbackGroup = dispatch_group_create(); + NSMutableArray *__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 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 diff --git a/GoogleUtilities/CHANGELOG.md b/GoogleUtilities/CHANGELOG.md index 30ce7e425c5..a5607041370 100644 --- a/GoogleUtilities/CHANGELOG.md +++ b/GoogleUtilities/CHANGELOG.md @@ -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 diff --git a/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m b/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m index 40a73be3137..8f66e903fa1 100644 --- a/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m +++ b/GoogleUtilities/Tests/Unit/Swizzler/GULAppDelegateSwizzlerTest.m @@ -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); @@ -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 { diff --git a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift index 9e454b48e30..1c66d6b0fba 100755 --- a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift +++ b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift @@ -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"),