I want to pull some data from server in every 30 min interval and set local notification to remind the user and i implemented below code to perform this operation.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSString *value = [[NSUserDefaults standardUserDefaults]objectForKey:#"bgFetch"];
if ([Utilities isInternetConnectionAvailable]) {
if ([self.window.rootViewController isKindOfClass:[HomeViewController class]]){
HomeViewController *homeController = (HomeViewController*)self.window.rootViewController;
[homeController fetchDatawithCompletionHandler:^(UserData *userData, NSString *errorResponse) {
if (userData) {
if (![homeController isNotificationAvailvableForTheData:userData]) {
[homeController scheduleLocalNotificationForUserData:userData];
completionHandler(UIBackgroundFetchResultNewData);
}
}else{
completionHandler(UIBackgroundFetchResultFailed);
}
}];
}else{
completionHandler(UIBackgroundFetchResultNoData);
}
}else{
completionHandler(UIBackgroundFetchResultFailed);
}
}
I also enabled background fetch in capability and added the key "Required background modes" in plist. When i checked after few hours, no local notification is set. Where am i doing wrong ?
I was facing the same issue. Whenever I simulated a background fetch via XCode it worked, whether with the emulator or a real device. If I unplugged the phone and just used it as I usually do. I never got a notification, respectively my code doesn't get executed. The solution is very simple and I think the difference to "simulate background fetch" is that your app is in a different stage then it is in your daily routine. To make it work, simply dispatch your code on a background thread. I just wrapped my code inside:
DispatchQueue.global(qos: .background).async
and it worked.
Related
I'm trying to understand how the NSThread is working when the app is going to the background. I have the following code in appdeligate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self backgroundHandler];
}
- (void)backgroundHandler {
NSInteger counter=0;int scontinue=true;
while(scontinue){
NSLog(#"counter:%d",counter++);
sleep(1)
}
}
When I go to the background then it prints out every 1second a value. I have kept it open for about 5min and it gave me:
counter:1
counter:2
...
counter:300
And this keeps going. However if try to get into the foreground the backgroundHandler doesn't exit the while loop and my app doesn't respond to anything.
Now I change the applicationDideEnterBackground and instead I'm using a thread, i.e.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[NSThread detachNewThreadSelector:#selector(backgroundHandler) toTarget:self withObject:nil];
}
- (void)backgroundHandler {
NSInteger counter=0;int scontinue=true;
while(scontinue){
NSLog(#"counter:%d",counter++);
//sleep(1) : I remove the sleep for the shake of the example
}
}
Although I was expecting to have the same behaviour as in the previous case the thread seems to be hold after some ms. So what I had as a result is:
counter:1
counter:2
...
counter:30
And now the thread stucks at that point without executing anything. When I go to the foreground then the thread starts running again, i.e. counter increases and it is being printed out. The application runs again normally.
The above example is a rather simplistic version of what I'm tyring to do. What I actually want is when by app goes to the background to communicate with a server as long as the user doesn't go to the foreground. When it goes any communication should be terminated. So what I actually want is a combination of the simple examples above, i.e. when go to background in while loop keep asking the server, and when I enter to the foreground my app start responding normally and terminate the the backgroundHandler for loop.
Any help?
The main purpose of - (void)applicationDidEnterBackground:(UIApplication *)application is to save the state of the application when the application goes into background. In this if the application starts using a lot of the CPU or RAM, OS will terminate the app based on the state of the phone.
When you want to perform background operations or services to the server enable the background fetch by calling [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions of the AppDelegate.
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
//Add your code here
[self setBackgroundCheckInterval:UIBackgroundFetchResultNewData];
completionHandler(UIBackgroundFetchResultNewData);
}
- (void)setBackgroundCheckInterval:(NSInteger)resultCode
{
UIBackgroundRefreshStatus status = [UIApplication sharedApplication].backgroundRefreshStatus;
if (status == UIBackgroundRefreshStatusAvailable)
{
switch (resultCode)
{
case UIBackgroundFetchResultFailed :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NO_NEW_DATA];
break;
case UIBackgroundFetchResultNewData :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NEW_DATA];
break;
case UIBackgroundFetchResultNoData :
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:BACKGROUND_CHECK_INTERVAL_NO_NEW_DATA];
break;
}
}
}
In my app I am using uilocalnotifications. Every thing is ok but one thing. I need to show notifications's alertbody. If app is in foreground state it's fine, but if app is at background state and notification occurs, when i tap on that didReceiveLocalNotification doesn't get called. Obviously didFinishLaunchingWithOptions is also don't called at that time. So what should i do to handle the notification. I am using ios7 and xcode5. Thanks very much in advance if you could help me.
For an app which is not in the foreground, the local notification can subsequently be found in the
-applicationDidFinishLaunchingWithOptions method
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
//Handle local notification here.
}
You can read Apple's documentation for handling notifications here.
If the app is currently in memory, you can check it's state in the following way:
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif
{
if (app.applicationState == UIApplicationStateInactive )
{
NSLog(#"app not running");
}
else if(app.applicationState == UIApplicationStateActive )
{
NSLog(#"app running");
}
}
if application is closed and notification is raised then for that you have to write below code in appdidfinishlaunching method
// Handle launching from a notification
UILocalNotification *objLocalNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (objLocalNotif)
{
NSLog(#"At the time of launching Recieved Notification %#",objLocalNotif);
//Do your stuff here
}
If application is in background and when any local notification is raised the following method of app delegate get called.
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
// Handle the notificaton when the app is running
NSLog(#"Recieved Notification %#",notif);
//do your stuff here
}
Write your code in the below method
- (void)applicationWillEnterForeground:(UIApplication *)application
it will be called when you open your application again which has not been terminated fully but still running in background
My App receives APNS Push Notifications, and when a user receive more than one NSNotification, he should be able to open the app in a specific view according the NSNotification tapped.
So in the method
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler
I added this code to save all the notifications
if (self.notifications == nil) {
self.notifications = [[NSMutableArray alloc] init];
}
[notifications addObject:userInfo];
And every time the app becomes active again it does this
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started)
// while the application was inactive.
// If the application was previously in the background,
// optionally refresh the user interface.
[notifications removeAllObjects];
application.applicationIconBadgeNumber = 0;
}
Before removing all the objects and setting the badge to zero, I would like to handle which NSNotification made my app open from the background. And once I have which push NSNotification it was, I would like to pass all the data to a specific view.
Based on your comment about UILocalNotification usage
UILocalNotification has a userInfo property. When you create your local notification from the push notification, set the appropriate information into this property and then when the app delegate receives application:didReceiveLocalNotification: you can use that into to update your UI.
If you not use new iOS7 background fetch notifications.
In - (void)applicationDidBecomeActive:(UIApplication *)application before removing all object from notification array, check for notification
NSDictionary * myNotification = [notifications lastObject];
if (myNotification)
{
// is last notification
}
Is will work because app receive only notification that user tap on it
Send with your push notification, data, and use it when your app receives it.
In this example, i'm using the image file and alert, and sending it to all the views that are registered for this Notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
//Posting the notificaiton to the use, if its valid:
NSDictionary *returnDic = [userInfo objectForKey:#"aps"];
if (![returnDic objectForKey:#"alert"]) {
return;
}
NSString *alert = [NSString stringWithFormat:#"%#",[returnDic objectForKey:#"alert"]];
if (![returnDic objectForKey:#"MagnetID"]) {
return;
}
NSString *magnetImage = [returnDic objectForKey:#"MagnetImage"];
NSDictionary *dictionaryToSend = [NSDictionary dictionaryWithObjectsAndKeys:magnetImage,MAGNET_IMAGE,alert,MAGNET_ERROR_MESSEGE, nil];
//Posting to the rest of the views, the messege:
[[NSNotificationCenter defaultCenter] postNotificationName:USER_MESSEGE_RECEAVED object:nil userInfo:dictionaryToSend];
NSLog(#"Notifications - userInfo=%#",userInfo);
}
What you may do, is save the data that you need, by UserDefults or whatever you prefer, and at the "applicationDidBecomeActive" method, use that data to show the right view.
Hope this helps
I am creating an application where I am retrieving data from the server like below:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self retrievedatafromserver];
dispatch_async(dispatch_get_main_queue(), ^{
//UIUpdation, fetch the image/data from DB and update into your UI
});
});
How do I retrieve data from the server even if application goes to background?
Thanks & Regards
sumana
If Your scope of project is in only iOS 7 then you can use A new background mode which comes in the iOS 7 and onwards. You can fetch the data in background mode without any extra efforts of coding.
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
Now that your app already knows to initiate background fetch, let’s tell it what to do. The method -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler will assist in doing so. This method is called every time that a background fetch is performed, and should be included in the AppDelegate.m file. The complete version is provided below:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;
id topViewController = navigationController.topViewController;
if ([topViewController isKindOfClass:[ViewController class]]) {
[(ViewController*)topViewController insertNewObjectForFetchWithCompletionHandler:completionHandler];
} else {
NSLog(#"Not the right class %#.", [topViewController class]);
completionHandler(UIBackgroundFetchResultFailed);
}
}
Now in your controller. Do like that
- (void)insertNewObjectForFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"Update the tableview.");
self.numberOfnewPosts = [self getRandomNumberBetween:0 to:4];
NSLog(#"%d new fetched objects",self.numberOfnewPosts);
for(int i = 0; i < self.numberOfnewPosts; i++){
int addPost = [self getRandomNumberBetween:0 to:(int)([self.possibleTableData count]-1)];
[self insertObject:[self.possibleTableData objectAtIndex:addPost]];
}
/*
At the end of the fetch, invoke the completion handler.
*/
completionHandler(UIBackgroundFetchResultNewData);
}
Note :- If you have to give supportability on iOS 6 and below then avoid this approach. Because it's not available.
When your app enters background mode. you can access code for couple of seconds. Suppose the background queue is still performing and you entered background. then you might need to recall the method when app entered foreground. (take a bool variable and check whether the process is completed or not, if process is completed no issues. if not call the method again.).
If you want to make app run in background mode also then you need to request for background run mode in plist. See this link for reference only for these features we can active background run mode and you can active any of them according to you usage http://blogs.innovationm.com/support-for-applications-running-in-background-ios/
Good evening everyone !
I have a simple question : How to enable [CLLocationManager startUpdateLocation] when I receive a correct push notification using didReceiveRemoteNotification:fetch on iOS 7 ?
Right now, I have :
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if ([userInfo objectForKey:#"aps"] && [[userInfo objectForKey:#"aps"] objectForKey:#"content-available"])
{
if ([userInfo objectForKey:#"update-location"])
{
[self performSelectorInBackground:#selector(handleLocationNotificationPush:) withObject:completionHandler];
}
if ([userInfo objectForKey:#"update-sensors"])
{
}
}
}
-(void)handleLocationNotificationPush:(void (^)(UIBackgroundFetchResult))completionHandler
{
[CLController.locMgr startUpdatingLocation];
++nbPushReceive;
[self.pushLockForLocation lock];
if ([self.pushLockForLocation waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:25]] == TRUE && self.lastKnownLocation != nil)
{
[self.pushLockForLocation unlock];
// Send my new location to server using HTTP request
[self sendLocationForPushUpdates:self.lastKnownLocation fetchCompletionHandler:completionHandler];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
// it stops location updates
[self stopAllLocationUpdates];
return;
}
// In case we didn't receive any new position during 25 secondes
[self.pushLockForLocation unlock];
if (![UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
[self stopAllLocationUpdates];
}
completionHandler(UIBackgroundFetchResultNoData);
}
- (void)locationUpdate:(CLLocation *)location
{
NSLog(#" *** LocationContrller - LocationUpdate location");
self.lastKnownLocation = location;
if (location.horizontalAccuracy < 500)
{
[self.pushLockForLocation signal];
}
}
Of course, my CLController delegate is the same class (location updates work when application is in foreground). In my plist.file, I have the "Remote notifications" checked.
I am missing something ?
Thanks for your help ! :D
This could be related to the fact that in iOS7 enabling Location Services whilst in the background does not give you unlimited background processing time as it did in previous iOS version. Check out the WWDC 2013 What’s New in Core Location video at around 5 minutes 30. Therefore your app gets suspended again around 30 seconds after receiving the push notification.
I have a similar problem which as yet I've not found the solution to. However if you'd like to get to the same place I am try the following -
Firstly put an NSLog in didReceiveRemoteNotification, run your app on the device, put it into the background and send it a push notification. If you see your NSLog you'll know that the content-available flag is set correctly in your push.
Next add an NSLog in -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations. If this gets hint then you know that you are indeed enabling location services.
If you get this far you've probably got the same problem I have. 30 seconds isn't always long enough to get a location to the accuracy I want it.
BTW if you're using an iPhone 5 or newer you can use deferred location and keep location services running constantly. Sadly I need it to work on an iPhone 4s.
UPDATE -
I've now found that this is specifically related to background push notifications and didReceiveRemoteNotification. Enabling Location Manager from a standard background task will work as it did before iOS7. Therefore you can still use background tasks together with Location Manager to get your position every x minutes, you just can't start the whole thing off using a background push.
Answering to myself, and using severals stackoverflow's posts, it's apparently impossible to re-active the location update when you're in background. To keep being updates of your location updates, you have to let it run even if you're going in background !!
:)