Home > Enterprise >  Publisher extension for .sink
Publisher extension for .sink

Time:09-20

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?

CodePudding user response:

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.

CodePudding user response:

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.)

  • Related