iOS APNs推送返回BadDeviceToken错误原因及远程通知注册方案咨询
BadDeviceToken错误的常见原因
- curl命令格式错误:你当前的请求头
apns-push-type: alert没有加单引号包裹,curl会把alert识别为独立的主机地址,虽然第二次请求成功发送到APNs服务器,但流程中的参数混乱很容易伴随无效token的使用。 - DeviceToken解析错误:你使用
[deviceToken description]的方式提取token,该方法的输出格式在iOS 13及以上系统发生了变更,会得到包含多余字段的无效token,这是BadDeviceToken最常见的诱因。 - 环境不匹配:新导入的provisioning profile如果是生产环境配置,生成的token属于生产环境,你向沙箱地址
api.sandbox.push.apple.com发送请求就会返回BadDeviceToken;同理如果证书和token所属环境不匹配也会触发该错误。 - 配置不匹配:provisioning profile没有开启推送能力、apns-topic和证书对应的Bundle ID不一致、测试设备没有添加到当前开发团队的设备列表中,都会导致token无效。
正确的远程通知注册及调试方案
1. 修正DeviceToken解析逻辑
不要使用description方法提取token,改用如下兼容所有iOS版本的解析代码:
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { NSUInteger length = deviceToken.length; if (length == 0) { return; } const unsigned char *buffer = deviceToken.bytes; NSMutableString *newToken = [NSMutableString stringWithCapacity:(length * 2)]; for (NSInteger i = 0; i < length; ++i) { [newToken appendFormat:@"%02x", buffer[i]]; } NSLog(@"My token is: %@", newToken); // 原有业务逻辑保留 UINavigationController *navigationController = (UINavigationController*)_window.rootViewController; ChatViewController *chatViewController = (ChatViewController*)[navigationController.viewControllers objectAtIndex:0]; DataModel *dataModel = chatViewController.dataModel; NSString* oldToken = [dataModel deviceToken]; [dataModel setDeviceToken:newToken]; if ([dataModel joinedChat] && ![newToken isEqualToString:oldToken]) { [self postUpdateRequest]; } }
2. 修正推送请求命令
给所有请求头加单引号,避免curl解析错误:
curl -v --header 'apns-topic: com.domain.appname' --header 'apns-push-type: alert' --cert aps.cer --cert-type DER --key PushChatKey.pem --key-type PEM --data '{"aps":{"alert":"Test"}}' --http2 https://api.sandbox.push.apple.com/3/device/你的正确deviceToken
如果是生产环境测试,把请求地址换成api.push.apple.com。
3. 修正注册代码的冗余逻辑
你当前的注册代码存在重复调用的问题,不管iOS版本判断结果如何,最后都额外调用了一次iOS8以前的注册接口,会导致高版本系统的注册逻辑异常,修正后代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { _storyBoard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; //-- Set Notification if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) { // iOS 8及以上系统 [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; [application registerForRemoteNotifications]; } else { // iOS 8以下系统 [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge)]; } if (launchOptions != nil) { NSDictionary* dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; if (dictionary != nil) { NSLog(@"Launched from push notification: %@", dictionary); /*[self addMessageFromRemoteNotification:dictionary updateUI:NO];*/ } } return YES; }
4. 配置文件校验
- 确认Xcode中当前的Signing & Capabilities已经添加Push Notifications能力
- 确认使用的provisioning profile属于开发环境,且对应的App ID已经开启推送服务
- 确认当前测试设备的UDID已经添加到该开发配置文件的设备列表中
- 确认推送证书和当前使用的App ID、环境完全匹配
内容的提问来源于stack exchange,提问作者programcode
相关产品推荐
相关产品推荐

