iOS background fetch keeps launching the app - ios

I'm working on an iOS application which had Background Fetch enabled via the Info.plist for around a year. There were multiple versions released with background fetch enabled, but then a few weeks ago the feature that necessitated background fetch was removed. The UIBackgroundModes key was removed completely from the Info.plist, and the app was released to the App Store.
Surprisingly though, telemetry indicated that the application was still being launched in the background periodically! This is a snippet from the actual app delegate source code:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// NB: this callback should NEVER happen! But if it happens, we want to know about it.
... send telemetry log here ...
completionHandler(UIBackgroundFetchResultNoData);
}
Has anybody experienced anything similar? According to the documentation, it should be impossible for the app to be launched if the UIBackgroundModes dictionary don't contain the right values. In this case, there's no background mode turned on at all, yet the app is periodically launched in the background to perform the fetch...
Telemetry data indicates that it happens on iOS 11 only, but that might be a coincidence because only a small fraction of the installed base is on iOS 9/10.
BTW a source level assistance request was sent to Apple with this issue, but they haven't replied yet; but maybe developers of other apps have encountered this issue before.

Related

Even with "voip" present in "UIBackgroundModes" in "plist", iOS App does not auto start after device reboot in iOS10

I need my VoIP App to auto start after rebooting the device.
Apple docs clearly mention that :-
(=========EDIT: This is from official Apple docs please have a look at this before commenting or answering that the App cannot be launched without user interaction or silent push notification. Also have a look at Github project below, people have verified this behaviour)
Values for the UIBackgroundModes array
Value : voip Description : The app provides Voice-over-IP services.
Apps with this key are automatically launched after system boot so
that the app can reestablish VoIP services. Apps with this key are
also allowed to play background audio.
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW1
I have ensured that :-
The App was running when the device was powered off.
VoIP is present in the plist and Capabilities section.
Ensured that app the certainly not launched after device reboot by adding logs to a file in the main method and the application:didFinishLaunchingWithOptions: method.
Screen of the device is unlocked at least once, after the device has been rebooted.
I even tried executing this GitHub example App with 36 stars to test
Boot Launch. https://github.com/lithium3141/BootLaunch
But even this App does not restart on reboot when I tried on device.
Hence, this leads me to think if something has been changed recently in iOS10 or am I still missing something here?
Okay, I investigated this a bit further, but first I should point out I did not verify this by actually trying to build a project for this as it would be too time consuming for me now.
I found this (already mentioned in the comments), this, and most importantly this tech Q&A.
What I gathered from especially the various comments of the Apple technicians in those threads, it appears that the behavior of iOS 10 has indeed changed. That means that the same code that connected to VoiP servers in past versions of iOS will no longer do that if you link your build against the latest SDK, i.e. iOS 10 libraries.
Now, in your case, you don't actually need a real VoiP connection, right? You are just interested in the "start after reboot" functionality, correct? At least the demo project you linked doesn't actually do any VoiP connection, the setKeepAliveTimeout:handler: method, for example, isn't even implemented.
I am aware this specific issue is not discussed in the linked threads or addressed in the Q&A, BUT:
It makes sense that, together with the entire legacy VoiP behavior, the reboot feature vanishes as well. Were you to switch to Push-Kit VoiP, your app wouldn't need to be started after a relaunch, it would restart once the next remote notification arrives (and VoiP notifications have high priority so there's supposedly no delay).
Obviously I am deducting the rationale behind this entire thing here and can't guarantee Apple really thought along those lines, but it makes sense: The entire reason for a (legacy) VoiP app to be (re-)launched after a reboot was that it needed to make a connection, i.e. it needed to run some code. With push notifications that is no longer necessary (the OS basically does that for you behind the scenes to get those notifications), so it makes sense that they removed this functionality along with the entire legacy VoiP approach altogether.
You could test this by compiling against the older SDK (i.e. use Xcode 7 as the Q&A suggests) and see whether it relaunches then. That one apple staffer actually explained that the OS does indeed distinguish on the build SDKs for apps, which is totally counter-intuitive to me. Apparently in this case it would decide "hey, this is an older app, so it expects to be relaunched cause its SDK was documented that way" for apps build on Xcode 7 and "Oh, this app is a new one, so I don't need to stick to the old ways" otherwise. Wowsies.
TL;DR: To me it strongly looks like yes, the iOS SDK changed this behavior along with abandoning the entire old, notification-less VoiP approach. Compiling against the new SDKs will result in apps not being relaunched after reboot.
For the record: I can understand the angry people in those threads. While there might be technical reasons for the change, this specific consequence was far from obvious. If a method is deprecated, but the project still compiles and runs I wouldn't expect such a process to fail in that manner. Those apps don't crash, they're just "treated differently by the OS", which not quite the same. At least I would have expected the documentation to be clearer about this in the new SDK.
App will invoke in background when it is in terminated mode only with push kit silent notification and certificates has to be generate for push kit, not with normal APNS notification and normal push notification certificates.
From back end, your payload has to be like this.
$body['aps'] = array(
'content-available'=> 1,
'alert' => $message,
'sound' => 'default',
'badge' => 0,
);
Once you get pushkit payload, then schedule local notification with sound file, your app will be invoked in background upto your sound file plays.( Max 30 seconds ) Til then you have to complete your background task.
Kindly do refer some important details about push kit integration step by step process
https://github.com/hasyapanchasara/PushKit_SilentPushNotification
Life cycle of app - when app is in terminated and push kit payload comes
First of all
didFinishLaunchingWithOptions // will invoke
Then
didReceiveIncomingPushWithPayload // payload method gets invoke
Then if you have local notification
didReceiveLocalNotification // receive local notification
Then
handleActionWithIdentifier // handler method if you have action buttons ( local )
Then if you have remote notification
didReceiveRemoteNotification // receive remote notification
Then
handleActionWithIdentifier // handler method if you have action buttons ( remote )
Note - Without tapping on app icon or receiving push kit payload, your app will never gets revoke/open/repoen automatically. If you want your app is VOIP based and app gets revoke after device reboot. Not possible.

