UIApplicationBackgroundRefreshStatusDidChangeNotification usage without corresponding delegate method - ios

I feel that UIApplicationBackgroundRefreshStatusDidChangeNotification introduced in iOS 7 is of little use without supporting UIApplication delegate method. Because, the app is not notified when user has switch ON the background refresh state for my app.
This is my notification handler...
- (void)applicationDidChangeBackgroundRefreshStatus:(NSNotification *)notification
{
NSLog(#"applicationDidChangeBackgroundRefreshStatus with notification info = %# and refresh status = %d", notification, UIApplication.sharedApplication.backgroundRefreshStatus);
if (UIApplication.sharedApplication.backgroundRefreshStatus == UIBackgroundRefreshStatusAvailable) {
// if ([CLLocationManager locationServicesEnabled]) {
[self.locationManager startUpdatingLocation];
// }
}
}
As above, I want to start updating core location when UIBackgroundRegreshStatus is made Available through app Settings > General > Background App Refresh. I feel there should have been an appropriate delegate method in UIApplicationDelegate to let the app know about this change so that App could re-establish everything it needs to.
Either I'm missing something (pre-existing API) or Apple SDK engineers have some other/limited intentions about this notification usage. Please advice.

ideally, you never have to check for that setting. It looks as though you are going around background fetching the wrong way. With the application minimised, the system will periodically wake your application up and allow it to perform tasks. From your code , you want to update the location. first place to start is here , using this delegate method that gets called when the app is woken up for a background fetch
/// Applications with the "fetch" background mode may be given opportunities to fetch updated content in the background or when it is convenient for the system. This method will be called in these situations. You should call the fetchCompletionHandler as soon as you're finished performing that operation, so the system can accurately estimate its power and data cost.
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);
this is how you use it, in your application delegate implementation, define the method body as follows
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"APP IS AWAKE FOR A BACKGROUND FETCH");
//do all the work you want to do
//once done, its important to call the completion hadler, otherwise the system will complain
completionHandler(UIBackgroundFetchResultNewData);
}
however, since you are updating the location, which has its own delegates, you only want the completion handler to be called when your delegates return and not before that. Calling the completion handler will send your application back to sleep. Since the completion handler is a block object, it can be passed around like any other object. One way of doing that is as follows: in the application delegate header file, define a block object:
void (^fetchCompletionHandler)(UIBackgroundFetchResult);
then in your performFetchWithCompletionHandler have :
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletionHandler = completionHandler;
NSLog(#"APP IS AWAKE FOR A BACKGROUND FETCH");
//do all the work you want to do
[self.locationManager startUpdatingLocation];
}
at some appropriate time, after your location delegate methods have returned, you call
fetchCompletionHandler (UIBackgroundFetchResultNewData);
be sure to check if your fetchCompletionHandler is non nill, calling it when nil will immediately crash your app. read more on blocks from the apple docs here https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
also have a look at the call [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval: ]; which Specifies the minimum amount of time that must elapse between background fetch operations.
You would probably chuck that into your app delegate application didFinishLaunchingWithOptions method.
hopefully this helps you.

You need to register for notification:
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationBackgroundRefreshStatusDidChangeNotification
object: [UIApplication sharedApplication]
queue:nil
usingBlock:^(NSNotification* notification){
NSLog(#"Just changed background refresh status because of this notification:%#",notification);
}];
You will get this output if your app is running and then you switch and disable background App refresh.
> Just changed background refresh status because of this notification:NSConcreteNotification 0x165657e0 {name = UIApplicationBackgroundRefreshStatusDidChangeNotification; object = <UIApplication: 0x16668670>}
You can obviously select a different queue. Also, this does not have to be in the app delegate. Any class can listens to this notification.
Recalling WWDC sessions, they said this was not available in DP seed 1. I am not sure if this is not an app delegate method now because they did not get to it or that is how they intended it since this is more likely to be used outside the app delegate (in your own model or VC) to listen and adjust to an app background refresh privileges being changed. In the latter case, a notification to anyone who registers for it makes more sense.
Hope this helps.
Update:
Based on the WWDC 2013 Core location video, Location updates that you want to work in the background has to start in the foreground. So this means that once the user turns off the background refresh for the app, location updates would stop and the only thing that would restart location updates, is restarting location updates in the foreground! In that case, a delegate would not be useful since it cannot restart location updates while in background. This is by design.

