iOS task when application is closed - ios

I am building an app which uploads files to the server via ajax.
The problem is that users most likely sometimes won't have the internet connection and client would like to have the ajax call scheduled to time when user have the connection back. This is possible that user will schedule the file upload when he's offline and close the app. Is it possible to do ajax call when the application is closed (not in background)?
When app is in the background, we can use background-fetch but I have never faced a problem to do something when app is closed.

The short answer is no, your app can't run code after being terminated.
You can run code in your AppDelegate's applicationWillTerminate, but that method is mainly intended for saving user data and other similar tasks.
See this answer also.

Yes you can do stuff in the background. You are limited to several different background modes of execution. Server communication is one of the modes that is allowed (background fetch). Make sure you set the properties correctly in Xcode per the guidelines (i.e. don't say you are a audio app when you are doing data transfer). See the details here:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
I found your question because it has the Cordova tag associated with it. If you are using Cordova you can use this plugin here to manage things in the background:
https://github.com/katzer/cordova-plugin-background-mode
Edit: If the user is FORCE closing / terminating the app then there is nothing you can do. If they are just exiting the app to the home screen and use other apps, then you can run in the background.
The other option you can do is schedule a local notification to upload the file. If you app successfully uploads the file because it is open / has a connection / did it in the background, then you cancel the local notification.
If the local notification isn't cancelled it will remind the user the file is not uploaded and when they open the notification your app will start back where it left off.

- (void)applicationWillTerminate:(UIApplication *)application {
if (application) {
__block UIBackgroundTaskIdentifier backgroundTask = UIBackgroundTaskInvalid;
backgroundTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
backgroundTask = UIBackgroundTaskInvalid;
}];
[self doSomething];
[application endBackgroundTask:backgroundTask];
backgroundTask = UIBackgroundTaskInvalid;
}
}

I have already answered a similar question:
https://stackoverflow.com/a/57245917/6157415
You can implement notification, when user receive notification part of your code can be executed.
in particular there are Silent Push Notification to do this:
Sometimes, you may want to use a Silent Push Notification to update
content inside you app in the background. A silent push notification
is defined as a push that does not have an alert, badge or sound, and
just has Key-Value data.
From: https://docs.mobile.sailthru.com/docs/silent-push-notifications

Related

How to execute function and update current state in background even app closed via Swift [duplicate]

The idea behind this app is very simple: download a file. However this app will be for people who are not always within internet access range, so I need it to know that at, say 9:00 AM, to download a file to the hard drive. There will be a button within the app to do it manually as well, but I've already got that working.
As I understand it, this will be difficult if it is even possible. I know that iOS doesn't like multitasking, but I am also aware that it does allow for background timer functions. I am open to any sort of suggestions anyone might have to accomplish this, even if it means writing a separate app. Thanks.
Edit: I see there is the possibility of working with Notifications, or even maybe the Calendar. Ideas in that category as also welcomed.
Edit 2: I also read something about an external server initiating an app, but it gave no description.
Here's the situation regarding background execution and notifications and timers etc. in relation to an app scheduling some activity to happen periodically.
An app cannot execute in the background unless:
It requests extra time from the OS to do so. This is done using beginBackgroundTaskWithExpirationHandler. It is not specified (intentionally) by Apple how long this extra time is, however in practice it is around 10 minutes.
An app has a background mode, the modes are: voip, audio, location, newstand. Even if it has one of these types an app cannot execute without some restrictions. The rest of this discussion assumes the app does not have a background mode.
When an app is suspended it cannot do ANYTHING to rouse itself directly. It cannot previously have scheduled an NSTimer, it cannot make use of something like performSelector:afterDelay. etc.
The ONLY way the app can become active again is if the USER does something to make it active. The user can do this from via of the following:
Launch the app directly from its icon
Launch the app in response to a local notification that was previously scheduled by the app while it was active.
Launch the app in response to a remote notification sent by a server.
A few others: such as URL launching if the app is registered to deal with launching via a url; or if its registered to be capable of dealing with a certain type of content.
If an app is in the foreground when a local/remote notification fires then the app receives it directly.
If the app is not currently in the foreground when a local/remote notification fires then the app DOES NOT receive it. There is no code that is executed when the notification fires!
Only IF the user selects the notification will the app become active and it can execute.
Note that the user can disable notifications, either for the entire device, or just for a specific application, in which case the user will never see them. If the device is turned off when a notification is due to fire then it is lost.
You could use local notifications. They execute code when the user opens the notification that is presented. You can set the local notification to recur at a specified interval (e.g. daily, hourly, weekly, etc). This still requires the user to open the app to get the process started.
UILocalNotification Class Reference
Once the delegate method fires, you only get a few seconds to execute code. Register for a long running background task, and download whatever you need to do. If it can't finish downloading in the 10 minutes you get for the task, then you need to rethink your download strategy.
Apple Multitasking and Backgrounding
We are using this same concept on iOS apps where I work, so this will work if you set it up right.
UPDATE
For those curious how this will work, you just need to implement the UILocalNotification delegate methods. They inherit from the UIApplicationDelegate that should already be in place.
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// start your long running bg task here and update your file
}
** UPDATE 2 **
Martin H's answer is the most correct so far. But this begs the question, if the user never opens the app, what is the point of downloading data they are never going to see? A recurring local notification reminding them to open the app and update may be the best way, but still requires the user to interact with your app if they want it to remain current and up-to-date.
Background applications have a set time limit (I believe 10 minutes, but don't quote me on that, it could be less) to complete whatever they are working on. You will not be able to use background tasks to do what you want.
What you can do is set an NSUserDefault with the date of the last download. On launch check the date saved, if the date is not the current date, and it is after 9:00am, initiate the download programatically.
I am pretty sure this is not possible, at most what you can do is send push notifications to the user, so that they manually update when required.
Have you tried the following?
if ([[[UIDevice currentDevice] systemVersion] floatValue] >=7.0)
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:600];
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
}

