Background Fetch and Local Notification - ios

In my app, I am planning to use Local Notification and not Push Notifications. I need to check whether a transaction update is available, if YES, just notify the user with Local Notification. I have done the below steps.
In didFinishLaunchingWithOptions, set fetch interval [[UIApplication sharedApplication]setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum].
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
> Added code for POST Request as below with my request data:
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if(conn) {
completionHandler(UIBackgroundFetchResultNewData);
}
else {
completionHandler(UIBackgroundFetchResultFailed);
}
After response parsing, comparing if any update is there and if any update is there, notification is displayed using the below code.
UILocalNotification* localNotification = [[UILocalNotification alloc] init]; localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertTitle = #"SIB Mirror";
localNotification.alertBody = #"You have a new transaction. Go to e-Statements to view it.";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
The issue is most of the times, when phone is locked, getting error message
>Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
and hence notification was not getting displayed. But when I connect Phone directly to Phone and in debug mode enable background sync, every thing works properly.
Kindly guide me an easy approach to do this or correct me if anything I am doing wrongly.
Thanks,
Mobile Developer.

This part of your code
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if(conn) {
completionHandler(UIBackgroundFetchResultNewData);
}
else {
completionHandler(UIBackgroundFetchResultFailed);
}
Is saying, create me a connection to get some data, and no matter what happens with the creation of this instance complete the background processing. So iOS does as you ask and stops the app.
If the connection can't be created then complete with failed, sure. But if the connection was created you shouldn't be calling completion until you have a response (data or error) to process...

Try the below code:
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error == nil)
{
// Parse data here
completionHandler(UIBackgroundFetchResultNewData);
}
else {
completionHandler(UIBackgroundFetchResultFailed);
}

Try POST Request with completion handler.

Related

iOS XMPP check connection to the internet

