Home > Software design >  How to declare an extension on Optional where Wrapped is a Result?
How to declare an extension on Optional where Wrapped is a Result?

Time:08-09

I use a pattern like this for mocking:

protocol Foo {
    func bar() -> AnyPublisher<Bool, Never>
}

final class MockFoo: Foo {

    var barResult: Result<Bool, Never>?

    init(barResult: Result<Bool, Never>? = nil) {
        self.barResult = barResult
    }

    func bar() -> AnyPublisher<Bool, Never> {
        switch barResult {
        case let .some(result):
            return result
                .publisher
                .eraseToAnyPublisher()
        case .none:
            return Empty<Bool, Never>()
                .eraseToAnyPublisher()
        }
    }
}

Which means I end up mapping a Result? to publisher. It is trivial but I type it so often that I am really tempted to make it generic in order to be able to just write this:

func bar() -> AnyPublisher<Bool, Never> {
    barResult.publisher
}

Unfortunately I am having problems with defining the extension on Optional. I need something roughly like this:

extension Optional where Wrapped == Result<Success,Failure> {

    var publisher: AnyPublisher<Success, Failure> {
        switch self {
        case .none:
            return Empty<Success, Failure>()
                .eraseToAnyPublisher()
        case let .some(result):
            return result.publisher
                .eraseToAnyPublisher()
        }
    }

}

But the above won't compile. Is it possible? How to declare it so that the compiler understands?

CodePudding user response:

You can achieve your goals by adding the generic type constraints to newly declared generic method rather than trying to add it to the extension, which is not possible. You also need to change publisher to a function rather than computed property, since you cannot add generic type constraints to a computed property, only to a function.

extension Optional {
  func publisher<Success, Failure>() -> AnyPublisher<Success, Failure> where Wrapped == Result<Success, Failure> {
    switch self {
    case .none:
        return Empty<Success, Failure>()
            .eraseToAnyPublisher()
    case let .some(result):
        return result.publisher
            .eraseToAnyPublisher()
    }
  }
}
  • Related