aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Xiangtian Dai <xiangtian@google.com>2017-09-01 14:38:45 -0700
committerGravatar GitHub <noreply@github.com>2017-09-01 14:38:45 -0700
commit1e9d6526109908ebd129ff30957a5b4d11d42e62 (patch)
tree8b5c38b661bebec643b01bec17d6f314a12e5472
parent36c20d92b7a1f4ac5cb950df61d381ee60be9670 (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.m59
-rw-r--r--Example/Auth/Tests/FIRAuthAppDelegateProxyTests.m68
-rw-r--r--Example/Auth/Tests/FIRPhoneAuthProviderTests.m8
-rw-r--r--Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m7
-rw-r--r--Firebase/Auth/Source/FIRAuth.m6
-rw-r--r--Firebase/Auth/Source/FIRAuthAPNSTokenManager.h11
-rw-r--r--Firebase/Auth/Source/FIRAuthAPNSTokenManager.m22
-rw-r--r--Firebase/Auth/Source/FIRAuthAppDelegateProxy.h6
-rw-r--r--Firebase/Auth/Source/FIRAuthAppDelegateProxy.m25
-rw-r--r--Firebase/Auth/Source/FIRAuthErrorUtils.h5
-rw-r--r--Firebase/Auth/Source/FIRAuthErrorUtils.m5
-rw-r--r--Firebase/Auth/Source/RPCs/FIRAuthBackend.m2
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]) {