Intermediate Combine

Apr 13 2021 · Swift 5.3, macOS 11.1, Xcode 12.2

Part 1: Intermediate Combine

05. Retrying and Catching Errors

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 04. Mapping Errors Next episode: 06. Testing Combine Operators

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

Take your career further with a Kodeco Personal Plan. 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.

Transcript: 05. Retrying and Catching Errors

There will be times in processing data where you’ll need to retry if a failure is encountered. Networking is a great example here. If a download only partially completes, you can use Combine’s retry operator to retry the download.

If other errors are encountered, including while retrying, you could try catching those errors to keep your app running smoothly. Let’s dive right into an example using retry and catch.

Included in this playground’s Sources folder is a PhotoService.swift file that contains a mechanism to fetch a photo in either low or high quality using a custom publisher. However, when asking for a high quality image, it always fails, so you can experiment with techniques for handling errors.

Make an instance of the PhotoService. Make an example block that calls fetchPhoto on the photoService and subscribe to the pipeline with a sink. In the receivedValue block, list the image and print a message to console.

let photoService = PhotoService()

example(of: "Catching and retrying") {
  photoService
    .fetchPhoto(quality: .low)
    .sink(
      receiveCompletion: { print("\($0)") },
      receiveValue: { image in
        image
        print("Got image: \(image)")
      }
    )
    .store(in: &subscriptions)
}

Run this in the playground, and the console shows you the result of the fetch. You can even use the playground to look at the image.

Change the quality from .low to .high and run the playground again. This time the console reports an error. Maybe we just need to try harder - add a call to retry after the fetchPhoto call.

.retry(3)

You get a free retry mechanism for every piece of work in a publisher thanks to the retry operator. Before running this, add a handleEvents call between the fetchPhoto and retry calls

.handleEvents(
  receiveSubscription: { _ in print("Trying ...") },
  receiveCompletion: {
    guard case .failure(let error) = $0 else { return }
    print("Got error: \(error)")
  }
)

This will help you see when the retries occur. Run the playground. You’ll see the initial try and 3 retries. Update the fetchPhoto call to have an extra argument - failingTimes. This will cause it to fail twice and then succeed on the third try.

.fetchPhoto(quality: .high, failingTimes: 2)

Run the playground again, and this time the high quality version of the image is received!

What if you want to fall back to a default image if the fetch fails? Remove the failingTimes argument from fetchPhoto and add a call to replaceError after the call to retry.

.replaceError(with: UIImage(named: "na.jpg")!)

Run this in the playground and look at the result. Instead of showing an error, it displays the default image.

What if you wanted to try to fallback to the low quality image if the fetch failed? Add a call to catch after the retry but before replaceError(with:)

.catch { error -> PhotoService.Publisher in
  print("Failed fetching high quality, falling back to low quality")
  return photoService.fetchPhoto(quality: .low)
}

Run this in the playground. As before an initial attempt is followed by 3 failures, but then the catch operator comes in and requests a low quality version of the image.