Related

Trigger backgroundFetch with UILocalnotification?

PeopleInTheKnow!
I'm writing this app, that uses Background Fetch, to see if there's some new information on a server.
Background Fetch works fine, but is pretty unpredictable in when it performs its trick.
Therefore, I though I should use scheduled UILocalNotifications, so I have more control over the exact timing and frequency of the execution of the method associated with Background Fetch.
But this is where I get confused:
My scheduled UILocalNotifications fire as expected. But the delegate
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
// I recieved a notification
NSString * notificationType = [notification.userInfo valueForKey:#"dailyCheck"];
NSLog(#"Received Notification of type %#",notificationType);
}
in which I would like to call the server check method, is only called when the app is active.
This seems to make such a mechanism useless for my case.
Could any of you advise me what would be the best approach, to make sure that my app, be it in the foreground or background, will check the server every day at a a specific time?
I should add, that I don't expect any user interaction here. So, no alerts with buttons or anything like it.
Thanks ahead

Checking whether iOS app is launched from push properly

I am trying to detect if my app was launched from push, and there are tons of threads and answers. They are all wrong in my case, and lead me to writing incorrect behavior. They all tell to write roughly this:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
BOOL wasLaunchedFromPush = NO;
if (application.applicationState == UIApplicationStateInactive) {
wasLaunchedFromPush = YES;
}
...
}
Or this:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
BOOL wasLaunchedFromPush = NO;
if (application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground) {
wasLaunchedFromPush = YES;
}
...
}
When I try this, my app thinks that it was started from a notification when I'm on a phone call, the app is in the background, pretty much always when the app is running but not directly active. When my users return to the app, app acts like they've tapped the notification and opened it, whereas the user hasn't interacted with it at all. I don't know why people accepted that as a correct answer in many questions.
How can I simply check if my app is launched from a notification or not, also regardless of the previous state of the app (running in the background or just launched)?
There is no simple way which is going to tell you that its open from pushNotificaiton specially when app is in active or in background. You have to do some work around,
UIApplicationStateInactive The app is running in the foreground but is
not receiving events. This might happen as a result of an interruption
or because the app is transitioning to or from the background
This is best candidate for the phone call interruption,
You can also add some other work around to check if app ever entered in background or not, if yes then after that if didReceiveRemoteNotification method get called its means its from push notification.
If you only want to know when your app was launched from a remote notification, you should check for this in - application:didFinishLaunchingWithOptions: or - application:willFinishLaunchingWithOptions:. You can check for the presence of UIApplicationLaunchOptionsRemoteNotificationKey in the launchOptions dictionary.
didReceiveRemoteNotification is call when your app is running and a remote notification is received. It is called regardless of any user interaction this is because if the app is running, there will not be a notification (pop up) for the user to interact with.
Checking against UIApplicationStateBackground is definitely wrong. If your push notifications are marked with content-available: 1 then your app is going to be called in that state so that you can perform your content download. This will happen without any user interaction.
The best I know of is using the check against UIApplicationStateInactive alone (i.e. your first code example).
As you say, this doesn't work if the notification arrives while your app has been interrupted by a phone call. That's an interesting problem. You could maybe keep track of call state using CTCallCenter but I've not done that and I don't know if you need VOIP permission to make it work.

How to know whether an app is being terminating in iOS?

