4

Following the recommendation from https://stackoverflow.com/a/27712427/3286489, I can perform asynchronous download as below, and it is called during viewDidLoad.

    func loadItems(tuple : (name : String, imageURL : URL)) {
        print("Download Started")
        URLSession.shared.dataTask(with: tuple.imageURL, completionHandler :
        { data, response, error in
            guard let data = data, error == nil else { return }
            print(response?.suggestedFilename ?? tuple.imageURL.lastPathComponent)
            print("Download Finished")
            DispatchQueue.main.async() { [weak self] in
                self?.displayFlag(data: data, title: tuple.name)
            }
        }).resume()
    }

This all works well. My question is, in the event of a slow download, and I exit the page, how and where (viewDidUnload? looks that that is deprecated already) can I cancel the fetching (image downloading) task?

Elye
  • 53,639
  • 54
  • 212
  • 474
  • 1
    Does this answer your question? [How to cancel a URL session request](https://stackoverflow.com/questions/46617961/how-to-cancel-a-url-session-request) – Claus Jørgensen Oct 23 '20 at 04:55
  • Thanks @ClausJørgensen. Looks like I can assign `URLSession.shared.dataTask` as a task, and get it cancel. Where should I do it? Is it in `viewDidUnload`? – Elye Oct 23 '20 at 05:02
  • That very much depends on your navigation flow. `viewDidDisappear` is probably a good bet, but then you may want to load the URL in `viewDidAppear` rather than `viewDidLoad`, again depending on when/how your page is loaded in a given navigation flow. – Claus Jørgensen Oct 23 '20 at 05:06
  • 1
    FWIW, `viewDidUnload` isn’t called anymore. I’d personally do it in `deinit`. – Rob Oct 23 '20 at 05:07
  • @Elye FYI, you should also look into Combine APIs, where the `AnyCancellable` type will handle the scenario for you. – Claus Jørgensen Oct 23 '20 at 05:13
  • Yes, I see that wtih `dataTaskPublisher`. What's the difference of using it compared to `dataTask`?, since `task` can be `cancel` too? – Elye Oct 23 '20 at 05:16
  • It'll automatically stop the task once the AnyCancellable is deallocated (so along with your view, no need to call `.cancel()` manually in deinit). And in general, neater syntax and [easier data mapping / error handling](https://gist.github.com/clausjoergensen/d4a517dc3dc904d0652c6529213c6b6b) – Claus Jørgensen Oct 23 '20 at 05:25
  • I see that's interesting. I didn't know that. I thought I still need to call `cancel` on `deinit`. In this case, when would be a good time to use `URLSession.shared.dataTask`? I have just posted a question in https://stackoverflow.com/q/64494369/3286489. Maybe you can help answer there? Thanks! (Sorry, I came from Android background, and confuse over all the iOS framework). – Elye Oct 23 '20 at 05:31

1 Answers1

4

Save the URLSessionTask reference:

private weak var task: URLSessionTask?

func loadItems(name: String, url: URL) {
    let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
        guard let data = data, error == nil else { return }
        print(response?.suggestedFilename ?? url.lastPathComponent)
        print("Download Finished")
        DispatchQueue.main.async {
            self?.displayFlag(data: data, title: name)
        }
    }
    task.resume()
    self.task = task
}

Then, when dismissing, cancel it:

deinit {
    task?.cancel()
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Nice. Complete answer. Assign a task variable and perform `cancel` at `deinit`. Thanks!! – Elye Oct 23 '20 at 05:09
  • Wouldn't you need the `[weak self]` in the `dataTask` closure to avoid retaining the entire page? (which is important here, as `deinit` requires the page to be deallocated, OP might not know that) – Claus Jørgensen Oct 23 '20 at 05:09
  • 1
    Both work, but I've moved the `weak` reference to remove any potential ambiguity. – Rob Oct 23 '20 at 05:23
  • After canceling the task how can I recall that request? I am getting `data = nil` if I simply try to call the request after canceling it like this. @Rob – Tulon Feb 27 '22 at 17:42
  • I’m not entirely sure what you mean by “recall that request”. But if you cancel a request, it’s done. If you want to request for that URL again, then initiate a new request. – Rob Feb 27 '22 at 17:45