performFetchWithCompletionHandler never gets fired

IosObjective CIos7

Ios Problem Overview


  1. My plist configuration to provide backgroundmode:

    UIBackgroundModes fetch

  2. In didFinishLaunchingWithOptions I have:

    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:1.0];

  3. I declared the protocol UIApplicationDelegate in the delegate.

  4. I implemented the following method, but it never gets fired. (It only works if I simulate the fetch with "XCode->Debug->Simulate Background Fetch".)

    -(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

Why? Is this a DP5 beta error? Should I radar this?

Ios Solutions


Solution 1 - Ios

Running your app in the iOS Simulator, in Xcode Debug mode, you can force a background fetch from the Xcode menu:

Debug > Simulate Background Fetch

https://i.stack.imgur.com/oid9v.jpg" width="300" alt="Fetch! Good doggie!"/>

May work for a tethered device, I haven't tried it recently.

Solution 2 - Ios

I'm afraid this is hard to debug on a device because you're not guaranteed it is called in the amount of time you specify.

setMinimumBackgroundFetchInterval means that it is not called in an interval which is smaller than the value you specified. But there's no setMaximumBackgroundFetchInterval. So if iOS decides to call your app just once a day or even just once a week than it won't be called more often regardless your minimumBackgroundFetchInterval. AFAIK iOS decides when to call performFetchWithCompletionHandler measured by the pattern when and how often the users start the app.

Solution 3 - Ios

There are many considerations:

  1. Make sure the background fetch capability was set in the plist.

  2. Make sure the background fetch capability hasn't been disabled for this particular app, or in general, in the device's Settings app.

  3. Make sure to set the minimum fetch interval.

  4. Make sure you gracefully leave the app (e.g. just hit the home button and fire up another app and/or just lock the device). But if you kill the app (by “force quitting” by double tapping on the home button and swiping up or, for those devices without home button, swiping up from the bottom to pull up the task manager and then swiping up on the app in question) that will prevent the OS from offering your app a chance to fire off subsequent background fetch requests (at least until the user runs the app again).

  5. Make sure you are testing this on physical device and not running the app via the Xcode debugger. Being attached to the debugger changes the behavior of background operations.

  6. Make sure the app is actually doing some network requests. If you have app that performs no network requests at all, it won't participate in background fetch. If you do, for example, a little test app with "background fetch" and don't issue any network requests, you won't participate in background fetch.

    Likewise, if the OS starts up your app in background mode so it can perform a background fetch, if you don't actually perform a network request, the OS may stop offering your app the ability to perform background fetches in the future.

  7. Make sure to call the completion handler, and do so within the allotted time, or your app may not participate in background fetch in the future.

  8. The timing of when the OS performs background fetch is dictated by poorly documented rules that may change in the future. But the relevant factors include:

    • Whether the device is connected to power and/or is sufficiently charged;

    • Whether connected to WiFi or not;

    • How often the user actually fires up the app;

    • Whether the device is doing other network related tasks (e.g. whether background fetch can be coalesced with other network operations);

    • How frequently past background fetch requests resulted in there being data available.

    In my experience, after the app is run the first time, if connected to wifi and power, if you wake the device about 5 minutes later, the app will perform background fetch. This isn't a hard and fast rule, but just what we've experienced in the past.

    But many new developers post on Stack Overflow with questions like “how can I have app request data ever x minutes (or hours)”, “how can I request data every day at 2 am time”, etc. The short answer is that you can't. The OS decides the timing of background at its own discretion. You cannot control this (other than the minimum request interval; but you cannot control the maximum interval, as the OS controls that).

  9. This may seem obvious to many, but make sure you've got a reliable way of knowing whether background fetch process is running correctly or not. User Notifications framework can be used to present some alert so you know if the background request resulted in something. Alternatively, os_log or Logger “Unified Logging” (see WWDC 2016 Unified Logging and Activity Tracing or 2020’s Explore logging in Swift) can be used to post messages on device that can be monitored on macOS Console app. But more than once, I've seen users do something like waiting for message to show up in Xcode or waiting for UIAlertController. You need some mechanism that works when not connected to Xcode and when the app never enters foreground.

Solution 4 - Ios

Using your device you can fire application:performFetchWithCompletionHandler with the following steps:

  • Put your app in the Background state
  • Lock your device and wait 5 minutes.
  • Unlock your device, this will fire the method

Solution 5 - Ios

> (It only works if I simulate the fetch with "Xcode->Debug->Simulate > Background Fetch".)

It's because you're in Debug mode. Please try launch app without Xcode.

Solution 6 - Ios

Another thing to check is your plist file. Make sure the UIApplicationExitsOnSuspend key is not present.

Many people here on Stack Overflow have recommended using that setting as a way to force your app to start fresh each time it's launched. That does work, but the side effect is that it prevents the new iOS 7 background fetch feature from being triggered.

Solution 7 - Ios

If application: performFetchWithCompletionHandler: never gets fired (unless you simulate it using Xcode), check also if "Background App Refresh" preference is "On" for your app. (Settings app -> General -> Background App Refresh)

Solution 8 - Ios

Also, background fetch is disabled if the iPhone is in Low Power Mode.

Solution 9 - Ios

Apple provides an algorithm which defines how often the background fetch should trigger, based on your own usage of the app. If you use it a lot, then it will fetch as often as possible, but if you use like at 4pm every day, the background fetch should trigger just before, so your data is updated when you launch it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2110287View Question on Stackoverflow
Solution 1 - IosClay BridgesView Answer on Stackoverflow
Solution 2 - IosOne Man CrewView Answer on Stackoverflow
Solution 3 - IosRobView Answer on Stackoverflow
Solution 4 - Iosuser2936585View Answer on Stackoverflow
Solution 5 - Iosuser1700099View Answer on Stackoverflow
Solution 6 - IosAxevaView Answer on Stackoverflow
Solution 7 - IosAnastasiaView Answer on Stackoverflow
Solution 8 - Iosadrian1katView Answer on Stackoverflow
Solution 9 - IosCrazyDeveloperView Answer on Stackoverflow