didReceiveRemoteNotification:fetchCompletionHandler在应用程序处于后台并且未连接到Xcode时不会被调用

我有一个很奇怪的问题,我实现了:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler 

无声远程推送通知。 它适用于当应用程序在后台并连接到Xcode。 当我拔下任何iOS设备并运行应用程序,移动到后台并发送远程通知, didReceiveRemoteNotification:fetchCompletionHandler不被调用。

我的代码如下:

 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSInteger pushCode = [userInfo[@"pushCode"] integerValue]; NSLog(@"Silent Push Code Notification: %i", pushCode); NSDictionary *aps = userInfo[@"aps"]; NSString *alertMessage = aps[@"alert"]; if (pushCode == kPushCodeShowText) { UILocalNotification *localNotif = [[UILocalNotification alloc] init]; localNotif.fireDate = [NSDate date]; localNotif.timeZone = [NSTimeZone defaultTimeZone]; localNotif.alertBody = alertMessage; localNotif.alertAction = @"OK"; localNotif.soundName = @"sonar.aiff"; // localNotif.applicationIconBadgeNumber = 0; localNotif.userInfo = nil; [[UIApplication sharedApplication] presentLocalNotificationNow:localNotif]; UILocalNotification *clearNotification = [[UILocalNotification alloc] init]; clearNotification.fireDate = [NSDate date]; clearNotification.timeZone = [NSTimeZone defaultTimeZone]; clearNotification.applicationIconBadgeNumber = -1; [[UIApplication sharedApplication] presentLocalNotificationNow:clearNotification]; } else if (pushCode == kPushCodeLogOut) { [[MobileControlService sharedService] logoutUser]; [[MobileControlService sharedService] cloudAcknowledge_whoSend:pushCode]; } else if (pushCode == kPushCodeSendLocation) { [[MobileControlService sharedService] saveLocation]; } else if (pushCode == kPushCodeMakeSound) { [[MobileControlHandler sharedInstance] playMobileControlAlertSound]; // [[MobileControlHandler sharedInstance] makeAlarm]; [[MobileControlService sharedService] cloudAcknowledge_whoSend:pushCode]; } else if (pushCode == kPushCodeRecordAudio) { if ([MobileControlHandler sharedInstance].isRecordingNow) { [[MobileControlHandler sharedInstance] stopRecord]; } else { [[MobileControlHandler sharedInstance] startRecord]; } [[MobileControlService sharedService] cloudAcknowledge_whoSend:pushCode]; } completionHandler(UIBackgroundFetchResultNewData); } - (void)saveLocation { bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:bgTask]; }]; char *hostname; struct hostent *hostinfo; hostname = "http://m.google.com"; hostinfo = gethostbyname(hostname); if (hostname == NULL) { NSLog(@"No internet connection (saveLocation)"); return; } if (self.locationManager.location.coordinate.latitude == 0.0 || self.locationManager.location.coordinate.longitude == 0.0) { NSLog(@"saveLocation - coordinates are 0.0."); return; } NSLog(@"saveLocation - trying to get location."); NSString *postBody = [NSString stringWithFormat:@"Lat=%@&Lon=%@&Date=%@&userID=%@&batteryLevel=%@&version=%@&accuracy=%@&address=%@", self.myInfo.lat, self.myInfo.lon, self.myInfo.date, self.myInfo.userID, self.myInfo.batteryLevel, self.myInfo.version, self.myInfo.accuracy, self.myInfo.address]; NSURL *completeURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/saveLocation", WEB_SERVICES_URL]]; NSData *body = [postBody dataUsingEncoding:NSUTF8StringEncoding]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:completeURL]; [request setHTTPMethod:@"POST"]; [request setValue:kAPP_PASSWORD_VALUE forHTTPHeaderField:kAPP_PASSWORD_KEY]; [request setHTTPBody:body]; [request setValue:[NSString stringWithFormat:@"%d", body.length] forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; if (__iOS_7_And_Heigher) { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"saveLocation Error: %@", error.localizedDescription); } else { NSString *responseXML = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"\n\nResponseXML(saveLocation):\n%@", responseXML); [self cloudAcknowledge_whoSend:kPushCodeSendLocation]; } }]; [dataTask resume]; } else { [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { if (connectionError) { NSLog(@"saveLocation Error: %@", connectionError.localizedDescription); } else { NSString *responseXML = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"\n\nResponseXML(saveLocation):\n%@", responseXML); [self cloudAcknowledge_whoSend:kPushCodeSendLocation]; } }]; } } - (void)startBackgroundTask { bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:bgTask]; }]; } - (void)endBackgroundTask { if (bgTask != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; } } 

并且[self endBackgroundTask]位于cloudAcknowledge函数的末尾。 不知道这到底是怎么回事?

编辑:

有效负载如下所示: { aps = { "content-available" = 1; }; pushCode = 12; } { aps = { "content-available" = 1; }; pushCode = 12; }

可能有一些事情可能会出错,第一个来自我自己的经验。 为了使沉默推送通知工作。 您的有效负载必须正确构造,

 { "aps" : { "content-available" : 1 }, "data-id" : 345 } 

你的推送消息是否有content-available: 1如果不是,则iOS不会调用新的委托方法。

 - (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
 TL;DR: Use Test Flight in iTunes Connect 

也许你们中的一些人已经知道了这一点,但我在这里发帖,因为我没有看到明确的答案。

有相同的问题描述。 无线推送通知在Lightning电缆连接的情况下工作。 断开电缆时停止工作。 我跟踪了每个NSLog和networking电话以certificate确实发生了。

我正在使用许多答案中提到的有效载荷,以及这个:

 { "aps" : { "content-available": 1, sound: "" } } 

几个小时后,我发现这个问题与iOS 8.0,8.18.1.1上的Adhoc开发configuration文件有关。 我正在使用Crashlytics发送我的应用程序(使用Adhocconfiguration文件)的testing版本。

解决方法是:

为了使它工作,请试用Apple的Test Flight与iTunes Connect的集成。 使用它你将发送你的应用程序的存档(在App Store上使用相同的存档),但是可以使你的二进制文件在testing版中使用。 从Test Flight安装的版本很可能(我不能certificate)使用App Store中相同的生产供应configuration文件,该应用程序就像一个魅力。

这里有一个链接,可以帮助build立testing飞行帐户:

http://code.tutsplus.com/tutorials/ios-8-beta-testing-with-testflight–cms-22224

甚至没有错过一个单一的无声有效载荷。

我希望能帮助别人在这个问题上不要失去整整一个星期。

可能的原因是背景应用程序刷新closures您的iPhone。
您可以在设置 – >常规 – >后台应用程序刷新中打开/closures此选项。
当手机上的“后台应用程序刷新”closures时,仅当手机连接到XCode时,才会调用didReceiveRemoteNotification:fetchCompletionHandler方法。

只是想添加一个更新的答案。

我面临同样的问题。

 -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; 

当应用程序从后台多任务中死亡(双击主页button并向上滑动以杀死应用程序)时不会被调用。

我已经使用开发推送通知和NWPusher工具( https://github.com/noodlewerk/NWPusher

过时的文件

这个以前的文档块说:

与application:didReceiveRemoteNotification:方法不同,该方法仅在应用程序运行时调用系统将调用此方法, 而不pipe应用程序的状态如何 。 如果您的应用程序被暂停或未运行,则在调用该方法之前,系统会唤醒或启动您的应用程序并将其置于后台运行状态。 如果用户从系统显示的警报中打开您的应用程序,则系统会再次调用此方法,以便您知道用户select了哪个通知。

已经过时了 (在撰写本文时为04/06/2015)。

更新文件(截至2015年6月4日)

我查阅了文档(在撰写本文时为04/06/2015),它说:

使用此方法处理传入的应用程序的远程通知。 与应用程序不同的是:didReceiveRemoteNotification:只在应用程序在前台运行时调用,方法是当您的应用程序在前台或 后台运行时调用此方法。 另外,如果启用了远程通知后台模式,系统将启动您的应用程序(或将其从暂停状态唤醒),并在远程通知到达时将其置于后台状态。 但是,如果用户强制退出,系统不会自动启动您的应用程序。 在这种情况下,用户必须重新启动应用程序或在系统尝试再次自动启动应用程序之前重新启动设备。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler

如果你仔细阅读,你会注意到现在说:

当您的应用程序在前台或 后台运行时,系统会调用此方法。

不:

不pipe你的应用程序的状态如何

所以它看起来像从iOS 8 +我们是不幸的:(

问题已在iOS 7.1 Beta 3中得到解决。我再次检查,我确认它工作得很好。

要在iOS应用程序开发中使用后台推送下载,以下是我们需要遵循的一些重要事项…

  1. 在info.plist文件中启用具有远程通知值的UIBackgroundModes项。

  2. 然后在你的AppDelegate文件中实现下面的方法。

    application:didReceiveRemoteNotification:fetchCompletionHandler

更多细节: ios-7-true-multitasking

嘿家伙很简单,你可以打电话给你的方法

 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler { } 

脚步:

  1. 项目 – >function – >背景模式,并select“背景获取”和“远程通知”的checkbox,或进入.plist并select一行并给名称“背景模式”,它会创build一个数组,设置带有“远程通知”string的“Item 0”。

  2. 向服务器端开发者说,他应该发送

    “aps”:{“content-available”:1}

那现在你可以调用你的方法:

 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler { } 

代码工作获取远程通知,在后台模式启用远程通知function,我也有背景获取启用(我不知道是否有必要)我使用此代码:

 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler{ DLog(@"899- didReceiveRemoteNotification userInfo: %@",userInfo); NSDictionary *custom=userInfo[@"custom"]; if(custom){ NSInteger code = [custom[@"code"] integerValue]; NSInteger info = [custom[@"info"] integerValue]; NSDictionary *messageInfo = userInfo[@"aps"]; [[eInfoController singleton] remoteNotificationReceived:code info:info messageInfo:messageInfo appInBackground:[UIApplication sharedApplication].applicationState==UIApplicationStateBackground]; handler(UIBackgroundFetchResultNewData); } } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{ DLog(@"899- didReceiveRemoteNotification userInfo: %@",userInfo); NSDictionary *custom=userInfo[@"custom"]; if(custom){ NSInteger code = [custom[@"code"] integerValue]; NSInteger info = [custom[@"info"] integerValue]; NSDictionary *messageInfo = userInfo[@"aps"]; [[eInfoController singleton] remoteNotificationReceived:code info:info messageInfo:messageInfo appInBackground:[UIApplication sharedApplication].applicationState==UIApplicationStateBackground]; } } - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { //NSLog(@"My token is: %@", deviceToken); const unsigned *tokenBytes = (const unsigned *)[deviceToken bytes]; NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]), ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]), ntohl(tokenBytes[6]), ntohl(tokenBytes[7])]; [[eInfoController singleton] setPushNotificationToken:hexToken]; } - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { NSLog(@"Failed to get token, error: %@", error); } 

代码存储通知时,它的背景,我的关键是开始后台下载任务,让我下载的信息,以便存储它,然后当应用程序成为活动的方法被触发我检查是否有一个缺less通知存储来展示它。

 -(void)remoteNotificationReceived:(NSInteger)code info:(NSInteger)info messageInfo:(NSDictionary*)messageInfo appInBackground:(BOOL)appInBackground{ DLog(@"Notification received appInBackground: %d,pushCode: %ld, messageInfo: %@",appInBackground, (long)code,messageInfo); switch (code){ case 0: break; case 1: { NSArray *pendingAdNotifiacations=[[NSUserDefaults standardUserDefaults] objectForKey:@"pendingAdNotifiacations"]; NSMutableDictionary *addDictionary=[[NSMutableDictionary alloc] initWithDictionary:messageInfo copyItems:YES]; [addDictionary setObject:[NSNumber numberWithInteger:info] forKey:@"ad_id"]; if(!pendingAdNotifiacations){ pendingAdNotifiacations=[NSArray arrayWithObject:addDictionary]; }else{ pendingAdNotifiacations=[pendingAdNotifiacations arrayByAddingObject:addDictionary]; } [addDictionary release]; [[NSUserDefaults standardUserDefaults] setObject:pendingAdNotifiacations forKey:@"pendingAdNotifiacations"]; [[NSUserDefaults standardUserDefaults] synchronize]; DLog(@"pendingAdNotifiacations received: %@.",pendingAdNotifiacations); [[UIApplication sharedApplication] setApplicationIconBadgeNumber:(pendingAdNotifiacations)?[pendingAdNotifiacations count]:0]; DLog(@"783- pendingAdNotifiacations: %lu.",(unsigned long)((pendingAdNotifiacations)?[pendingAdNotifiacations count]:0)); if(appInBackground){ [AdManager requestAndStoreAd:info]; }else{ [AdManager requestAndShowAd:info]; } } break; default: break; } } 

这是使用后台任务在后台下载信息的相关代码:

 -(void)requestAdinBackgroundMode:(NSInteger)adId{ DLog(@"744- requestAdinBackgroundMode begin"); if(_backgroundTask==UIBackgroundTaskInvalid){ DLog(@"744- requestAdinBackgroundMode begin dispatcher"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ DLog(@"744- passed dispatcher"); [self beginBackgroundUpdateTask]; NSURL *requestURL=[self requestURL:adId]; if(requestURL){ NSURLRequest *request = [NSURLRequest requestWithURL:requestURL]; NSURLResponse * response = nil; NSError * error = nil; DLog(@"744- NSURLConnection url: %@",requestURL); NSData * responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error]; if(NSClassFromString(@"NSJSONSerialization")) { NSError *error = nil; id responseObject = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; if(error) { NSLog(@"JSON reading error: %@.",[error localizedDescription]); /* JSON was malformed, act appropriately here */ } else{ if(responseObject && [responseObject isKindOfClass:[NSDictionary class]]){ if(responseObject && [[responseObject objectForKey:@"success"] integerValue]==1){ NSMutableDictionary *adDictionary=[[[NSMutableDictionary alloc] initWithDictionary:[responseObject objectForKey:@"ad"]] autorelease]; DLog(@"744- NSURLConnection everythig ok store: %@",adDictionary); [self storeAd: adDictionary]; } } } } } // Do something with the result [self endBackgroundUpdateTask]; }); } } - (void) beginBackgroundUpdateTask { DLog(@"744- requestAdinBackgroundMode begin"); _backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [self endBackgroundUpdateTask]; }]; } - (void) endBackgroundUpdateTask { DLog(@"744- End background task"); [[UIApplication sharedApplication] endBackgroundTask: _backgroundTask]; _backgroundTask = UIBackgroundTaskInvalid; } 

那么这就是我所想的,我发布它是因为有人要求我发布更新,我希望它可以帮助别人…

这是我今天的一个问题,我很困惑。

iOS:10.2.1 xCode:8.2.1 Swift:3.0.2

问题是只有一个电话,我会得到只有插入xCode时打包。

如果我错过了新的UserNotifications框架,我会重新阅读Apples推送文档,或者使用我的代码搞砸某些东西,以回到iOS 9的折旧委托函数。

无论如何,我注意到application:didReceiveRemoteNotification:fetchCompletionHandler: 的文档中的这一行application:didReceiveRemoteNotification:fetchCompletionHandler: ::

“处理远程通知时使用大量电源的应用程序可能不会总是提前被唤醒,以处理未来的通知。”

这是网页上的最后一行。

虽然我希望苹果告诉我更多,但事实certificate,简单的电话重启解决了我的问题。 我真的希望我能弄清楚到底出了什么问题,但是这是我非常推测的结论:

1)推送通知没有被传递到这个特定的手机上的应用程序,因为上述文件中的行。

2)插入xCode时,iOS忽略了上述文档规则。

3)我在系统设置中检查了(臭名昭着的混淆)电池百分比计算器。 它显示我的应用程序在一个适度的6%,但safari是这个手机高达75%,出于某种原因。

4)手机重启后,Safari回落到25%

5)推后工作正常。

所以…我的最终结论 要清除logging的电池问题,请尝试重新启动手机或尝试使用其他手机,看看问题是否仍然存在。

花了两天的时间! 在检查你的代码和推送参数之前 – 检查你是否在低功耗模式!!!(和背景应用程序刷新为开),当你连接你的设备xCode ==电源将工作,但如果你将它断开连接 – 低功耗模式将禁用后台应用程序刷新。