Extension for default empty value for optional publisher?

Viewed 2118

I'm trying to pass in an optional publisher to my view's .onReceive. I don't want to force wrap it like this:

let refreshPublisher: AnyPublisher<Void, Never>?

var body: some View {
    Group {
        ...
    }
    .onReceive(refreshPublisher!) {...}
}

So I'm passing an empty default value like this:

var body: some View {
    Group {
        ...
    }
    .onReceive(refreshPublisher ?? Empty<Void, Never>().eraseToAnyPublisher()) {...}
}

This is really verbose and want to add an extension on Optional publishers something like this:

extension Optional where Wrapped == Publisher { // Incorrect syntax, doesn't compile
    func orEmpty() -> AnyPublisher<Void, Never> { ... }
}

This way, I can end or doing something like this:

var body: some View {
    Group {
        ...
    }
    .onReceive(refreshPublisher.orEmpty()) {...}
}

Is something like this possible or a better way to handle optional publishers in non-optional chains?

1 Answers

To your specific question, it's certainly possible. I just wouldn't do it this way.

Note the addition of Combine. before Publisher. There's a type called Optional.Publisher that shadows Combine.Publisher and that's what is causing your error.

extension Optional where Wrapped: Combine.Publisher {
    func orEmpty() -> AnyPublisher<Wrapped.Output, Wrapped.Failure> {
        self?.eraseToAnyPublisher() ?? Empty().eraseToAnyPublisher()
    }
}

Instead of doing it this way, I'd make the default value Empty and not use Optional at all:

let refreshPublisher: AnyPublisher<Void, Never>

init(refreshPublisher: AnyPublisher<Void, Never> = Empty().eraseToAnyPublisher()) {
    self.refreshPublisher = refreshPublisher
}

I'd also rework this so that the caller can pass any publisher they want rather than forcing AnyPublisher on them:

let refreshPublisher: AnyPublisher<Void, Never>

init<Refresh>(refreshPublisher: Refresh) where Refresh: Publisher,
    Refresh.Output == Void, Refresh.Failure == Never {
        self.refreshPublisher = refreshPublisher.eraseToAnyPublisher()
}

init() {
    self.init(refreshPublisher: Empty())
}
Related