Using Swift-4.0.3, iOS-11.2, Xcode-9.2, iPhone-6S (or Simulator-10.0)
Since about a week I try to figure out what the problem is with the following issue: The UI freezes whenever you re-enter a ViewController that involves a URLSession-Background-DownloadTask. By re-entering, I mean: Going from VC to Detail-VC and back to VC...and then RE-ENTER a second time from VC to the Detail-VC.
Please find my example project here on github : https://github.com/korners/Test00008
The sample project uses the MZDownloadManager from mzeeshanid. I tried also other frameworks - same issue. The MZDownloadManager is just a very nice implementation I found.
Back to the issue: Now - as for the first entry of the Detail-VC: Everything runs smoothly as it should (no issues). Even a closed App will smoothly kick-in to the already running background-downloadTask (no issues - i.e. progressBar and UI-labels update nicely).
But if the user presses the back button on the Detail-VC (top bar of the NavigationController) - from this moment on, the Detail-VC can only be seen in a frozen default-state ! (no UI updates or progressBar-movements anymore whatsoever).
I highly appreciate any help on this !
P.S. By the way, it is not the Segue per se that creates the problems. I also tried instantiating VC from the storyboard - and the same thing: RE-ENTERING freezes the UI :/
Yeah, I think you got it right.
In my case I had a list based VC (lets call it parent VC) and detail VC. Detail VC would create a background session and set itself as a delegate to it. I also noticed that if I exit and re-enter detail VC something would be wrong. I came to the same conclusion, session was giving updates to the first detail VC, the one I though was gone, killed when I navigated back from it.
You see, first detail VC creates session as a property so it has a pointer to the session. It sets itself as a delegate on the session so now session has a pointer to it. This is called a cycle and is a cause of memory leaks. Thats why first detail VC is not killed - session is still pointing to it. Usually this would be fixed by having a weak reference in one direction but as you already discovered there is another problem here: You can't just go around and create more than one background session with the same identifier. You need to create one and hold on to it as a singleton.
After some more debugging I figured out that at the re-enter of the Detail-VC, the delegate of the URLSesssion is
nil
.As it turns out - (if set up as background-task) - then the URLSession still existing from a previous download, does not create a new URLSession object but returns the existing one WITH THE OLD DELEGATE OBJECT ATTACHED !!!
The way the project was set up: By pressing the back-button (or leaving the current Detail-VC) will throw away everything form that ViewController (including its downloadManager-property and his delegate). Therefore, if re-entering the Detail-VC then the URLSession kicking in from previous run will try to send its delegate-methods to the URLSession-delegate that no longer exists !!! (i.e. the one killed when leaving the Detail-VC).
Interestingly enough, if the App is closed and re-started, then the delegate of the Background-URL-session can be newly set. The holding on to the delegate is only happening as long as the App is running, obviously.
Thinking loudly, ...one way to go about this, I guess, is for example by keeping a View-object alive (and give it to the background-URLSession as its delegate) and then presenting this View-object whenever there is a need (with
addsubView(View)
or similar). But in general, it is a bad idea to have unique View-objects throughout the App.The best way to go about this, I guess, is to create a singleton-URLSession-object in the first place (that survives throughout and is not affected by any Navigation-controller segue'ing). And therefore also its delegate survives throughout the App.
My take-home message for now: BE CAREFUL WHO YOU ASSIGN TO THE BACKGROUND-URLSESSION TO BE ITS DELEGATE AND WHAT YOUR APP-LIFECYCLE DOES WITH IT (it won't easily let go of it - not even if the URLSession object (and its delegate together with it) are dead. ;) It will only let go of it if you close the App and re-start)
Hope, I got that all right ?
Any suggestions or comments to this subject highly appreciated !