Open specified URL in the Webview (iOS push) - ios

in my iOS App i have an UIWebView so how i can open a specified URL in the UIWebView, with a push notification?
If somebody open the App with the notification, i want to show a specified Website in
the UIWebView.
Can I bind the URL (in background) with the push notification?
Thank you.

According to Apple...
If the app is running and receives a remote notification, the app
calls this method to process the notification. Your implementation of
this method should use the notification to take an appropriate course
of action. ... If the app is not running when a push notification
arrives, the method launches the app and provides the appropriate
information in the launch options dictionary. The app does not call
this method to handle that push notification. Instead, your
implementation of the application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method needs to get the
push notification payload data and respond appropriately.
So, there are three possible scenarios:
1) App is in foreground: you will have full control, just implement didReceiveNotification and do whatever you want.
2) App is running, but in background: the action won't be triggered until the user actually open your app using the received notification.
3) The app is not running: in this case you should implement didFinishLaunchingWithOptions in order to get the additional info and perform the task.
So the code should look like this for didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if(apsInfo) {
// Get the URL or any other data
}
}
And this is an approximation for didReceiveNotification
/**
* Remote Notification Received while application was open.
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
#if !TARGET_IPHONE_SIMULATOR
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
NSString *message = nil;
id aps = [userInfo objectForKey:#"aps"];
if ([aps isKindOfClass:[NSDictionary class]]) {
message = [aps objectForKey:#"alert"];
}
if (message) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Notificación"
message:message
delegate:self
cancelButtonTitle:#"Aceptar"
otherButtonTitles:nil, nil];
[alertView show];
}
}
// Aditional data
NSString *url = [userInfo objectForKey:#"url"];
NSLog(#"Received Push URL: %#", url);
if(url!=nil)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
NSLog(#"remote notification: %#",[userInfo description]);
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alert = [apsInfo objectForKey:#"alert"];
NSLog(#"Received Push Alert: %#", alert);
NSString *sound = [apsInfo objectForKey:#"sound"];
NSLog(#"Received Push Sound: %#", sound);
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
NSString *badge = [apsInfo objectForKey:#"badge"];
NSLog(#"Received Push Badge: %#", badge);
application.applicationIconBadgeNumber = [[apsInfo objectForKey:#"badge"] integerValue];
#endif
}

Related

How to open specific controller when user taps on Push Notifications when App is NOT running (i.e Totally Killed) Objective-C

I'm able to send push notifications to my IOS device. But when I click on that notification it just opens the app.when my app is in background or in foreground it works perfectly, I want the app to open and navigate to a specific view controller depending on the push notification received.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Print message ID.
NSLog(#"Message ID: %#", userInfo[#"gcm.message_id"]);
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
NSDictionary *apsDict = [userInfo objectForKey:#"aps"];
NSString *alertForegroundMessage = [NSString stringWithFormat:#"%#", [apsDict objectForKey:#"alert"]];
// Pring full message.
NSLog(#"%#", userInfo);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
// Check result of your operation and call completion block with the result
completionHandler(UIBackgroundFetchResultNewData);
NSMutableArray *arrnotification;
arrnotification = [userInfo valueForKey:#"aps"];
});
if (application.applicationState == UIApplicationStateActive)
{
NSDictionary *userInfo = [[NSDictionary alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:#"downloadDataFromServer" object:self userInfo:userInfo];
NSLog(#"User Info : %#",userInfo);
completionHandler(UIBackgroundFetchResultNewData);
}
else
{
NSLog(#"userInfo->%#", [userInfo objectForKey:#"aps"]);
NSDictionary *sentObject = [NSDictionary dictionaryWithObjectsAndKeys:noteDict,#"data", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"downloadDataFromServer" object:sentObject];
completionHandler(UIBackgroundFetchResultNewData);
}
completionHandler(UIBackgroundFetchResultNewData);
}
When the app gets launched after the user opens the app from the notification. The best way to handle navigation based on the notification is to check the launchOptions dictionary in the application(_:didFinishLaunchingWithOptions:) method. This should contain the remoteNotification- key, which holds the push notification data.
As mentioned in the discussion section of application(_:didReceiveRemoteNotification:fetchCompletionHandler:) -method, documentation, the system does not automatically launch your app.
application(_:didFinishLaunchingWithOptions:)

iOS- Push notification issue

Currently one of my php developer provided me push notification API for iOS devices
The problem is : If i run that api with respective parameter in any Browser(Chrome/Safari/Firefox and etc..) i am getting notification on foreground of iOS device. But not in iOS app(Xcode) itself
In my app i used code like:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Register for Push Notitications, if running on iOS 8
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0){
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
}
#pragma mark
#pragma mark -- Push Notification Delegate Methods
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings: (UIUserNotificationSettings *)notificationSettings{
//register to receive notifications
[application registerForRemoteNotifications];
}
-(void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken{
// Prepare the Device Token for Registration (remove spaces and < >)
devToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
NSLog(#"My token is: %#", devToken);
// My token is: cd2887c4093569b3eed142320f21a81e521e486cf5c40467390901d3a191398b
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:deviceToken forKey:#"deviceToken"];
}
-(void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error{
NSLog(#"Failed to get token, error: %#", error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSLog(#"%s..userInfo=%#",__FUNCTION__,userInfo);
}
I am getting response:(in didReceiveRemoteNotification)
{
aps = {
alert = "You're login successfully";
sound = default;
};
}
This message is not showing on Status bar(top of the screen). Is there any issue in iOS side (or) PHP side
If the issue is in iOS side--> How can i do this
Here is my Testing Push notification API:
https://shopgt.com/mobile1/iphone/register.php?device_type=2&email=sukhpal#anaad.net&regId=4d1d9067cc1382ecb8b0532831cce7fc8eb6fc388a6139060cd84712407a0ae5
Can you please help me out regarding this issue
You need to customize the view for showing Banner of Push Notification while the app in Foreground. You can use JCNotificationBannerPresenter. Follow the sample code using below link.
https://github.com/jcoleman/JCNotificationBannerPresenter
#import "JCNotificationCenter.h"
#import "JCNotificationBannerPresenterSmokeStyle.h"
- (void) application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)notification {
NSString* title = #"Push Notification";
NSDictionary* aps = [notification objectForKey:#"aps"];
NSString* alert = [aps objectForKey:#"alert"];
[JCNotificationCenter
enqueueNotificationWithTitle:title
message:alert
tapHandler:^{
NSLog(#"Received tap on notification banner!");
}];
}
Hope it Helps you..!
There is no issue, this is default behaviour.
The banner that appears at the top of the screen when you are on the Home screen does not appear when you are inside the app.
You need to get the app to do something with the notification yourself, inside didReceiveRemoteNotification. This could be showing an alert, adding a badge to the tab bar, etc. but banners only show when you are outside of the app.

stringByEvaluatingJavaScriptFromString in didReceiveRemoteNotification but after webView is loaded

I am facing a problem in iOS push notification in a Cordova hybrid app, need guidance/help in identifying whether I am doing it the right way?
Here is the flow ->
User recieved push notification while app is in background -> Tap on the notification from the notification center -> App is opened (invoked) -> Get the custom payload values -> perform a JS Callback using stringByEvaluatingJavaScriptFromString -> The JS function will do the necessary actions the app has to do.
Here is my code where I am doing it
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[Pushbots sharedInstance] receivedPush:userInfo]; //Using Pushbots notification platform
NSString* Value1 = [userInfo objectForKey:#"val1"];
NSString* Value2 = [userInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
[self.viewController.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
}
Everything works till my NSLog(#"%#", jsCallBack);, and it fails at the jsCallBack. I understood that by the time my jsCallBack is being called, the webView is not yet ready, as the app is invoked from background.
What do I do to make this work? How to check whether my webView is ready, and then perform my jsCallBack?
How do I do this when the app is not running at all? For e.g., a user received a Whatsapp message and the app is not in background/foreground. But when the user taps on the message from the notification center, the whatsapp app opens with the user chat screen.
PS: All this code perfectly works when the app is in foreground. That means, the webView is already there and so jsCallBack has no problem.
If the app is in open or in background, then you can execute the javascript directly:
//This will be called if the app was open, in the foreground (active) or in the background and you reopen it from a push message
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[Pushbots sharedInstance] receivedPush:userInfo]; //Using Pushbots notification platform
NSString* Value1 = [userInfo objectForKey:#"val1"];
NSString* Value2 = [userInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
// If the app is in the foreground just execute the javascript
if ( application.applicationState == UIApplicationStateActive ) {
[self.viewController.webView stringByEvaluatingJavaScriptFromString: jsCallBack];
} else {
// If the app was in background, then force to execute the js code on the main thread
[self.viewController.webView performSelectorOnMainThread:#selector(stringByEvaluatingJavaScriptFromString:) withObject:jsCallBack waitUntilDone:NO]
}
}
If the app was completelly closed, then you can add an observer that will listen for the CDVPageDidLoadNotification (cordova finish loading), so you store the userInfo in a new variable myUserInfo and wait until the page is loaded to use it:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//Don't remove the existing code, just paste this before the return YES
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cordovaDidLoad) name:CDVPageDidLoadNotification object:self.viewController.webView];
//This will be called if the app was closed completelly and you open it from a push
if (launchOptions) { //launchOptions is not nil
NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
self.myUserInfo = userInfo;
}
return YES;
}
Then, when cordova is loaded this method will be called:
- (void)cordovaDidLoad {
//Check that myUserInfo isn't null, that will mean that the apps was completelly closed and it was opened from the push notification
if (self.myUserInfo) {
NSString* Value1 = [self.myUserInfo objectForKey:#"val1"];
NSString* Value2 = [self.myUserInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
[self.viewController.webView stringByEvaluatingJavaScriptFromString: jsCallBack];
}
}

Push notification - alert missing from payload

This is the userInfo I am getting in didReceiveRemoteNotification
aps = {
badge = 2;
sound = default;
};
u = "{\"custom\":{\"redirectlink\":\"groupdetail.html?groupid=314416&selectedtabid=5\"}}";
}
Clearly alert field is missing here. This is what I tried to make to handle missing alert :
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
NSMutableDictionary *pushDict = [NSMutableDictionary dictionaryWithDictionary:[userInfo objectForKey:#"aps"] ];
BOOL isSilentPush = [[pushDict objectForKey:#"content-available"] boolValue];
[pushDict setObject:#"You have a notification" forKey:#"alert"];
NSMutableDictionary * mut = [NSMutableDictionary dictionaryWithDictionary:userInfo]; //[userInfo copy];
[mut setObject:pushDict forKey:#"aps"];
//[mut setObject:#"5#" forKey:#"p"];
if (isSilentPush) {
NSLog(#"Silent push notification:%#", userInfo);
//load content here
// must call completionHandler
completionHandler(UIBackgroundFetchResultNewData);
}
else {
[[PushNotificationManager pushManager] handlePushReceived:mut];
// must call completionHandler
completionHandler(UIBackgroundFetchResultNoData);
}
}
But this does not generate a banner notification. Only badge, sound and alert is generated.
I know this should be handled from server side and not from iOS application, but the server guy will be back in 2 days and I have a demo to show.

didReceiveRemoteNotification:fetchCompletionHandler not being called when app is in background and not connected to Xcode

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; }
There could be number of things might have gone wrong, The first from my own experience. In order to make silent push notification work. Your payload has to be structured correctly,
{
"aps" : {
"content-available" : 1
},
"data-id" : 345
}
Does your push message has content-available: 1 if not then iOS will not call the new delegate method.
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
Possible reason is that Background App Refresh is off on your iPhone.
You can turn this option on/off in Settings->General->Background App Refresh.
When Background App Refresh is off on your phone, didReceiveRemoteNotification:fetchCompletionHandler method will be called only when the phone is connected to XCode.
Just want to add an updated answer.
I am facing the same problem.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
Doesn't get called when the app is killed from background multitasking (double tap home button and swipe up to kill app).
I have tested this myself using development push notification and NWPusher tool (https://github.com/noodlewerk/NWPusher)
Outdated documentation
This previous block of documentation which says:
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running, the system calls this method
regardless of the state of your app. If your app is suspended or not
running, the system wakes up or launches your app and puts it into the
background running state before calling the method. If the user opens
your app from the system-displayed alert, the system calls this method
again so that you know which notification the user selected.
Is outdated (at the time of writing this 04/06/2015).
Updated Documentation (as at of 04/06/2015)
I checked the documentation (at the time of writing this 04/06/2015), it says:
Use this method to process incoming remote notifications for your app.
Unlike the application:didReceiveRemoteNotification: method, which is
called only when your app is running in the foreground, the system
calls this method when your app is running in the foreground or
background. In addition, if you enabled the remote notifications
background mode, the system launches your app (or wakes it from the
suspended state) and puts it in the background state when a remote
notification arrives. However, the system does not automatically
launch your app if the user has force-quit it. In that situation, the
user must relaunch your app or restart the device before the system
attempts to launch your app automatically again.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:
If you read carefully, you'll notice it now says:
the system
calls this method when your app is running in the foreground or
background.
NOT:
regardless of the state of your app
So it looks like from iOS 8+ we're out of luck :(
TL;DR: Use Test Flight in iTunes Connect
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:
{
"aps" : {
"content-available": 1,
sound: ""
}
}
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.
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.
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
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.
It is very simple. You can call your method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler {
}
Steps:
project -->Capablities--> Background Modes
and select check boxes of "Background Fetch" & "Remote notifications", or go into .plist and select a row & give name "Background Modes" and it will create with an array, set "Item 0" with string "Remote notifications".
say to server side developer that he should send
"aps" : {
"content-available" : 1
}
thats it now you can call your methods:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler {
}
Issue have been fixed in iOS 7.1 Beta 3.
I double checked and I confirm it's working just fine.
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:
- (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);
}
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.
-(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;
}
}
This is the relevant code to download the info in the background using a background task:
-(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;
}
Well this is all I think, I post it because someone asked me to post an update, I hope it may help someone...

Resources