ViewDidLoad() seems to run when coming out of suspended state into background?

My app runs two quick background download tasks evey time a background fetch is sent to it by the OS. A local notification "GotData" is sent by the background task when the web data has arrived and was processed.

In normal active operation I register my app's root view controller, "Main", to receive the local notification "GotData". This works fine and allows the view to get updated as soon as the fresh data has arrived.

    override func viewDidLoad() {
    super.viewDidLoad()
        let notificationName = Notification.Name("GotData")
        NotificationCenter.default.addObserver(self, selector: #selector(updateGUI), name: notificationName, object: nil)

The view removes itself from receiving notifications:

    override func viewWillDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self)
    }


When the app is in the foreground, receiving the notification causes only the selector to run, not the entire VDL. When the app has been in the background a while and the web fetch completes and sends the GotData notification, it seems that the entire Main VDL is rerun. It responds to the notifcation (which is fine) but then also does all the ususal VDL stuff. This is a problem because it's unanticipated. It does not happen all the time even when the app is in the background, but seems likely to be happening when it comes out of suspension.

Does it make sense the entire VDL is run in full when the notification is sent by the background download task?

It might. "viewDidLoad" runs at the end of view loading, which sounds like a stupid thing to say, but the point is that views are loaded from an archive separately from "appearing". If you app goes into the background, the view and the archive it came from might be unloaded to save memory, and the view might be reloaded when you return to the foreground. In that case, it would be correct for "viewDidLoad" to run again.


Note that "viewWillDisappear" is not the inverse of "viewDidLoad", but of "viewWillAppear". "viewDidLoad" may be called 0, 1, 2 (or whatever) times for each time "viewWillDisappear" is called. [There used to be a "viewDidUnload" method, but it was abolished a few years ago.]


Your best solution is to keep track of whether the view controller currently is observing, and test this before re-establishing the observation in "viewDidLoad". That way you don't care how the various methods are paired.

I added some more detailed logging statements (I'm using and loving SwiftyBeaver logging) and once again let my app run all night. I can confirm that the root view controller does indeed get reloaded. I didn't count them all, but it looks like it reloads every time the background downloads complete. I had no idea!


I had sent the app to the background while viewing a view other than Main, but each time the app was awakened, it reloaded the Main, which is the root. I don't think it reloaded the view I had been least viewing.


My app succeeded in running a background fetch about every 15 minutes all night until 4:48AM, when things just ceased. Reloading the Main VDL did result in an error almost every time

Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."


I suppose the OS gets tired of all these errors and stops sending fetches to my app? I'll fix the reloading problem that and see if it improves.


My app always seems to die (stop running fetches, no more logging) every nght around 3-4AM. Could there be some other reason for this? The app can still be seen as an open app, but everything completely stops. No fetches, no logging, nothing. The app can be reactivated and everything seems fine again.

This might be something you'd use a TSI (tech support incident) with Apple to gain insight into what's going on. You may have an architectural issue (e.g dealing with the background download in a view controller, which is intrinsically a foreground context), or you may just be seeing a consequence of memory filling up. Chances are, there's a better way to organize the code so that it recovers better.

Well the baby made it through the night without messing itself! 😍


Once I tweaked the viewDidLoad of my root view controller to anticipate running when the app comes out of the background to run a fetch, my app has been fetching for over 22 hours.


There are still some question marks. The average time between fetches was 20 minutes, which would be fine, but I also saw a range from 200ms (this happened three times) up to an hour or more. Those extremes are both a problem.


I've set my minimum background fetch interval to 180 seconds. But there's no way to ensure the system sends timely fetches, which for my app needs to be at least every 30 minutes or so. It looks like I'm going to have to use remote push notifications. 😕


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
     UIApplication.shared.setMinimumBackgroundFetchInterval(180)  // No reason for fetches less than 3 minutes apart
     UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert,.sound,.badge],
            completionHandler: { (granted,error) in
             }
        )
     let console = ConsoleDestination()  //  These lines initialize SwiftyBeaver logging
     let file = FileDestination()
     logSB.addDestination(console)
     logSB.addDestination(file)       
     return true
 }
ViewDidLoad() seems to run when coming out of suspended state into background?
 
 
Q