0

I have in my code some asynchronous method written with a completion handler, and I want to convert it to the new Swift Concurrency syntax.

Usually this is simply a matter of wrapping the call in withCheckedThrowingContinuation and calling either resume(throwing:) or resume(returning:) instead of, respectively completion(nil, error) or completion(value, nil).

However I have in my code a method that may call the completion handler with a value AND an error (the use case is: outdated cache, but use the value if network is unreachable), aka completion(value, CacheError.outdatedCache).

What approach may I use to convert this to a throwing async method?

KPM
  • 10,558
  • 3
  • 45
  • 66
  • can you please create a short example (e.g. in Playground) demonstrating this problem? – timbre timbre Dec 05 '22 at 17:08
  • 1
    A Swift function can either return a value or throw an error. It cannot do both. So what do you expect a `throwing async` method to do when the completion handler would return both an error and a value? If you want to return both a value and an error, return a tuple containing both or create a custom type that can contain both a value and an error. – Dávid Pásztor Dec 05 '22 at 17:28
  • `resume(throwing: CacheError.outdatedCache)` on cache error. – cora Dec 05 '22 at 18:03
  • @cora - if that error is thrown, how will the cached value be retrieved? – Rob Dec 05 '22 at 18:19
  • @Rob Associated value :) – cora Dec 05 '22 at 18:22
  • 1
    Yep, I went down that road, but you’d probably want to make that associated value be generic, which is hard to specialize in the `catch` statement. I guess you could have it not be generic, but then you end up having to define a unique error for every type of possible cached response. – Rob Dec 05 '22 at 18:28
  • Yes I was also thinking of an associated value, but thought it might not be the best option. – KPM Dec 05 '22 at 19:11

1 Answers1

2

There are many alternatives. Here are a few:

  1. You could have a non-throwing rendition that returns a Response:

    enum Response<Value, Failure: Error> {
        case success(Value)
        case failure(Failure)
        case failureWithCachedValue(Failure, Value)
    }
    
  2. You could have non-throwing rendition that returns a tuple of (Foo?, Error?).

Rob
  • 415,655
  • 72
  • 787
  • 1,044