I am wanting to test background downloads of large files in iOS 7+ applications.
I am using a NSURLSession with a background configuration as explained in the Apple documentation on Background Execution.
The documentation states that "Once configured, your NSURLSession object seamlessly hands off upload and download tasks to the system at appropriate times."
I am trying to for the creation of such appropriate times so that I can test an app before release. Various examples suggest that I can simply bring a different application to the foreground to trigger handover of the background download task to the system.
However, that is not proving to be the case. Instead, the download completes under the control of the app.
Importantly
- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)(void))completionHandler
never runs in my application delegate.
I have tried to force a handover on both the iOS simulator and on actual devices but with no luck.
Thoughts on ways to force handover to of background downloads to the system would be appreciated.
There is no "handover". Every task in a background session always runs in a separate process, period.
The application:handleEventsForBackgroundURLSession:completionHandler: method, however, runs only if your app gets terminated by the OS (not by the user) while a download is in progress. If this happens, your app gets relaunched in the background whenever the download finishes.
You can potentially test this by starting a large download in the background that will take long enough to complete that your app will get evicted... or by switching to another app that pigs out on RAM until your app gets evicted.
I am trying to implement interactive local notifications for my app, and in particular via implementing this method:
- (void) application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
The method works fine except if the user kills the app and then receives an interactive local notification. In that case there is no indication that the local notification action (button click) was received.
I am looking for help/advice on 2 fronts
(1) Is anyone able to confirm/deny that handleActionWithIdentifier is not called when the app is not running at all (i.e. not even in background mode). I went through the relevant programming guide and I noticed that much of the language talked about foreground and background mode without addressing the third possibility. However, I'm looking for something more solid than 'it doesn't work for me' to put this to rest. Is there anything in the docs that says these should or should not work after the app is killed?
(2) How can I log in the Xcode console after I have killed my app? Every time I try this, I notice that when I relaunch the app I no longer have logging. This is important because I need to prove one way or another whether I receive interactive notification messages from user clicks after the user has killed the app.
I would appreciate any and all advice on this matter. Thank you.
If the app is terminated then when it gets launched via the notification then didFinishLaunchingWithOptions gets called with a key of UIApplicationLaunchOptionsLocalNotificationKey. The value of this key is an NSDictionary containing the payload of the local notification
You can't log in the Xcode console from the app if the app isn't running of course. But if you run the app not via Xcode you can watch the output by going to Window|Devices then selecting your attached device.
If you are relaunching the app via Xcode and logging isn't there this is probably a bug in Xcode, I've noticed recent versions of Xcode is having problems displaying logging. If this happens try the Devices output.
I need to detect if a user resumes my application from the homescreen or from the application switcher. This is very easy on Android, but on iOS I'm experiencing the same behavior.
In both cases both delegate methods get called
applicationWillEnterForeground and applicationDidBecomeActive
as you would expect.
In case of answers like "There shouldn't be a difference in behavior": my app should display a menu when opened from the homescreen (opened "fresh"), but should resume when switched back to.
Edit:
For clarification I want to make a tap on the homescreen icon seem like the app started fresh even though it might have been suspended.
I believe that what you are really trying to detect:
is it a fist launch -vs.-
is my app already running.
There is an inherent problem with this approach: unlike Android, where sessions are fuzzy and apps are generally running in the background, iOS hides the state of all applications to the user : once launched, it is just about permanently present in the fast-app-switching list. Until, that is, the user explicitly removes the app from that list (*).
(*) Even so, the consequences on your app vary vastly ; if your app was running, then it quits. But your app may already have been stopped long ago, in case the iOS needed to reclaim the memory for another frontmost application. Right there, you are bound to get inconsistencies.
(**) Furthermore, the very same happens from the Springboard. As you know, the app may already be running.
You can only differentiate fresh launch from hot swap using:
// Fresh launch
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
vs.
// App already running
- (void)applicationWillEnterForeground:(UIApplication *)application
There is more.
You have some granularity on how exactly you were launched (as in, say, from a URL or another app). See UIApplicationLaunchOptionsURLKey and all the other provided with -application:didFinishLaunchingWithOptions:.
Finally, many apps just display a splash screen each and every time they are brought to the forground. Since working around the iOS is generally brittle and bound to break at the next update, you may just want to do that. Less code, more robustness, consistency all over.
I am triggering a background fetch by using the content-available flag on a push notification. I have the fetch and remote-notification UIBackgroundModes enabled.
Here is the implementation I am using in my AppDelegate.m:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"Remote Notification Recieved");
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"Looks like i got a notification - fetch thingy";
[application presentLocalNotificationNow:notification];
completionHandler(UIBackgroundFetchResultNewData);
}
When the app is running in the background, it works fine. (The notification is received and the app triggered the "looks like i got a notification" local notification, as the code above should do).
However, when the app is not running and a push notification is received with the content-available flag, the app is not launched and the didRecieveRemoteNotification delegate method is never called.
The WWDC Video Whats New With Multitasking (#204 from WWDC 2013) shows this:
It says that the application is "launched into background" when a push notification is received with the content-available flag.
Why is my app not launching into the background?
So the real question is:
Will iOS perform background tasks after the user has force-quit the app?
UPDATE2:
You can achieve this using the new PushKit framework, introduced in iOS 8. Though PushKit is used for VoIP. So your usage should be for VoIP related otherwise there is risk of app rejection. (See this answer).
UDPDATE1:
The documentation has been clarified for iOS8. The documentation can be read here. Here is a relevant excerpt:
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 push
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.
Although this was not made clear by the WWDC video, a quick search on the developer forums turned this up:
https://devforums.apple.com/message/873265#873265 (login required)
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
That post was by an Apple employee so I think i can trust that this information is correct.
So it looks like when the app is killed from the app switcher (by swiping up), the app will never be launched, even for scheduled background fetches.
You can change your target's launch settings in "Manage Scheme" to Wait for <app>.app to be launched manually, which allows you debug by setting a breakpoint in application: didReceiveRemoteNotification: fetchCompletionHandler: and sending the push notification to trigger the background launch.
I'm not sure it'll solve the issue, but it may assist you with debugging for now.
The answer is YES, but shouldn't use 'Background Fetch' or 'Remote notification'. PushKit is the answer you desire.
In summary, PushKit, the new framework in ios 8, is the new push notification mechanism which can silently launch your app into the background with no visual alert prompt even your app was killed by swiping out from app switcher, amazingly you even cannot see it from app switcher.
PushKit reference from Apple:
The PushKit framework provides the classes for your iOS apps to
receive pushes from remote servers. Pushes can be of one of two types:
standard and VoIP. Standard pushes can deliver notifications just as
in previous versions of iOS. VoIP pushes provide additional
functionality on top of the standard push that is needed to VoIP apps
to perform on-demand processing of the push before displaying a
notification to the user.
To deploy this new feature, please refer to this tutorial: https://zeropush.com/guide/guide-to-pushkit-and-voip - I've tested it on my device and it works as expected.
Actually if you need to test background fetch you need to enable one option in scheme:
Another way how you can test it:
Here is full information about this new feature:
http://www.objc.io/issue-5/multitasking.html
I've been trying different variants of this for days, and I thought for a day I had it re-launching the app in the background, even when the user swiped to kill, but no I can't replicate that behavior.
It's unfortunate that the behavior is quite different than before. On iOS 6, if you killed the app from the jiggling icons, it would still get re-awoken on SLC triggers. Now, if you kill by swiping, that doesn't happen.
It's a different behavior, and the user, who would continue to get useful information from our app if they had killed it on iOS 6, now will not.
We need to nudge our users to re-open the app now if they have swiped to kill it and are still expecting some of the notification behavior that we used to give them. I'm worried this won't be obvious to users when they swipe an app away. They may, after all, be basically cleaning up or wanting to rearrange the apps that are shown minimized.
This might help you
In most cases, the system does not relaunch apps after they are force
quit by the user. One exception is location apps, which in iOS 8 and
later are relaunched after being force quit by the user. In other
cases, though, the user must launch the app explicitly or reboot the
device before the app can be launched automatically into the
background by the system. When password protection is enabled on the
device, the system does not launch an app in the background before the
user first unlocks the device.
Source:
https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
For iOS13
For background pushes in iOS13, you must set below parameters:
apns-priority = 5
apns-push-type = background
//Required for WatchOS
//Highly recommended for Other platforms
The video link: https://developer.apple.com/videos/play/wwdc2019/707/
In IOS 4 if the application is closed by user manually then it is not launching again it crashes when launched and i cant do anything.
I am testing it on Ipod4
When i pressed the hard key of ipad twice and close the app manually then i am not able to start the application again.
how to terminate the application completely do i need to write something in
- (void)applicationWillTerminate:(UIApplication *)application
of appdelegate?
Seems like you have some serious issues with your code.
An application should support both going into the background and transitioning out of it into active state again, as well as terminating from the background once that is initiated by the OS, or by the user via the 'double home tap -> close' action.
Terminating the application forcibly through code is not supported and will actually result in apple rejecting your app when you submit it.