Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

performFetchWithCompletionHandler never gets fired

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

Fetch! Good doggie!

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


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.


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.


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