61

1) My plist configuration to provide backgroundmode:

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
</array> 

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?

smholloway
  • 589
  • 7
  • 14
  • Are you trying your app on the Simulator and/or real device? – OutOfBoundsException Aug 30 '13 at 10:30
  • 1
    I'm in the exact same boat with the shipping version of iOS 7. Background fetch works just fine when I trigger it manually in Xcode, but never seems to be triggered on an actual device. How can we diagnose the cause of the issue? – Axeva Oct 30 '13 at 18:19

9 Answers9

107

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.

Clay Bridges
  • 11,602
  • 10
  • 68
  • 118
39

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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
One Man Crew
  • 9,420
  • 2
  • 42
  • 51
  • 5
    Can anyone elaborate on this answer? "You don't know when, and you will never know when" may well be the right answer, but I'm really hopeful that we can do better. It's nearly impossible to debug a background fetch process if we have no way to distinguish between "something went wrong" and "your time hasn't come up yet". – Axeva Oct 30 '13 at 18:17
  • 2
    I'm seeing refresh times between 10 and 20 minutes when the device is left locked and not connected to wi-fi. Waking up the device can trigger refresh earlier, but not within 5 minutes. – Steven Kramer Nov 02 '13 at 13:12
  • i.e. "minimum" means, the shorted time interval between invokations of the applicatio IF the iOS decides to call. It is upto iOS to decide when to call, after the elapse of the "minimum" period, and need not be immediate. Is this what is meant? Thanks! – Ravindranath Akila Feb 06 '14 at 06:47
  • 1
    It would be nice if someone could add perhaps a little explanation of the metrics iOS uses to decide when it is called and perhaps things that may make it call less often? – DBIT Sep 08 '18 at 18:44
35

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    Good answer. However, here the app starts performFetchWithCompletionHandler the first time after 24h on the debug device. Changed a lots of parameters, but couldn't get this to work within say 1h (setMinimumBackgroundFetchInterval set to 10 min, 3h, Minimum doesn't change anything. – brainray Jun 19 '19 at 12:11
  • "Make sure the app is actually doing some network requests." Source? – Iulian Onofrei Mar 09 '21 at 12:20
  • @IulianOnofrei - Re background fetch much actually perform network request: This was discussed a WWDC session. Obviously, nowadays, you’d generally use [`BGAppRefreshTask`](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask) for refresh tasks with network requests and [`BGProcessingTask`](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask) for processing tasks. – Rob Mar 09 '21 at 15:38
  • @IulianOnofrei - Re killing app: With no offense, I do not believe you are correct. Since iOS 7, force quitting an app would prevent most background operations, too. The old “App Programming Guide for iOS” said that (with the exception of some location related features), “the system does not relaunch apps [in the background] after they are force quit by the user ... the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system”. Force quitting is to stop tasks associated with app. – Rob Mar 09 '21 at 16:01
  • 1
    Wow, apparently, [there is a difference](https://developer.apple.com/forums/thread/62005?answerId=175862022#175862022) between the user force quitting an app, and the app being closed by the system or crashing. I always thought that they are handled the same: the app is completely closed. – Iulian Onofrei Mar 22 '21 at 12:12
  • Basically, [this answer](https://stackoverflow.com/a/19202487/865175). – Iulian Onofrei Mar 22 '21 at 12:18
23

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
Avijit Nagare
  • 8,482
  • 7
  • 39
  • 68
user2936585
  • 231
  • 2
  • 2
  • 1
    interesting that this works. Why does it never gets called if the device is closed? I sat there for good hour and nothing happened. As soon as I unlocked my phone it worked. This fetch feature is so pointless – Sam B Nov 12 '15 at 13:03
  • It worked. Thanks for this info. QUESTION: Is it the unlocking that will fire the method? Or will it have fired while locked? – Chuck Krutsinger Jun 08 '18 at 15:29
  • what happen if i set lock option as never in setting? – Ramakrishna Dec 24 '18 at 07:36
23

(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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
user1700099
  • 466
  • 5
  • 6
  • Because iOs isn't able to collect real usage statistics of your app in this mode. This value is mandatory to calculate fetch interval. – user1700099 Oct 20 '14 at 11:47
  • This did work! Are there any official references for 'usage statistics' used for calculation of fetch interval? – Swapnil Luktuke Apr 20 '17 at 11:23
  • I was able to get the fetch to fire using @user2936585 method, so "It only works if..." is not accurate. XCode->Debug->Simulate Background Fetch does work, but so does putting the app in background, lock device, wait five minutes. And the latter does work with debug build but NOT while connected to debugger. – Chuck Krutsinger Jun 08 '18 at 15:38
5

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.

Axeva
  • 4,697
  • 6
  • 40
  • 64
3

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)

Anastasia
  • 3,024
  • 1
  • 23
  • 34
2

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

adrian1kat
  • 138
  • 16
1

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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
CrazyDeveloper
  • 995
  • 1
  • 13
  • 24
  • I guess he refers to this article: https://www.raywenderlich.com/143128/background-modes-tutorial-getting-started – bio Apr 16 '18 at 11:05