watchOS: Complications

Feb 7 2023 Swift 5.6, watchOS 8.5, Xcode 13

Part 1: Introduction to Complications

7. Update with Background URL Downloads

Episode complete

Play next episode

About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 6. Update with Background Tasks Next episode: 8. Update with Push Notifications

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Pro subscription. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

This video Update with Background URL Downloads was last updated on Feb 7 2023

Heads up... You've reached locked video content where the transcript will be shown as obfuscated text.

Downloading data from the network follows the same general pattern as background tasks.

URLSession setup and configuration

To start, create a file named UrlDownloader.swift

final class UrlDownloader: NSObject {

  let identifier: String

  init(identifier: String) {
    self.identifier = identifier
  private lazy var backgroundUrlSession: URLSession = {
    let config = URLSessionConfiguration.background(
      withIdentifier: identifier
    config.isDiscretionary = false
    config.sessionSendsLaunchEvents = true
    return .init(
      configuration: config,
      delegate: self,
      delegateQueue: nil
extension UrlDownloader: URLSessionDownloadDelegate {
  func urlSession(
    _ session: URLSession,
    downloadTask: URLSessionDownloadTask,
    didFinishDownloadingTo location: URL
  ) {

Scheduling a network download

Now, similar to how we created a background task in the previous episode, we can create a URL Sessions Download Task.

private var backgroundTask: URLSessionDownloadTask?
func schedule(firstTime: Bool = false) {
  let minutes = firstTime ? 1 : 15

  let when =
    byAdding: .minute,
    value: minutes,
  let url = URL(
    string: ",80/forecast"
  let task = backgroundUrlSession.downloadTask(with: url)
  task.earliestBeginDate = when
  task.countOfBytesClientExpectsToSend = 100
  task.countOfBytesClientExpectsToReceive = 12_000
  backgroundTask = task


When the backgroundTask finishes downloading, watchOS will call this urlSession(_:downloadTask:didFinishDownloadingTo:) method we left empty earlier.

let decoder = JSONDecoder()

else {
else {
  let data = try? Data(contentsOf: location),
  let decoded = try? decoder.decode(Weather.self, from: data),
  let temperature =
else {
UserDefaults.standard.set(temperature, forKey: "temperature")
func urlSession(
  _ session: URLSession,
  task: URLSessionTask,
  didCompleteWithError error: Error?
) {

  backgroundTask = nil

  DispatchQueue.main.async {
    self.completionHandler?(error == nil)
    self.completionHandler = nil

Preparing for download

So let’s add one at the top of UrlDownloader:

private var completionHandler: ((Bool) -> Void)?
public func perform(_ completionHandler: @escaping (Bool) -> Void) {
  self.completionHandler = completionHandler
  _ = backgroundUrlSession

Alright, let’s tie this all together in back in ExtensionDelegate.swift

private var downloads: [String: UrlDownloader] = [:]
private func downloader(for identifier: String) -> UrlDownloader { 

  guard let download = downloads[identifier] else {
    let downloader = UrlDownloader(identifier: identifier)
    downloads[identifier] = downloader
    return downloader
  return download
case let task as WKURLSessionRefreshBackgroundTask:
  let downloader = downloader(for: task.sessionIdentifier)
  downloader.perform { updateComplications in
    if updateComplications {

Updating ContentView

In ContentView.swift, add a URLDownloader.

@State private var downloader = UrlDownloader(identifier: "ContentView")
Button {
  downloader.schedule(firstTime: true)
} label: {