Как продолжить подписку на издателя после ошибки? - PullRequest
0 голосов
/ 29 мая 2020

Я пытаюсь настроить издателя, который будет публиковать sh набор целых чисел и в какой-то момент может выйти из строя. Это немного надумано, но, надеюсь, иллюстрирует принцип. Пример ниже.

enum NumberError: Int, Error {
   case isFatal, canContinue
}

struct Numbers {
    let p = PassthroughSubject<Int, NumberError>()

    func start(max: Int) {

        let errorI = Int.random(in: 1...max)
        for i in (1...max) {
            if errorI == i {
                p.send(completion: .failure(NumberError.canContinue))
            } else {
                p.send(i)
            }
        }
        p.send(completion: .finished)

    }
}

Затем я подписываюсь, используя:

let n = Numbers()
let c = n.p
    .catch {_ in return Just(-1)}

    .sink(receiveCompletion: {result in
        switch result {
        case .failure:
            print("Error")
        case .finished:
            print("Finished")
        }
    }, receiveValue: {
        print($0)
    })

n.start(max: 5)

Это работает тем, что заменяет ошибки на -1, но затем я хотел бы продолжить получение значений. Кто-нибудь знает возможно ли это? Прочитав и осмотревшись, кажется, что flatMap может быть путем к go, но я не могу понять, какого издателя использовать в закрытии? Любая помощь очень ценится.

1 Ответ

0 голосов
/ 30 мая 2020

Я думаю, вы ошибаетесь, что PassthroughSubject может опубликовать sh дополнительных выходных данных после публикации ошибки. Оно не может. После того, как вы наберете p.send(completion: ...), все вызовы на p.send(...) будут игнорироваться. Кроме того, если вы подпишетесь на p после того, как позвоните p.send(completion: ...), p немедленно завершит новую подписку и не отправит никаких выходных данных.

Таким образом, вы не можете отправить свою ошибку как .failure, если вы хотите отправить больше значений после. Вместо этого измените тип Output вашего издателя на Result<Int, NumberError>, а его тип Failure на Never:

import Combine

enum NumberError: Int, Error {
   case isFatal, canContinue
}

struct Numbers {
    let p = PassthroughSubject<Result<Int, NumberError>, Never>()

    func start(max: Int) {
        let bad = (max + 1) / 2
        for i in (1...max) {
            if bad == i {
                p.send(.failure(NumberError.canContinue))
            } else {
                p.send(.success(i))
            }
        }
        p.send(completion: .finished)
    }
}

Но теперь вы не можете использовать catch для обработки ошибки, потому что это не считаться неудачей. Вместо этого вы можете использовать map:

let n = Numbers()
let c = n.p
    .map({
        switch $0 {
        case .success(let i): return i
        case .failure(_): return -1
        }
    })
    .sink(receiveCompletion: {result in
        switch result {
        case .failure:
            print("Error")
        case .finished:
            print("Finished")
        }
    }, receiveValue: {
        print($0)
    })

n.start(max: 5)

Вывод:

1
2
-1
4
5
Finished
...