5

I followed all the steps in order to set up the background fetch but I'm suspecting that I made a mistake when writing the function performFetchWithCompletionHandlerin the AppDelegate.

Here is the warning that I get as soon as I simulate a background fetch

Warning: Application delegate received call to -  application:
performFetchWithCompletionHandler:but the completion handler was never called.

Here's my code :

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    if let tabBarController = window?.rootViewController as? UITabBarController,
            viewControllers = tabBarController.viewControllers as [UIViewController]! {
      for viewController in viewControllers {
        if let notificationViewController = viewController as? NotificationsViewController {
         firstViewController.reloadData()
         completionHandler(.NewData)
         print("background fetch done")
      }
    }
  }
}

How can I test if the background-fetchis working ?

AziCode
  • 2,510
  • 6
  • 27
  • 53

1 Answers1

3

If you don't enter the first if statement, the completion handler will never be called. Also, you could potentially not find the view controller you're looking for when you loop through the view controllers, which would mean the completion would never be called. Lastly, you should probably put a return after you call the completion handler.

func application(
    application: UIApplication,
    performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    guard let tabBarController = window?.rootViewController as? UITabBarController,
        let viewControllers = tabBarController.viewControllers else {
        completionHandler(.failed)
        return
    }

    guard let notificationsViewController = viewControllers.first(where: { $0 is NotificationsViewController }) as? NotificationsViewController else {
        completionHandler(.failed)
        return
    }

    notificationViewController.reloadData()
    completionHandler(.newData)
}
AdamPro13
  • 7,232
  • 2
  • 30
  • 28
  • But my viewController is always there, so theoretically it should always be found right ? and it should always enter inside the If statement ? – AziCode Jul 13 '16 at 16:29
  • 1
    I would think so, but the warning seems to be proving otherwise. – AdamPro13 Jul 13 '16 at 16:32
  • Do I need to loop through the view controllers in order to invoke a function located in one of my VC from within the 'performFetchWithCompletionHandler' ? – AziCode Jul 13 '16 at 16:55
  • 1
    You could do that. Another option would be to create properties in your tab bar controller for each tab. Those properties could just return the view controller at each respective index. It'd break if you ever changed the order of your tabs or replaced one, but that shouldn't happen THAT often. I'd also recommend adding asserts to those getters to validate the return type is correct. – AdamPro13 Jul 13 '16 at 17:03
  • Can you explain how the completion handler works, I have a more detailed question where I was able to make it work in one case and not in another: http://stackoverflow.com/questions/38258399/how-to-properly-use-the-completionhandler-when-performing-a-background-fetch?noredirect=1#comment63984606_38258399 – AziCode Jul 13 '16 at 17:16
  • 1
    The easiest solution would be to put a `return` statement immediately after you call the completion handler above. Then outside the first `if`, I'd call `completionHandler(.Failed)`. That will ensure the completion handler is always called, even if your UI isn't set up. – AdamPro13 Jul 13 '16 at 17:53
  • Actually I just updated my question to show the code that I'm using, I think I'm doing something wrong when I loop through the view controllers ? Maybe that is why it never get inside the body of the loop ? Do you see any issue in my loop ? – AziCode Jul 13 '16 at 17:57
  • 1
    I don't see any issues with how you're trying to locate that view controller, but I am also not familiar with how you have your UI set up. If you're not doing anything weird, this should work. You still aren't addressing your original problem that the completionHandler isn't being called in some cases. Because the OS handles creating the UI hierarchy & whatnot, you can't necessarily guarantee all those VCs have been created. You need to protect yourself in that case. Updating answer w/ code. – AdamPro13 Jul 13 '16 at 18:02
  • If I use your line `"viewControllers = tabBarController.viewControllers as? [UIViewController]"` I get this error `Downcast from '[UIViewController]?' to '[UIViewController]' only unwraps oprionals; did you mean to use '!' ` – AziCode Jul 13 '16 at 18:26
  • 2
    Updated. You don't need to try to cast the view controller array. – AdamPro13 Jul 13 '16 at 18:32