I've a very strange problem, I implemented:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
For silent remote push notification.
It works perfect when app is in background and connected to Xcode.
When I unplug any iOS device and run the app, move to background and send remote notification, didReceiveRemoteNotification:fetchCompletionHandler
not being called.
My code below:
- (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;
}
}
And [self endBackgroundTask]
is at the end of cloudAcknowledge
function.
Any idea what the hell is going on here?
EDIT:
Payload goes like this:
{ aps = { "content-available" = 1; }; pushCode = 12; }
Maybe some of you guys already figured out this, but I posting here since I don't see a clear answer.
The had the exact same problem describe. Silent push notifications worked while the Lightning cable was connected. Stopped working when I disconnected the cable. I had every NSLog and network call tracked to prove that was indeed happening.
I was using the payload suggested in many answers, as well as this one:
After many hours, I discovered that the issue is related to Adhoc and Development provisioning profiles, on iOS 8.0, 8.1 and 8.1.1. I was using Crashlytics to send beta versions of my app (that uses Adhoc profile).
The fix is:
In order to have it working, try out Apple's Test Flight integration with iTunes Connect. Using that you will send an Archive of your app (the same archive to be used on App Store) but enable your binary to be used in beta. The version installed from Test Flight probably (I can't prove) uses the same Production Provisioning Profile from the App Store, and the app works like a charm.
Here's a link that helps set up the Test Flight account:
http://code.tutsplus.com/tutorials/ios-8-beta-testing-with-testflight--cms-22224
Not even a single Silent Payload was missed.
I hope that helps someone not to lose a whole week on this issue.
To use Background Push Download in iOS application development, here are some important points which we need to follow…
Enable
UIBackgroundModes
key with remote-notification value in info.plist file.Then implement below method in your AppDelegate file.
application:didReceiveRemoteNotification:fetchCompletionHandler
More Details:ios-7-true-multitasking
This was an issue for me today and I was baffled.
iOS: 10.2.1 xCode: 8.2.1 Swift: 3.0.2
The issues was only on one phone I would get the packed only when plugged into xCode.
I re-read Apples push documentation in case I missed something with the new
UserNotifications
framework and or messed something up with my code to fall back to the depreciated delegate functions for iOS 9.Anyway, I noticed this line in the documentation for
application:didReceiveRemoteNotification:fetchCompletionHandler:
:"Apps that use significant amounts of power when processing remote notifications may not always be woken up early to process future notifications."
It's the very last line on the page.
While I wish Apple told me more, it turns out a simple phone restart solved the problem for me. I really wish I could figure out exactly what went wrong, but here are my very speculative conclusions:
1) Push notifications were not being delivered to this app on this particular phone because of the line in the documentation mentioned above.
2) When plugged into xCode iOS is ignoring the above, documented rule.
3) I checked the (notoriously confusing) battery percentage calculator in system settings. It showed my app at a modest 6%, BUT Safari was a whopping 75% on this phone for some reason.
4) After phone restart, Safari was back down to about 25%
5) Push worked fine after that.
So... My ultimate conclusion. To weed out the documented battery issue either try a phone restart or try a different phone and see if the problem persists.
Code that works fetching remote notifications, enable te remote notifications capability in background modes and i have background fetch enabled too (i don't know if it is necessary) I use this code:
Code that stores the notification when it background, the key for me was to start a background download task to allow me to download the information in order to store it and then when app becomes active method is triggered i check if there is a missing notification stored to show it.
This is the relevant code to download the info in the background using a background task:
Well this is all I think, I post it because someone asked me to post an update, I hope it may help someone...
Spent two days on this! Before checking your code and your push params - check that you are not on LOW POWER MODE!!!(and Background App Refresh is ON) as you connect your device to xCode==power it will work, but if you will disconnect it - low power mode will disable background app refresh.
Issue have been fixed in iOS 7.1 Beta 3. I double checked and I confirm it's working just fine.