Почему у нас нет оператора tryFlatMap в Combine? - PullRequest
0 голосов
/ 22 апреля 2020

А что мы будем использовать вместо этого?
Я удивлен, что никто раньше не спрашивал об этом.

Ответы [ 2 ]

3 голосов
/ 22 апреля 2020

Поскольку преобразование flatMap возвращает издателя, вам не нужно строго tryFlatMap. Вы можете использовать do/catch внутри закрытия преобразования и возвращать издателя Fail, если обнаружите ошибку.

import Combine

func someFunction(of i: Int) throws -> AnyPublisher<Int, Error> {
    return Just(i + 1)
        .setFailureType(to: Error.self)
        .eraseToAnyPublisher()
}

let upstream: AnyPublisher<Int, Error> = Just(100)
    .setFailureType(to: Error.self)
    .eraseToAnyPublisher()

upstream
    .flatMap({ i -> AnyPublisher<Int, Error> in
        do {
            return try someFunction(of: i).eraseToAnyPublisher()
        } catch {
            return Fail(error: error).eraseToAnyPublisher()
        }
    })

Вы можете написать свой собственный оператор tryFlatMap, если хотите:

extension Publisher {
    func tryFlatMap<Pub: Publisher>(
        _ transform: @escaping (Output) throws -> Pub
    ) -> Publishers.FlatMap<AnyPublisher<Pub.Output, Error>, Self> {
        return flatMap({ input -> AnyPublisher<Pub.Output, Error> in
            do {
                return try transform(input)
                    .mapError { $0 as Error }
                    .eraseToAnyPublisher()
            } catch {
                return Fail(outputType: Pub.Output.self, failure: error)
                    .eraseToAnyPublisher()
            }
        })
    }
}

А затем используйте это так:

upstream
    .tryFlatMap { try someFunction(of: $0) }
0 голосов
/ 23 апреля 2020

Еще одно решение.
tryFlatMap = tryMap + flatMap.

Например.
Вся история началась с того, что я просто хотел развернуть дополнительный. И если это ноль, я хочу просто потерпеть неудачу.

let upstream = Just(siteURL)
    .tryMap { url -> URL in
        guard let url = url else { throw Errors.invalidSiteURL }
        return url
    }
    .flatMap {
        URLSession.shared.dataTaskPublisher(for: $0).mapError { $0 as Error }
    }

[Отредактировано] Так же это работает:

let container = Just(siteURL)
    .tryMap { url -> URLSession.DataTaskPublisher in
        guard let url = url else { throw Errors.invalidSiteURL }
        return URLSession.shared.dataTaskPublisher(for: url)
    }
    .map { $0.mapError { $0 as Error } }
    .switchToLatest()

Беспорядок с типами ошибок немного раздражает.

...