0

I've made an extension for Publisher to simplify sink, but my backend (not made by me) is messed up and I have a custom error that shouldn't be an error, and I want to return .success.

extension Publisher {
    func sinkToResult(_ result: @escaping ((Result<Self.Output, Self.Failure>) -> Void)) -> AnyCancellable {
        return sink(receiveCompletion: { completion in
            switch completion {
            case .failure(let error):
                if let error = error as? ApiError, let globalError = error.globalErrors.first, globalError.code == 2233 {
                    //Here i want to send something like return(.success(true))
                }
                result(.failure(error))
            case .finished:
                break
            }
        }, 
        receiveValue: { output in
            result(.success(output))
        })
    }
}

Can you help me to create a custom Output.Self type that I can return here?

Fabio Felici
  • 2,841
  • 15
  • 21
Marta Paniti
  • 152
  • 6

2 Answers2

1

I think you have a couple of options:

enum MyResult<T> {
  case noError
  case object(T)
}

and then change func signature to: func sinkToResult(_ result: @escaping ((Result<MyResult<Self.Output>, Self.Failure>) -> Void)) -> AnyCancellable

Or you can wrap Result and add your specific case:

enum MyResult<Success, Failure: Error> {
  case noError
  case result(Result<Success, Failure>)
}

and change signature to: func sinkToResult(_ result: @escaping ((MyResult<Self.Output, Self.Failure>) -> Void)) -> AnyCancellable.

Either way, I think it would be better to handle this directly when you perform the JSON decoding.

Fabio Felici
  • 2,841
  • 15
  • 21
  • Thnak you! Unfortunately the problem is that in this case I don't have any JSON, only a simple text response from server, so my API call always gives me error (I'm using a custom component with Alamofire). So I have to do this workaround for now – Marta Paniti Sep 20 '22 at 12:28
0

I think what you need is tryCatch before you get to sink

func tryCatch<P>(_ handler: @escaping (Self.Failure) throws -> P) -> Publishers.TryCatch<Self, P> where P : Publisher, Self.Output == P.Output

Apple docs say:

Handles errors from an upstream publisher by either replacing it with another publisher or throwing a new error.

You could just add it as the last step in your pipeline:

///...
.tryCatch { error in
    if let error = error as? ApiError, let globalError = error.globalErrors.first, globalError.code == 2233 {
        return Just(true)
    } else {
        throw error
    }
}

It would be difficult to make this generic if you wanted this publisher to return something different depending on circumstances (i.e true or an empty Array etc.)

LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57