Networking with URLSession - Part 7: Background | Ray Wenderlich

Download and upload tasks can run in a background session. Find out how the system handles this, and learn some advice and tips for long-running tasks.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4321-networking-with-urlsession/lessons/7

Hi Audrey,
Why table view cell not updating after return on table view controller from another view controller? After the click on download the progress view and label not updating. Only If I use dismiss works, but this is not a good solution.
Thanks for your attention.

Best regards,
Michele.

@audrey Can you please help with this when you get a chance? Thank you - much appreciated! :]

hi Michele:

I tried this: downloaded a song, started another download and quickly tapped the already-downloaded song to play it, then returned to the table view — the second song was still downloading and still showing the progress view etc. I downloaded another song, and the progress view still appeared.

what are your steps / other view controller?

1 Like

The problem manifests itself when return on table view controller from another view without passing from navigation controller. In your project I added a button that shows a new view controller, when I return on table view controller the cell not updating. I understand that the problem manifests itself when the view controller has loaded its view hierarchy into memory the second time.

have you tried putting a breakpoint in the didWriteData delegate method? does the app stop calling this when you go to the new view controller?

and you’re on the main queue when looking for trackCell?

if let trackIndex = trackIndex(for: downloadTask) {
  DispatchQueue.main.async {
    if let trackCell = self.tableView.cellForRow(at: IndexPath(row: trackIndex, section: 0)) as? ProgressUpdateDelegate {
      trackCell.updateDisplay(progress: download.progress, totalSize: totalSize)
    }
  }
}
1 Like

After the return trackCell is nil, but If I add self.collectionView.layoutIfNeeded() like first line in Dispatch it isn’t nil but the cell not updating. updateDisplay is called but the cell not updating.
Thanks for your attention.

you’re using this?

if let trackCell = self.collectionView.cellForItem(at: IndexPath(row: trackIndex, section: 0))

is the index path correct after returning from the other view controller?

or has the download finished by the time you return?

1 Like

Yes, I’am using that. The index path is correct.
This is an example, when return from View Controller all cell not updating after click on download:


Thanks.

use breakpoints to track what happens when you tap Download: in downloadTapped, startDownload, etc.

1 Like

I will try this way.
Thank you for your attention.

1 Like

Hi Audrey,

The video at 19:53, I am wondering if it is supposed to call completionHandler() before
appDelegate.backgroundSessionCompletionHandler = nil ? Thanks!

hi mike: the order of that code is straight from Apple: Listing 1-12

the address of appDelegate.backgroundSessionCompletionHandler is stored safely in completionHandler, which suspends the app, so it must be the last thing called.

Hello.
This part of source produces warning:
if let trackIndex = trackIndex(for: downloadTask) {
if let trackCell = tableView.cellForRow(at: IndexPath(row: trackIndex, section: 0)) as? ProgressUpdateDelegate {
DispatchQueue.main.async {
trackCell.updateDisplay(progress: download.progress, totalSize: totalSize)
}
}
}

The warning is: UITableView.cellForRow(at:) must be used from main thread only
How to get rid of this warning?

hi! are you looking at the Demo finished version of HalfTunes? that section of code is there to demonstrate the main thread checker, and above it are commented out statements that do it correctly:

//      if let trackIndex = trackIndex(for: downloadTask) {
//        DispatchQueue.main.async {
//          if let trackCell = self.tableView.cellForRow(at: IndexPath(row: trackIndex, section: 0)) as? ProgressUpdateDelegate {
//            trackCell.updateDisplay(progress: download.progress, totalSize: totalSize)
//          }
//        }
//      }

// Switch to this version of progress tracking, to demo main thread API checker
      if let trackIndex = trackIndex(for: downloadTask) {
        if let trackCell = tableView.cellForRow(at: IndexPath(row: trackIndex, section: 0)) as? ProgressUpdateDelegate {
          DispatchQueue.main.async {
            trackCell.updateDisplay(progress: download.progress, totalSize: totalSize)
          }
        }
      }
    }
  }

Hello Audrey,

Regarding the TopTunes app, can you tell me if the data argument in the delegate method didRecieve data can sometimes be populated with incomplete \ invalid JSON data when we’re using background sessions? Seems to be happening to me and I’ve verified the JSON Is incomplete by writing it out. I’ve had to put try - catch around decoder.decode(TrackList.self, from: data) to prevent the app from crashing.

Is there a way to prevent incomplete JSON in this background session scenario?

I haven’t experienced the incomplete JSON issue when using a default session and the datatask closure.

Thank You,
Mike

hi Mike: that sounds very strange! the same request fetches different JSON, depending on default vs background session? what’s your search term?

Thanks for the quick response. The background session wasn’t fetching different JSON, it’s just that it was clipped off. Instead of top ten, I’d get half of that and closing curly braces weren’t reliably present since data was chopped off. If I change the urlStrings to give me the top 1 or top 2 hot tracks like below… seems like the results are better and more reliable:

let urlString = “https://rss.itunes.apple.com/api/v1/us/apple-music/hot-tracks/all/1/non-explicit.json
let urlString = “https://rss.itunes.apple.com/api/v1/us/apple-music/hot-tracks/country/1/non-explicit.json

Hope I’m being clear.

Mike

ah, so you need to wait until the download is complete before you try to parse.

but the search query doesn’t usually run in the background session? that’s just for the 30-second snippets?

I think videos need to break into smaller sections - 25 - 30 mins videos are too long and I keep getting lost. esp. Background Sessions video.

Referencing an existing app (halftunes) which is not built from start as part of this course is not helping.

I think a fresh course where an App can be built from scratch will keep concepts fresh and flow easy.

Hope you guys consider this. Networking is a very important topic and subscribers would love to get a good grasp over how to do basic stuff efficiently with code thats easy to read and understand.