i am new in iOS Development so pardon me for asking this question in advance.
I am trying to post some data through API (after a specific time, lets say 30 minutes), I am able to achieve this when the app is in foreground or in background but is it possible to achieve even when the App is terminated ? I can perform this activity while the App is in background through this piece of code
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
But once the app gets terminated everything stops. I have found out that the thread of an iOS App gets killed after 3 minutes of its termination.
Presenting UIView after an interval
If the above is possible then is it possible to animate a particular UIView to appear on the screen after a specific time even if the app is terminated.
Thanks in advance.
When your app is terminated it can't execute any code; You can execute some code as the app terminates in applicationWillTerminate, if it is called. In many cases it won't be.
On iOS, you cannot execute code at a specific time or after a specific interval if your app is suspended.
You can schedule a local notification to prompt your user to return to your app, but if they don't tap the notification or launch your app from its icon in response then you cannot execute any code.
Have a look at the applicationWillTerminate: method of the UIApplicationDelegate protocol here. Once the system decides to shut down your application, this method is invoked giving you the possibility to do everything your app needs to do before it can be finished. This could be posting API data to your server as you desire.
Please note that according to the documentation this method is not guaranteed to be called when the user presses the home button but only when the system decides to kill the application:
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.
If you don't know about the concept of Delegates in Cocoa Programming think of them as a special connection between one instance object (controller in the MVC design pattern) of your application (it has to be the app's delegate) and your app itself. The app will tell your controller if something interesting did/will happen like application startup or shutdown. Note: This is also available for macOS programming.
Related
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);
}
In iOS, we all know that there is AppDelegate method applicationWillTerminate, and it is called when my app is closed by user when it is currently running(i.e. not in background). But I want to do something(save data, for example) when my app is terminated(closed by user or killed by OS) when it runs in background.
PS: my app can run in background.
Do you have any solutions? thanks.
Sorry but you should use applicationWillTerminate:
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 if you need to save data ALSO when user manually kill the app use applicationDidEnterBackground that it's called if your app support background mode.
If you need to execute code when your app isn’t running, there are
several options open to you depending on what you’re trying to do.
Background fetch will let your app run in the background for about 30 seconds at scheduled intervals. The goal of this is to fetch data
and prepare your UI for when the app runs next.
Push notifications let your app fetch fresh data from your server. You can make a message appear on the device if you want, but it’s not
required – silent push notifications let you skip that part.
Local notifications let you display an alert to the user, along with any media attachments you want and some options for the user to
select from. If they choose those options then your app can be
launched in the foreground or background to handle them.
From:
https://www.hackingwithswift.com/example-code/system/how-to-run-code-when-your-app-is-terminated
You can also use Silent Push Notification
as I mentioned in a similar question: https://stackoverflow.com/a/57245980/6157415
In the GCM client guide for iOS, it shows you to call [[GCMService sharedInstance] connectWithHandler:...] in -applicationDidBecomeActive:, and later it shows you to call [[GCMService sharedInstance] disconnect]; in -applicationDidEnterBackground:.
The placement of connect and disconnect seems asymmetric to me. Is there a reason it is done this way? I feel that it should either be
Connect in -applicationDidBecomeActive: and disconnect in -applicationWillResignActive:, or
Connect in -applicationWillEnterForeground: and disconnect in -applicationDidEnterBackground:
If you look at the Execution States for Apps in Apple's App Programming Guide for iOS, it states that:
applicationWillResignActive:—Lets you know that your app is transitioning away from being the foreground app. Use this method to put your app into a quiescent state.
Compare to
applicationDidEnterBackground:—Lets you know that your app is now running in the background and may be suspended at any time.
For example, if you press the Home button of your device twice, the applicationWillResignActive method will be call immediately, your app is in a transitioning state now. see the image below (Your app leave foreground, but not yet in background):
If you select other app, the applicationDidEnterBackground of your app will be called, and your app enters background. So it is better to call GCMService.sharedInstance().disconnect() when your app is actually in a background state compared to a transitioning state.
Similarly, applicationWillEnterForeground vs applicationDidBecomeActive.
If you are in other app now, and you switch back to your own app, the applicationWillEnterForeground will be called immediately, but your app is might still in an inactive state(which is currently not receiving events).
It might only last for less than half a second, and applicationDidBecomeActive will called immediately after. Because applicationWillEnterForeground is more a transitioning state. So it is better to call GCMService.sharedInstance().connectWithHandler() when your app is actually in an active state(app is running in the foreground and is receiving events).
You might think applicationDidBecomeActive and applicationDidEnterBackground are asymmetric, but these two methods all have a word Did. But two other methods have the word Will, which means a transitioning state.
You can also see the Life Cycle picture from this StackOverflow answer.
Edit after #user102008's comment:
If you do connect in the applicationWillEnterForeground method, then connect method wont be called when the app is first launched. Also, the GCM doc says that Call disconnect when the client app is in the background or when it needs to stop exponential background connection retry, so I think its better to call disconnect when the app is in background, instead of when phone call comes? I think you can use enum with 3 different states connecting , not connected and connected, when the applicationDidBecomeActive method is called, you can check if its not connected or connecting, then you call the connect method again.
I was wondering which event in my AppDelegate is called when I swipe out my running app from within the app switcher? My target iOS is >= 7.0 (i.e. with multitasking support).
Maybe I just don't understand the multitasking paradigm itself... When I swipe out my app from within the app switcher, the app is going to be terminated, right? Or does it continue running in the background?
EDIT-1: Why I am asking: During startup of the app, I connect to my Linux game server by plain sockets. I want to disconnect the socket when the app terminates.
EDIT-2: I am not sure if I am using background execution. I register my network socket within a run-loop object to asynchronously receive inputs from the server socket. Moreover I have a kind of network service installed that periodically tests for network connectivity. This service is using SCNetworkReachabilityScheduleWithRunLoop.
There are situations where applicationWillTerminate: will be called and sometimes not.
More can be read up from the Apple documentation here
Excerpt:
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.
What's interesting to note is that
After calling this method, the app also posts a
UIApplicationWillTerminateNotification notification to give interested
objects a chance to respond to the transition.
It is - (void)applicationWillTerminate:(UIApplication *)application
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);
}