I'm pretty sure it is, but you'll have to try and actually build it.
I have played some time in playground, the code now looks like:
func doSomething() {
fetchProducts()
.mapSuccess { products in
fetchProductImages(products)
} onFailureDoFinally: { error in
handleError(error: error)
}
.mapSuccess { productImages in
processProductImages(productImages)
} onFailureDoFinally: { error in
handleError(error: error)
}
}
extension Result {
@discardableResult
func mapSuccess<NewSuccess, NewFailure>(onSuccess: (Success) -> Result<NewSuccess, NewFailure>,
onFailureDoFinally: (Failure) -> Void) -> Result<NewSuccess, NewFailure>? {
switch self {
case let .success(value):
return onSuccess(value)
case let .failure(error):
onFailureDoFinally(error)
return nil
}
}
}
extension Optional {
@discardableResult
func mapSuccess<Success, Failure, NewSuccess, NewFailure>(onSuccess: (Success) -> Result<NewSuccess, NewFailure>,
onFailureDoFinally: (Failure) -> Void)
-> Result<NewSuccess, NewFailure>? where Wrapped == Result<Success, Failure> {
switch self {
case let .success(value):
return onSuccess(value)
case let .failure(error):
onFailureDoFinally(error)
return nil
case .none:
return nil
}
}
}
So, technically it can be done, but I can hardly say it is as good API:
- it is easy to get a compiler error, the error explanation is hard to understand in long chains
- it can (and will, I suppose) be incorrectly used and understood
- control flow is weird
One more variant:
func doSomething() {
guard let products = fetchProducts().getValue(onErrorDo: { handleError(error: $0) }) else { return }
guard let productImages = fetchProductImages(products).getValue(onErrorDo: { handleError(error: $0) }) else { return }
guard let processedImages = processProductImages(productImages).getValue(onErrorDo: { handleError(error: $0) }) else { return }
}
extension Result {
func getValue(onErrorDo: (Failure) -> Void) -> Success? {
switch self {
case .success(let success):
return success
case .failure(let error):
onErrorDo(error)
return nil
}
}
}
I've written synchronous variant for simplicity, async variant can also be done. May be it will help
1 Like
This, adapted from my earlier example, seems simpler:
1 Like
I totally agree that there's something missing here and I suggested a solution to this problem here:
With my suggested change, you would be able to write:
guard let products = fechProducts() catch {
handleFailure(error)
return
}
Feel free to provide your usage example in the thread, too.
4 Likes