diff options
author | Xiangtian Dai <xiangtian@google.com> | 2017-09-01 14:38:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-01 14:38:45 -0700 |
commit | 1e9d6526109908ebd129ff30957a5b4d11d42e62 (patch) | |
tree | 8b5c38b661bebec643b01bec17d6f314a12e5472 | |
parent | 36c20d92b7a1f4ac5cb950df61d381ee60be9670 (diff) |
Swizzles APNs token error app delegate method for faster turnaround. (#226)
Also removes the server request in case the token is missing.
-rw-r--r-- | Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m | 59 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m | 68 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRPhoneAuthProviderTests.m | 8 | ||||
-rw-r--r-- | Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m | 7 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuth.m | 6 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAPNSTokenManager.h | 11 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAPNSTokenManager.m | 22 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAppDelegateProxy.h | 6 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthAppDelegateProxy.m | 25 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthErrorUtils.h | 5 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthErrorUtils.m | 5 | ||||
-rw-r--r-- | Firebase/Auth/Source/RPCs/FIRAuthBackend.m | 2 |
12 files changed, 201 insertions, 23 deletions
diff --git a/Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m b/Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m index 37d95a6..68e8bcf 100644 --- a/Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m +++ b/Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m @@ -80,6 +80,11 @@ static const NSTimeInterval kExpectationTimeout = 1; @brief Another piece of data used for testing. */ NSData *_otherData; + + /** @var _error + @brief The fake error used for testing. + */ + NSError *_error; } - (void)setUp { @@ -118,18 +123,20 @@ static const NSTimeInterval kExpectationTimeout = 1; // Add first callback, which is yet to be called. OCMExpect([_mockApplication registerForRemoteNotifications]); __block BOOL firstCallbackCalled = NO; - [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { XCTAssertEqualObjects(token.data, _data); XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox); + XCTAssertNil(error); firstCallbackCalled = YES; }]; XCTAssertFalse(firstCallbackCalled); // Add second callback, which is yet to be called either. __block BOOL secondCallbackCalled = NO; - [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { XCTAssertEqualObjects(token.data, _data); XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox); + XCTAssertNil(error); secondCallbackCalled = YES; }]; XCTAssertFalse(secondCallbackCalled); @@ -149,9 +156,10 @@ static const NSTimeInterval kExpectationTimeout = 1; // Add third callback, which should be called back immediately. __block BOOL thirdCallbackCalled = NO; - [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { XCTAssertEqualObjects(token.data, _data); XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox); + XCTAssertNil(error); thirdCallbackCalled = YES; }]; XCTAssertTrue(thirdCallbackCalled); @@ -176,8 +184,48 @@ static const NSTimeInterval kExpectationTimeout = 1; // Add callback to time out. OCMExpect([_mockApplication registerForRemoteNotifications]); XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; - [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { + XCTAssertNil(token); + XCTAssertNil(error); + [expectation fulfill]; + }]; + + // Time out. + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockApplication); + + // Calling cancel afterwards should have no effect. + [_manager cancelWithError:_error]; +} + +/** @fn testCancel + @brief Tests cancelling the pending callbacks. + */ +- (void)testCancel { + // Set up timeout. + XCTAssertGreaterThan(_manager.timeout, 0); + _manager.timeout = kRegistrationTimeout; + + // Add callback to cancel. + OCMExpect([_mockApplication registerForRemoteNotifications]); + __block BOOL callbackCalled = NO; + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { + XCTAssertNil(token); + XCTAssertEqualObjects(error, _error); + XCTAssertFalse(callbackCalled); // verify callback is not called twice + callbackCalled = YES; + }]; + XCTAssertFalse(callbackCalled); + + // Call cancel. + [_manager cancelWithError:_error]; + XCTAssertTrue(callbackCalled); + + // Add callback to timeout. + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { XCTAssertNil(token); + XCTAssertNil(error); [expectation fulfill]; }]; @@ -200,9 +248,10 @@ static const NSTimeInterval kExpectationTimeout = 1; [[[_mockApplication expect] ignoringNonObjectArgs] registerForRemoteNotificationTypes:0]; #pragma clang diagnostic pop __block BOOL callbackCalled = NO; - [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) { XCTAssertEqualObjects(token.data, _data); XCTAssertNotEqual(token.type, FIRAuthAPNSTokenTypeUnknown); + XCTAssertNil(error); callbackCalled = YES; }]; XCTAssertFalse(callbackCalled); diff --git a/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m b/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m index d877182..b01ee26 100644 --- a/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m +++ b/Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m @@ -81,6 +81,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, copy, nullable) NSData *deviceTokenReceived; +/** @var tokenErrorReceived + @brief The last token error received, if any. + */ +@property(nonatomic, copy, nullable) NSError *tokenErrorReceived; + /** @var notificationReceived @brief The last notification received, if any. */ @@ -101,6 +106,11 @@ NS_ASSUME_NONNULL_BEGIN } - (void)application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error { + self.tokenErrorReceived = error; +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { self.notificationReceived = userInfo; @@ -153,6 +163,11 @@ NS_ASSUME_NONNULL_BEGIN */ NSData *_deviceToken; + /** @var _error + @brief The fake error for testing. + */ + NSError *_error; + /** @var _notification @brief The fake notification for testing. */ @@ -173,6 +188,7 @@ NS_ASSUME_NONNULL_BEGIN [super setUp]; _mockApplication = OCMClassMock([UIApplication class]); _deviceToken = [@"asdf" dataUsingEncoding:NSUTF8StringEncoding]; + _error = [NSError errorWithDomain:@"FakeError" code:12345 userInfo:nil]; _notification = @{ @"zxcv" : @1234 }; _url = [NSURL URLWithString:@"https://abc.def/ghi"]; _isIOS9orLater = [[[UIDevice currentDevice] systemVersion] doubleValue] >= 9.0; @@ -262,6 +278,8 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertTrue([delegate respondsToSelector: @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); XCTAssertTrue([delegate respondsToSelector: + @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); + XCTAssertTrue([delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); XCTAssertFalse([delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:)]); @@ -288,6 +306,12 @@ NS_ASSUME_NONNULL_BEGIN didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; OCMVerifyAll(mockHandler); + // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. + OCMExpect([mockHandler handleAPNSTokenError:_error]); + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + OCMVerifyAll(mockHandler); + // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES); __block BOOL fetchCompletionHandlerCalled = NO; @@ -324,6 +348,8 @@ NS_ASSUME_NONNULL_BEGIN [delegate application:_mockApplication didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + [delegate application:_mockApplication didReceiveRemoteNotification:_notification fetchCompletionHandler:^(UIBackgroundFetchResult result) { XCTFail(@"Should not call completion handler."); @@ -374,6 +400,8 @@ NS_ASSUME_NONNULL_BEGIN // Verify certain methods are swizzled while others are not. XCTAssertTrue([delegate respondsToSelector: @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); + XCTAssertTrue([delegate respondsToSelector: + @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); XCTAssertFalse([delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); XCTAssertTrue([delegate respondsToSelector: @@ -401,6 +429,14 @@ NS_ASSUME_NONNULL_BEGIN OCMVerifyAll(mockHandler1); OCMVerifyAll(mockHandler2); + // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. + OCMExpect([mockHandler1 handleAPNSTokenError:_error]); + OCMExpect([mockHandler2 handleAPNSTokenError:_error]); + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + OCMVerifyAll(mockHandler1); + OCMVerifyAll(mockHandler2); + // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(YES); // handler2 shouldn't been invoked because it is already handled by handler1. @@ -431,6 +467,12 @@ NS_ASSUME_NONNULL_BEGIN didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; OCMVerifyAll(mockHandler1); + // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. + OCMExpect([mockHandler1 handleAPNSTokenError:_error]); + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + OCMVerifyAll(mockHandler1); + // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is NOT handled. OCMExpect([mockHandler1 canHandleNotification:_notification]).andReturn(NO); [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; @@ -457,6 +499,8 @@ NS_ASSUME_NONNULL_BEGIN // Verify the delegate still works after all handlers are released. [delegate application:_mockApplication didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; [delegate application:_mockApplication didReceiveRemoteNotification:_notification]; XCTAssertEqualObjects(delegate.notificationReceived, _notification); delegate.notificationReceived = nil; @@ -505,6 +549,8 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertTrue([delegate respondsToSelector: @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:)]); XCTAssertTrue([delegate respondsToSelector: + @selector(application:didFailToRegisterForRemoteNotificationsWithError:)]); + XCTAssertTrue([delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]); XCTAssertFalse([delegate respondsToSelector: @selector(application:didReceiveRemoteNotification:)]); @@ -532,6 +578,14 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); delegate.deviceTokenReceived = nil; + // Verify `application:didFailToRegisterForRemoteNotificationsWithError:` is handled. + OCMExpect([mockHandler handleAPNSTokenError:_error]); + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + OCMVerifyAll(mockHandler); + XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); + delegate.tokenErrorReceived = nil; + // Verify `application:didReceiveRemoteNotification:fetchCompletionHandler:` is handled. OCMExpect([mockHandler canHandleNotification:_notification]).andReturn(YES); __block BOOL fetchCompletionHandlerCalled = NO; @@ -565,6 +619,10 @@ NS_ASSUME_NONNULL_BEGIN didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; XCTAssertEqualObjects(unaffectedDelegate.deviceTokenReceived, _deviceToken); unaffectedDelegate.deviceTokenReceived = nil; + [unaffectedDelegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + XCTAssertEqualObjects(unaffectedDelegate.tokenErrorReceived, _error); + unaffectedDelegate.tokenErrorReceived = nil; fetchCompletionHandlerCalled = NO; [unaffectedDelegate application:_mockApplication didReceiveRemoteNotification:_notification @@ -590,6 +648,10 @@ NS_ASSUME_NONNULL_BEGIN didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); delegate.deviceTokenReceived = nil; + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); + delegate.tokenErrorReceived = nil; __block BOOL fetchCompletionHandlerCalled = NO; [delegate application:_mockApplication didReceiveRemoteNotification:_notification @@ -615,7 +677,11 @@ NS_ASSUME_NONNULL_BEGIN didRegisterForRemoteNotificationsWithDeviceToken:_deviceToken]; XCTAssertEqualObjects(delegate.deviceTokenReceived, _deviceToken); delegate.deviceTokenReceived = nil; - __block BOOL fetchCompletionHandlerCalled = NO; + [delegate application:_mockApplication + didFailToRegisterForRemoteNotificationsWithError:_error]; + XCTAssertEqualObjects(delegate.tokenErrorReceived, _error); + delegate.tokenErrorReceived = nil; + __block BOOL fetchCompletionHandlerCalled = NO; [delegate application:_mockApplication didReceiveRemoteNotification:_notification fetchCompletionHandler:^(UIBackgroundFetchResult result) { diff --git a/Example/Auth/Tests/FIRPhoneAuthProviderTests.m b/Example/Auth/Tests/FIRPhoneAuthProviderTests.m index 5d67ac8..9a0a219 100644 --- a/Example/Auth/Tests/FIRPhoneAuthProviderTests.m +++ b/Example/Auth/Tests/FIRPhoneAuthProviderTests.m @@ -274,7 +274,7 @@ static const NSTimeInterval kExpectationTimeout = 1; .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); }); OCMExpect([_mockAppCredentialManager credential]).andReturn(nil); OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY]) - .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(nil); }); + .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(nil, nil); }); // Expect verify client request to the backend wth empty token. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]]) .andCallBlock2(^(FIRVerifyClientRequest *request, @@ -314,7 +314,7 @@ static const NSTimeInterval kExpectationTimeout = 1; FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data type:FIRAuthAPNSTokenTypeProd]; OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY]) - .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); }); + .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); }); // Expect verify client request to the backend. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]]) .andCallBlock2(^(FIRVerifyClientRequest *request, @@ -417,7 +417,7 @@ static const NSTimeInterval kExpectationTimeout = 1; }); OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY]) - .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); }); + .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); }); // Expect verify client request to the backend. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]]) .andCallBlock2(^(FIRVerifyClientRequest *request, @@ -509,7 +509,7 @@ static const NSTimeInterval kExpectationTimeout = 1; }); OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY]) - .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token); }); + .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); }); // Expect verify client request to the backend. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]]) .andCallBlock2(^(FIRVerifyClientRequest *request, diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m index 7c29f13..4a0e7e7 100644 --- a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m @@ -169,7 +169,12 @@ typedef void (^FIRVerifyClientCallback)(FIRAuthAppCredential *_Nullable appCrede completion(_auth.appCredentialManager.credential, nil); return; } - [_auth.tokenManager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token) { + [_auth.tokenManager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, + NSError *_Nullable error) { + if (!token) { + completion(nil, [FIRAuthErrorUtils missingAppTokenErrorWithUnderlyingError:error]); + return; + } FIRVerifyClientRequest *request = [[FIRVerifyClientRequest alloc] initWithAppToken:token.string isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index 0a8af0e..d2915a3 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -971,6 +971,12 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; }); } +- (void)handleAPNSTokenError:(NSError *)error { + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + [_tokenManager cancelWithError:error]; + }); +} + - (BOOL)canHandleNotification:(NSDictionary *)userInfo { __block BOOL result = NO; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.h b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.h index a2d6f4c..566780f 100644 --- a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.h +++ b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.h @@ -24,8 +24,11 @@ NS_ASSUME_NONNULL_BEGIN /** @typedef FIRAuthAPNSTokenCallback @brief The type of block to receive an APNs token. @param token The APNs token if one is available. + @param error The error happened if any. + @remarks Both `token` and `error` being `nil` means the request timed-out. */ -typedef void (^FIRAuthAPNSTokenCallback)(FIRAuthAPNSToken *_Nullable token); +typedef void (^FIRAuthAPNSTokenCallback)(FIRAuthAPNSToken *_Nullable token, + NSError *_Nullable error); /** @class FIRAuthAPNSTokenManager @brief A class to manage APNs token in memory. @@ -64,6 +67,12 @@ typedef void (^FIRAuthAPNSTokenCallback)(FIRAuthAPNSToken *_Nullable token); */ - (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback; +/** @fn cancelWithError: + @brief Cancels any pending `getTokenWithCallback:` request. + @param error The error to return. + */ +- (void)cancelWithError:(NSError *)error; + @end NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m index 300b7e3..a4e4389 100644 --- a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m +++ b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m @@ -58,7 +58,7 @@ static const NSTimeInterval kLegacyRegistrationTimeout = 30; - (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback { if (_token) { - callback(_token); + callback(_token, nil); return; } if (_pendingCallbacks) { @@ -77,9 +77,13 @@ static const NSTimeInterval kLegacyRegistrationTimeout = 30; #pragma clang diagnostic pop } }); + NSArray<FIRAuthAPNSTokenCallback> *applicableCallbacks = _pendingCallbacks; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), FIRAuthGlobalWorkQueue(), ^{ - [self callBack]; + // Only cancel if the pending callbacks remain the same, i.e., not triggered yet. + if (applicableCallbacks == _pendingCallbacks) { + [self callBackWithToken:nil error:nil]; + } }); } @@ -97,22 +101,28 @@ static const NSTimeInterval kLegacyRegistrationTimeout = 30; token = [[FIRAuthAPNSToken alloc] initWithData:token.data type:detectedTokenType]; } _token = token; - [self callBack]; + [self callBackWithToken:token error:nil]; +} + +- (void)cancelWithError:(NSError *)error { + [self callBackWithToken:nil error:error]; } #pragma mark - Internal methods /** @fn callBack - @brief Calls back all pending callbacks with the current APNs token, if one is available. + @brief Calls back all pending callbacks with APNs token or error. + @param token The APNs token if one is available. + @param error The error occurred, if any. */ -- (void)callBack { +- (void)callBackWithToken:(nullable FIRAuthAPNSToken *)token error:(nullable NSError *)error { if (!_pendingCallbacks) { return; } NSArray<FIRAuthAPNSTokenCallback> *allCallbacks = _pendingCallbacks; _pendingCallbacks = nil; for (FIRAuthAPNSTokenCallback callback in allCallbacks) { - callback(_token); + callback(token, error); } }; diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h index d974e2c..ccae93a 100644 --- a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h +++ b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.h @@ -30,6 +30,12 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setAPNSToken:(NSData *)token; +/** @fn handleAPNSTokenError: + @brief Handles APNs device token error. + @param error The APNs device token error. + */ +- (void)handleAPNSTokenError:(NSError *)error; + /** @fn canHandleNotification: @brief Checks whether the notification can be handled by the receiver, and handles it if so. @param notification The notification in question, which will be consumed if returns @c YES. diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m index 15151f3..8899407 100644 --- a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m +++ b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m @@ -82,6 +82,15 @@ static id noop(id object, SEL cmd, ...) { application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; }]; + SEL failToRegisterRemoteNotificationSelector = + @selector(application:didFailToRegisterForRemoteNotificationsWithError:); + [self replaceSelector:failToRegisterRemoteNotificationSelector + withBlock:^(id object, UIApplication* application, NSError *error) { + [weakSelf object:object + selector:failToRegisterRemoteNotificationSelector + application:application + didFailToRegisterForRemoteNotificationsWithError:error]; + }]; SEL receiveNotificationSelector = @selector(application:didReceiveRemoteNotification:); SEL receiveNotificationWithHandlerSelector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); @@ -200,6 +209,22 @@ static id noop(id object, SEL cmd, ...) { } - (void)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + if (object == _appDelegate) { + for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) { + [handler handleAPNSTokenError:error]; + } + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + if (originalImplementation && originalImplementation != &noop) { + typedef void (*Implmentation)(id, SEL, UIApplication *, NSError *); + ((Implmentation)originalImplementation)(object, selector, application, error); + } +} + +- (void)object:(id)object selector:(SEL)selector application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.h b/Firebase/Auth/Source/FIRAuthErrorUtils.h index 4fdfe9b..1537553 100644 --- a/Firebase/Auth/Source/FIRAuthErrorUtils.h +++ b/Firebase/Auth/Source/FIRAuthErrorUtils.h @@ -425,11 +425,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (NSError *)quotaExceededErrorWithMessage:(nullable NSString *)message; -/** @fn missingAppTokenError +/** @fn missingAppTokenErrorWithUnderlyingError @brief Constructs an @c NSError with the @c FIRAuthErrorCodeMissingAppToken code. + @param underlyingError The underlying error, if any. @return The NSError instance associated with the given FIRAuthError. */ -+ (NSError *)missingAppTokenError; ++ (NSError *)missingAppTokenErrorWithUnderlyingError:(nullable NSError *)underlyingError; /** @fn notificationNotForwardedError @brief Constructs an @c NSError with the @c FIRAuthErrorCodeNotificationNotForwarded code. diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.m b/Firebase/Auth/Source/FIRAuthErrorUtils.m index ad9ad55..d5b1c9b 100644 --- a/Firebase/Auth/Source/FIRAuthErrorUtils.m +++ b/Firebase/Auth/Source/FIRAuthErrorUtils.m @@ -877,8 +877,9 @@ static NSString *const FIRAuthErrorCodeString(FIRAuthErrorCode code) { return [self errorWithCode:FIRAuthInternalErrorCodeQuotaExceeded message:message]; } -+ (NSError *)missingAppTokenError { - return [self errorWithCode:FIRAuthInternalErrorCodeMissingAppToken]; ++ (NSError *)missingAppTokenErrorWithUnderlyingError:(nullable NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingAppToken + underlyingError:underlyingError]; } + (NSError *)notificationNotForwardedError { diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m index 0387a1a..afe05bd 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -1023,7 +1023,7 @@ static id<FIRAuthBackendImplementation> gBackendImplementation; } if ([shortErrorMessage isEqualToString:kMissingAppTokenErrorMessage]) { - return [FIRAuthErrorUtils missingAppTokenError]; + return [FIRAuthErrorUtils missingAppTokenErrorWithUnderlyingError:nil]; } if ([shortErrorMessage isEqualToString:kMissingAppCredentialErrorMessage]) { |