Advanced Error Handling

Beyond the basic .onError() block, Then offers several tools for more sophisticated error handling scenarios.

recover

The .recover() method allows you to gracefully handle an error and continue the promise chain with a valid result. It's like a catch block that can provide a fallback value.

Recover with a Static Value

You can provide a default value to be used if the promise fails.

fetchAvatarURL()
    .recover(with: defaultAvatarURL) // If fetch fails, use the default URL
    .then { url in
        // This will be called with either the fetched URL or the default URL
        imageView.setImage(from: url)
    }

You can also recover from a specific error type:

.recover(NetworkError.offline, with: cachedAvatarURL)

Recover with a Block

For more complex logic, you can provide a block that receives the error and returns a recovery value. You can even throw a different error from this block.

fetchData()
    .recover { error -> Data in
        if error is NetworkError {
            return cachedData
        } else {
            // Don't know how to handle this error, so re-throw it
            throw error
        }
    }

Recover with another Promise

You can also return a new promise from the recover block, for example, to try an alternative asynchronous operation.

fetchPrimaryServer()
    .recover { error -> Promise<Data> in
        print("Primary server failed, trying backup...")
        return fetchBackupServer()
    }
    .then { data in
        // We have data from either the primary or backup server
    }

retry

The .retry() method allows you to automatically restart a failing promise a specified number of times. This is useful for transient network errors.

fetchData()
  .retry(3) // Will try the fetchData() operation up to 3 times on failure
  .then { data in
   // Succeeded within 3 tries
  }.onError { e in
    // Failed 3 times in a row
  }

bridgeError

The .bridgeError() method allows you to intercept a low-level error and replace it with a more meaningful, high-level domain error. This is great for isolating your application logic from underlying framework details.

api.fetchUser(id: 123)
    .bridgeError(to: DomainError.userFetchFailed) // Catches any error and replaces it
    .onError { error in
        // error is guaranteed to be DomainError.userFetchFailed
    }

You can also bridge only specific errors:

// Only bridge URLSession's URLError.notConnectedToInternet
.bridgeError(URLError.notConnectedToInternet, to: DomainError.offline)