- (void)applicationDidEnterBackground:(UIApplication *)application {
//...function_a call
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
//...function_b call
}
- (void)applicationWillTerminate:(UIApplication *)application {
//...function_c call
}
I'm tracking my app with AppDelegates methods. Its working fine, if app just go / up from background to foreground. But when I try to remove my app from the running applications then, it will first call - (void)applicationDidEnterBackground:(UIApplication *)application and then, will call - (void)applicationWillTerminate:(UIApplication *)application. How do I know in - (void)applicationDidEnterBackground:(UIApplication *)application that app is being terminating. By mean, I don't want function_a call twice instead it should only call when app goes into background.
Update:
Setting even following observer doesn't help.
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification
UIApplicationWillTerminateNotification
- (void)applicationWillTerminate:(UIApplication *)application:
This method lets your app know that it is about to be terminated and purged from memory entirely. You should use this method to perform any final clean-up tasks for your app, such as freeing shared resources, saving user data, and invalidating timers. Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app. For apps that support background execution, this method is generally not called when the user quits the app because the app simply moves to the background in that case. However, this method may be called in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
So this is the to go delegate method to know when the app will be terminated, either by the user or by the system, while - (void)applicationWillTerminate:(UIApplication *)application runs whenever the app goes to the background, it doesn't mean it was exitted, unless your exit means not visible

Schedule notification in background task

I'm developing a Calendar/Alarm app for iOS which is synchronising with a web server. When an activity is added on the server, a push notification is sent out so that the iOS client can fetch the new data and, if needed, update and schedule the time for next alarm (local notification).
But this only works when the app is open on client side. I would like the client to receive the push notifications and if needed, re-schedule the time for next alarm in background.
Is this impossible on iOS?
You can use Background Fetch for this, where the OS will "wake up" your app periodically to perform data fetching in the background.
First, enable the background fetch capability for your app. In XCode 6, view your project, then go to the Capabilities tab, turn on Background Modes, and check Background Fetch.
Then you'll have to implement some codes in the App Delegate:
In application:didFinishLaunchingWithOptions:, add:
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
The above sets how often you wish the system to "wake up" your app for background processes ideally. Note that the final frequency is determined by an algorithm in the iOS, so it may not always be this often.
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
//fetch code here
completionHandler(UIBackgroundFetchResultNewData);
}
The above is the actual overridden function that is called during this period of background process. Remember to call the completionHandler - failing to do so might reduce the chance of your app being run in the background next time (or so says the docs). The enums you may pass to the completionHandler are UIBackgroundFetchResultNewData, UIBackgroundFetchResultNoData, UIBackgroundFetchResultFailed. Use one of these depending on the result of your fetch.
// use this methods in Appdeleagte
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[self showAlarm:notification.alertBody];
application.applicationIconBadgeNumber = 1;
application.applicationIconBadgeNumber = notification.applicationIconBadgeNumber-1;
}
// call this in appdelagete
-(void)makeNotificationRequest:(UILocalNotification *)notification1
{
[self showAlarm:notification1.alertBody];
}
// call this mathods in appdelagte
- (void)showAlarm:(NSString *)text {
**strong text**
// set notification and call this notification methods your another view .....
[[NSNotificationCenter defaultCenter] postNotificationName:#"uniqueNotificationName" object:self]; //leak
}

Receive and respond to EKEventStoreChangedNotification in the background?

I was wondering if in iOS7, with the new API's it was finally possible to respond to a notification in the background, in my case, I have the following observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(storeChanged:)
name:EKEventStoreChangedNotification
object:eventStore];
I am receiving the notification perfectly but i need to run the app so that the selector gets called. I've browsed through the response and they say it's not possible but not sure if they where referring to iOS7 specifically.
Any thoughts?
Thanks!
The EKEventStoreChangedNotification will only fire when your app comes to the foreground. However if you want to call your storeChanged: method in the background, and thus having the UI already updated on coming to foreground again, you need to add the Background Fetch capability to your app.
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
In your app delegate didFinishLaunchingWithOptions method add the line
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
This ensures your app actually calls your background fetch, as the default interval is never. This minimum key is the key that ensures iOS handles when to call your background fetch method. You can set your own minimum interval if you don't want it to fire as often as possible.
Finally implement the background fetch method in your app delegate:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[self storeChanged:nil];
completionHandler(UIBackgroundFetchResultNewData);
}
You can test in Xcode while debugging from Debug > Simulate Background Fetch.
Firstly when the app is in the background you can only run methods using the background task APIs to call a method after you've been backgrounded (as long as your task doesn't take too long - usually ~10 mins is the max allowed time). This applies to all versions of iOS even iOS7.
Read this question for more clarifications.
App States and Multitasking Guide by Apple can give you more clarifications on background handling.

Resources