i am working on XMPP based application. i want to setup that is user's device is not connected with internet and they try to login in application , then alertview will appear that you are not connected to internet. i tried this but did not get success.
Here is my try. what should i have to modify?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (![self connect])
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"not connected.");
});
}
Checking the internet connection by performing a HEAD request is a very bad way to do what you want. Your user may have to wait for about 60 seconds (a default timeout for http requests) to determine he has no internet connection! This is definitely NOT what user waits for your product to act like!
There is a best way to determine the availability of internet connection - it is a Reachability monitoring functions. There is a nice block-based wrapper for it I highly recommend you to look at.
Try this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Check internet connectivity
if(![self connectedToInternet])
{
UIAlertView *uiAlertView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(#"Connection Error", #"Connection Error")
message:#"you are not connected to internet"
delegate:nil
cancelButtonTitle:NSLocalizedString(#"Ok", #"Ok")
otherButtonTitles:nil];
[uiAlertView show];
}else
{ //internet connection was connected
}
return YES;
}
- (BOOL)connectedToInternet
{
NSURL *url=[NSURL URLWithString:#"http://www.google.com"];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
return ([response statusCode]==200)?YES:NO;
}

How to send local notification

I'm trying to send notifications in objective c but I can't figure out how.
So far I have
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if(self.username != nil)
{
NSLog(#"Background fetch username: %#", self.username);
[self fetchNotifications];
}
completionHandler(UIBackgroundFetchResultNewData);
}
- (void)fetchNotifications
{
NSURL *url = [NSURL URLWithString:
[NSString stringWithFormat:#"%#%#",
#"https://myurl?username="
, self.username]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *notifications = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
int nbOfNewMatch = [[notifications objectForKey:#"NbOfNewMatch"] intValue];
int nbOfNewPlanification = [[notifications objectForKey:#"NbOfNewPlanification"] intValue];
//int nbMessages = [[notifications objectForKey:#"NbOfUnreadMessage"] intValue];
NSLog(#"Notifications - nbMessage: %i",nbMessages);
NSLog(#"Notifications - nbNewMatch: %i",nbOfNewMatch);
NSLog(#"Notifications - nbNewPlan: %i",nbOfNewPlanification);
// Schedule the notification
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = nil;
localNotification.alertBody = #"test";
localNotification.alertAction = #"Show me the item";
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}];
}
When I try on the iPhone 6 simulator with IOS 8.1 I have the following error
Attempting to badge the application icon but haven't received permission from the user to badge the application
I will have to read and understand this post
but my real problem is when I use an iPhone 5 IOS 7.1 simulator, the function fetchNotifications is not even called.
Why fetchNotifications is only called under IOS 8.1?
performFetchWithCompletionHandler is called under both OS
Attempting to badge the application icon but haven't received permission from the user to badge the application
is the way Xcode tells you that you can't change the notifications badge value while running on the simulator. It's not allowed
This delegate method is called when iOS decide. From the UIApplicationDelegate documentation:
When an opportunity arises to download data, the system calls this method to give your app a chance to download any data it needs. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads. If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future.

Sending a UILocationNotification from the background

I have an app where I need to send a UILocationNotification when the app is not in the foreground / Active. If I have my code to do so, it will not send until the app is opened and is active.
Here is my function:
- (void)setLocalNotificationForType:(SPKLocalNotificationType)notificationType fromUser:(NSString *)userName withMessageText:(NSString *)msgText {
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.userInfo = #{#"handle": _convoHandle, #"room": _convoName};
notif.fireDate = [NSDate date];
notif.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
notif.soundName = UILocalNotificationDefaultSoundName;
notif.timeZone = [NSTimeZone defaultTimeZone];
if (notificationType == 1) { // message
notif.alertBody = [NSString stringWithFormat:#"#%# said: \"%#\"", userName, msgText];
} else if (notificationType == 2) { // image
notif.alertBody = [NSString stringWithFormat:#"#%# sent an image", userName];
} else {
return;
}
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
[[UIApplication sharedApplication] presentLocalNotificationNow:notif];
}
}
Update:
It now seems that the problem is that the connection to the server is being "paused" while the app is in the background. Once I then open the app all the data comes in at once. I am using SocketRocket for connecting to my Node.js Primus web socket server. Is this something that normally happens? This is my first time using SocketRocket so I'm not sure.
Update:
I have also enabled Remote Notifications for Background Modes, I have also registered for remote notifications, and on the device, I have also made sure that "banners" and "badges" are enabled.
Update:
I have additionally set the web socket to use a background queue.
[webSocket setDelegateOperationQueue:[NSOperationQueue new]];
The connection is still being paused.
Thanks!
Edit: It looks like SocketRocket uses the main dispatch queue by default, which runs on the application's main thread. When the app is backgrounded, processing on the main thread stops, so it would be worth trying to move the work to a background thread in a background operation queue.
On your SRWebSocket object, try calling:
[webSocket setDelegateOperationQueue:[NSOperationQueue new]];
Without checking the docs, perhaps setting timeZone and fireDate on the UILocalNotification is interfering. You don't need those if you're going to pass the notification to presentLocalNotificationNow:.
I have also found that, even if you're using local notifications exclusively, you must:
Enable Remote notifications for your target on the Capabilities tab under Background Modes
Enable either alerts or banners in the Settings app, in Notification Center settings.
A sample event handler for a background fetch:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
// ... perform a network request
if (successfulNetworkRequest) {
UILocalNotification* localNotification = [UILocalNotification new];
localNotification.alertAction = #"View";
localNotification.alertBody = #"Stuff";
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1;
localNotification.userInfo = #{#"stuff": #"other stuff"};
[application presentLocalNotificationNow:localNotification];
result = UIBackgroundFetchResultNewData;
} else {
result = UIBackgroundFetchResultFailed;
}
completionHandler(result);
}
I am afraid you are misunderstanding of local notification. I guess what you should do is registering a notification in the future and send it some time after user clicked the home button. But in your code, you registered a alert in CURRENT TIME and did some thing on the notification (sorry but I don't know what you want to do in your code).
You could change it like this to make a sense:
- (void)setLocalNotificationForType:(SPKLocalNotificationType)notificationType fromUser:(NSString *)userName withMessageText:(NSString *)msgText {
UILocalNotification *notif = [[UILocalNotification alloc] init];
notif.userInfo = #{#"handle": _convoHandle, #"room": _convoName};
//Set the fire time 10 seconds later
notif.fireDate = [[NSDate date] dateByAddingTimeInterval:10];
notif.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
notif.soundName = UILocalNotificationDefaultSoundName;
notif.timeZone = [NSTimeZone defaultTimeZone];
if (notificationType == 1) { // message
notif.alertBody = [NSString stringWithFormat:#"#%# said: \"%#\"", userName, msgText];
} else if (notificationType == 2) { // image
notif.alertBody = [NSString stringWithFormat:#"#%# sent an image", userName];
} else {
return;
}
//Register this notification
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
And in your app delegate's -applicationDidEnterBackground:, call your -setLocalNotificationForType:fromUser:withMessageText: method to make and register a notification. Then you can expect a local notification 10s after you click the home button.

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...

Alert views with if statements

I am creating an alarm app that sends udp data when the alarm goes off. At the moment the udp data is sending as soon as the alarm is set, not when the alarm goes off. I was thinking i could do this using an if statement? I think this is the right bit of the code. Please help!!
-(void)scheduleloalNotificationWithDate:(NSDate *)fireDate {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = fireDate;
notification.alertBody = #"Time For Coffee!";
SwitchOn= [sendOn dataUsingEncoding:NSUTF8StringEncoding];
NSString *newIP = [setting IP];
NSLog(#"IP = %#", newIP);
if(!newIP){
[socket sendData: SwitchOn toHost: #"192.168.0.2" port: 5900 withTimeout:-1 tag:1];
// NSLog(#"cancel");
}
else{
[socket sendData:SwitchOn toHost: newIP port: 5900 withTimeout:-1 tag:1];
}
[[UIApplication sharedApplication] scheduleLocalNotification: notification];
[notification release];
}
The application might be terminated after the notification is scheduled but before fired. As a user i could dismiss the notification not opening the app, which means the data will not always be sent. There also will appear a race condition for multiple updates if the user is using the same app with several devices.
If you still want to update the data when the application is fired instead of when it is scheduled, the UILocalNotification overview will help you to understand all the situations you'll have to handle.
PS you've missed c at scheduleloalNotificationWithDate

Resources