iOS: Handling interactive notifications when app is not running in background

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.

SendMessage Complication Controller Wake iOS App

So, I am trying to use the oppertunity when my Watchkit complication calls requestUpdateDidBegin() to use sendMessage() to wake my iOS app and cause it to calculate complication data and use transferCurrentComplicationUserInfo() to update the complication. I call sendMessage() from the extension delegate. I am experiencing mixed luck with this actually reaching the iOS app. Even with my iPhone next to the watch the performance varies. One time I built my app to my phone and it successfully delivered over 30 updates to my complication through sendMessage() and transferCurrentComplicationUserInfo(), over the course of two days. I then uninstalled the app from the watch using the watch app, and then reinstalled it from the watch app (So absolutly no code or build changes) and it stopped working! I suspect there is clearly some bugs and unreliability with calling sendMessage(). Is anyone else finding this, is there any workarounds? How can I wake my iPhone app from when the complication controller calls requestUpdateDidBegin()?
I don't believe there is any guarantee you can wake the iPhone from the watch. If you need to update with data from the phone, you are better to schedule the updates on the iPhone app and then push the updated data from the phone. You'll probably need to enable a background mode on the iPhone app for this to work (location services, background data transfer, depending on your data type.)
(As posted here https://forums.developer.apple.com/thread/26934)

PFQuery findObjectsInBackgroundWithBlock: in WatchKit app works in simulator, not on watch unless iOS app is in foreground

I'm writing a WatchKit app which needs to make a query to our Parse server. Because this is a network call, I follow the following procedure:
I make a call from WatchKit to the iOS app using openParentApplication:reply:
Inside of handleWatchKitExtensionRequest:userInfo:reply:, I start a background task with a UIBackgroundTaskIdentifier to ensure the iOS app isn't terminated immediately
I set up my PFQuery and call findObjectsInBackgroundWithBlock:
In the block I pass to the PFQuery, I reply the relevant information to the watch
I wrote this app maybe a month ago before the Apple Watch was out, and it ran just fine in the Watch simulator. Yesterday I received my Apple Watch and I was able to get the app running on the watch, but it doesn't quite work - I managed to figure out that when the iOS app is actively running in the foreground on the iPhone, then everything is great; but if the app is not active (e.g. backgrounded or terminated), then findObjectsInBackgroundWithBlock: never actually calls the block, and execution seems to just stop.
Does anyone have any ideas of what might be going on? My initial thought was that the iOS app is getting suspended/killed before it can complete the query, but I was pretty sure the background task would prevent that.
PS. If anyone stumbles upon this question because they were looking for how to get a WatchKit app running on an actual watch, I found this post extremely helpful in getting it working.
Turned out that I had several problems related to openParentApplication:reply:, and they all seem to have been resolved by using the technique from this post:
http://www.fiveminutewatchkit.com/blog/2015/3/11/one-weird-trick-to-fix-openparentapplicationreply
I had started a background task for my own app, but I didn't use the "bogus workaround" background task described in this post, because it wasn't necessary for me in the simulator. On the watch, though, it apparently was.

Will iOS launch my app into the background if it was force-quit by the user?

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/

Resources