Asynchronous code doesn't execute until app foregrounded in application:didReceiveRemoteNotification:fetchCompletionHandler:

In our app, we want to download a small amount of data in response to a push notification. So far, push notifications are working smoothly, launching the app in the background and causing didReceiveRemoteNotification to be called.
The problem is that, after this method returns, the app doesn't get any more CPU time until it's foregrounded again, so there's no opportunity to fetch that data asynchronously in the background.
Reducing this to the simplest case, I'm still unable to get asynchronous code running.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[application setApplicationIconBadgeNumber:1];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[application setApplicationIconBadgeNumber:9];
completionHandler(UIBackgroundFetchResultNewData);
});
}
In response to a push, the app launches in the background and the badge count is set to 1, but the badge number is not set to 9 until the app is launched from the home screen.
Shouldn't iOS keep running the app until that completion handler is called, up to 30 seconds?
The Info.plist has the remote-notification background mode specified, the push payload contains 'content-available' : '1', and I'm not quitting the app by swiping up in the app switcher.
To add, we're using Parse to send this push notification using the following Javascript:
Parse.Push.send({
where: installationQuery,
data: {
"content-available": 1,
}
}, { success: function() {},
error: function(error) {}
});
First take a here and make sure you enabled push notification and added the content-available field:
Using Push Notifications to Initiate a Download
If your server sends push notifications to a user’s device when new content is available for your app, you can ask the system to run your app in the background so that it can begin downloading the new content right away. The intent of this background mode is to minimize the amount of time that elapses between when a user sees a push notification and when your app is able to able to display the associated content. Apps are typically woken up at roughly the same time that the user sees the notification but that still gives you more time than you might have otherwise.
To support this background mode, enable the Remote notifications option from the Background modes section of the Capabilities tab in your Xcode project. (You can also enable this support by including the UIBackgroundModes key with the remote-notification value in your app’s Info.plist file.)
For a push notification to trigger a download operation, the notification’s payload must include the content-available key with its value set to 1. When that key is present, the system wakes the app in the background (or launches it into the background) and calls the app delegate’s application:didReceiveRemoteNotification:fetchCompletionHandler: method. Your implementation of that method should download the relevant content and integrate it into your app.
When downloading any content, it is recommended that you use the NSURLSession class to initiate and manage your downloads. For information about how to use this class to manage upload and download tasks, see URL Loading System Programming Guide.
Next, is there a reason your using "dispatch_after" with 2 seconds delay?
It is possible that since you call "dispacth_after" by the end of the run loop iOS "thinks" there's no pending work to do and puts the process to sleep so by the time the block is dispatched no one is listening to it.
Replacing it with "dispatch_async" might solve this.
Finally, if you do need to delay, you should tell iOS you need some time in the background, like this -
UIApplication *application = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier __block backgroundTaskId = [application beginBackgroundTaskWithExpirationHandler:^{
if (backgroundTaskId != UIBackgroundTaskInvalid) {
[application endBackgroundTask:backgroundTaskId];
backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
Then do your background work.
Don't forget end the task when your work is done. call something like -
if (backgroundTaskId != UIBackgroundTaskInvalid) {
[application endBackgroundTask:backgroundTaskId];
backgroundTaskId = UIBackgroundTaskInvalid;
}

iOS Backgrounding Not Working

I am in the process of writing a VoIP application for iOS but when App is in background it stops accepting calls. When the app is active again all the queued up messages start getting processed.
The following is what I have done.
When building the app I add Voice over IP as well as Audio and AirPlay to the plist file. Then I mark the websocket connection with NetworkServiceTypeVoIP as you can see here.
I have not set the keep alive timeout handler because registration doesn't matter if the app won't wake up to answer the call. Any help would be greatly appreciated.
It should be noted that this is my first Swift project and I'm not very familiar with the iOS platform.
To allow to work your app in background mode, you need to enable Voice over IP flag ON(Path : Go to target --> capabilities --> Background Modes). like as below.
And add following code in your project to support in background:
Step 1: Declare __block UIBackgroundTaskIdentifier bgTask as global variable.
Step 2: To add following code in applicationDidEnterBackground.
- (void)applicationDidEnterBackground:(UIApplication *)application {
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
}
Step 3: Stop background task handler once apps come in foreground mode.
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
Take a look at Apple documentation, you're probably interested in UIBackgroundModes=voip.
UPD:
From documentation:
To configure a VoIP app, you must do the following:
Enable support for Voice over IP from the Background modes section of
the Capabilities tab in your Xcode project. (You can also enable this
support by including the UIBackgroundModes key with the voip value in
your app’s Info.plist file.)
Configure one of the app’s sockets for
VoIP usage.
Before moving to the background, call the
setKeepAliveTimeout:handler: method to install a handler to be
executed periodically. Your app can use this handler to maintain its
service connection.
Configure your audio session to handle transitions
to and from active use.
You may find your answer or some clue --> here <--
Properly have to research the following:
applicationDidEnterBackground
beginBackgroundTaskWithExpirationHandler

Will iOS awake my app when i receive silent push notification?(When app is not in running state)

Update Question :
The requirement is; as soon as I receive silent notification, I want to run a web service and show the one liner in the notification bar. It has to work if the app is killed also. any workaround ?
I am trying following method below.
I am new to iOS and i struggled with silent push notification,googled a lot and got stuck.
Will iOS awake my app when i receive silent push notification when app is not launched(i.e when app is removed from app switcher).
my pay load is as
{
aps: {
content-available: 1,
sound: ""
}
}
.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler{
int CA=[[[userInfo valueForKey:#"aps"] valueForKey:#"content-available"] intValue];
if (CA==1) {
my action...
}
completionHandler(UIBackgroundFetchResultNewData);
}
this method is called and works fine when app is in foreground and background.cant awake my app when app is not in running state(i.e app is not launched or killed from app switcher)..
If the App has been removed from the App Switcher, iOS will not awake your app, since the user specifically asked for closing your app.
If the user open your app at least once, and do not remove it from App Switcher, iOS will awake your app
What we have done server-side to handle this is :
If the user's app doesn't connect in the minute after we sent the silent notification, (you can set it as you wish), we send another non-silent push notification to alert the user.
Since the App (is not closed by the user) should automatically fetch data, it should take under a minute.
But for that of course you need a more complex server code than simply sending silent push.
EDIT : (Getting a vote up on this question showed me that it was outdated)
This answer is no longer True... you can now with PushKit wake up your app to do some minor things (like downloading small chunks of data to update content) even if the App has been removed from App Switcher.
Please checkout this:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:
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.
This clearly says that Using new Background Push feature you can Awake the App Only if Your app is suspended Not if it is terminated forcefully by User.
As far I have tested when the app is terminate by the user (swiping up from the app switcher) you won't have background execution time due to silent push flag (content-available) or background fetch.
Also this:
Also keep in mind that if you kill your app from the app switcher
(i.e. swiping up to kill the app) then the OS will never relaunch the
app regardless of push notification or background fetch. In this case
the user has to manually relaunch the app once and then from that
point forward the background activities will be invoked. -pmarcos (Apple worker)
From apple forums: https://devforums.apple.com/message/873265#873265
You CAN get a PUSH-notification and work with it. I know a little way to do this... Open the AppDelegate.m and and find or put this method - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
After that, put into this method code like mine:
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo) {
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alertType = [apsInfo objectForKey:#"type"]; //my own param in PUSH-notification
globalPushType = alertType; //global variable for working with it in some ViewControllers after app's load
}
I know, this helps a lot of people. =)

iOS Run Code Once a Day

The idea behind this app is very simple: download a file. However this app will be for people who are not always within internet access range, so I need it to know that at, say 9:00 AM, to download a file to the hard drive. There will be a button within the app to do it manually as well, but I've already got that working.
As I understand it, this will be difficult if it is even possible. I know that iOS doesn't like multitasking, but I am also aware that it does allow for background timer functions. I am open to any sort of suggestions anyone might have to accomplish this, even if it means writing a separate app. Thanks.
Edit: I see there is the possibility of working with Notifications, or even maybe the Calendar. Ideas in that category as also welcomed.
Edit 2: I also read something about an external server initiating an app, but it gave no description.
Here's the situation regarding background execution and notifications and timers etc. in relation to an app scheduling some activity to happen periodically.
An app cannot execute in the background unless:
It requests extra time from the OS to do so. This is done using beginBackgroundTaskWithExpirationHandler. It is not specified (intentionally) by Apple how long this extra time is, however in practice it is around 10 minutes.
An app has a background mode, the modes are: voip, audio, location, newstand. Even if it has one of these types an app cannot execute without some restrictions. The rest of this discussion assumes the app does not have a background mode.
When an app is suspended it cannot do ANYTHING to rouse itself directly. It cannot previously have scheduled an NSTimer, it cannot make use of something like performSelector:afterDelay. etc.
The ONLY way the app can become active again is if the USER does something to make it active. The user can do this from via of the following:
Launch the app directly from its icon
Launch the app in response to a local notification that was previously scheduled by the app while it was active.
Launch the app in response to a remote notification sent by a server.
A few others: such as URL launching if the app is registered to deal with launching via a url; or if its registered to be capable of dealing with a certain type of content.
If an app is in the foreground when a local/remote notification fires then the app receives it directly.
If the app is not currently in the foreground when a local/remote notification fires then the app DOES NOT receive it. There is no code that is executed when the notification fires!
Only IF the user selects the notification will the app become active and it can execute.
Note that the user can disable notifications, either for the entire device, or just for a specific application, in which case the user will never see them. If the device is turned off when a notification is due to fire then it is lost.
You could use local notifications. They execute code when the user opens the notification that is presented. You can set the local notification to recur at a specified interval (e.g. daily, hourly, weekly, etc). This still requires the user to open the app to get the process started.
UILocalNotification Class Reference
Once the delegate method fires, you only get a few seconds to execute code. Register for a long running background task, and download whatever you need to do. If it can't finish downloading in the 10 minutes you get for the task, then you need to rethink your download strategy.
Apple Multitasking and Backgrounding
We are using this same concept on iOS apps where I work, so this will work if you set it up right.
UPDATE
For those curious how this will work, you just need to implement the UILocalNotification delegate methods. They inherit from the UIApplicationDelegate that should already be in place.
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// start your long running bg task here and update your file
}
** UPDATE 2 **
Martin H's answer is the most correct so far. But this begs the question, if the user never opens the app, what is the point of downloading data they are never going to see? A recurring local notification reminding them to open the app and update may be the best way, but still requires the user to interact with your app if they want it to remain current and up-to-date.
Background applications have a set time limit (I believe 10 minutes, but don't quote me on that, it could be less) to complete whatever they are working on. You will not be able to use background tasks to do what you want.
What you can do is set an NSUserDefault with the date of the last download. On launch check the date saved, if the date is not the current date, and it is after 9:00am, initiate the download programatically.
I am pretty sure this is not possible, at most what you can do is send push notifications to the user, so that they manually update when required.
Have you tried the following?
if ([[[UIDevice currentDevice] systemVersion] floatValue] >=7.0)
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:600];
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
completionHandler(UIBackgroundFetchResultNewData);
}

Resources