【问题标题】:Replace nil with Empty or Error in Combine在合并中将 nil 替换为空或错误
【发布时间】:2021-03-11 09:14:02
【问题描述】:

我有一个这样的联合发布者:

enum RemoteError: Error {
    case networkError(Error)
    case parseError(Error)
    case emptyResponse
}

func getPublisher(url: URL) -> AnyPublisher<Entiy, RemoteError> {
    return URLSession.shared
        .dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: RemoteResponse.self, decoder: decoder)
        .mapError { error -> RemoteError in
            switch error {
            case is URLError:
                return .networkError(error)
            default:
                return .parseError(error)
            }
        }
        .map { response -> Entiy in
            response.enitities.last
        }
        .eraseToAnyPublisher()
}

struct RemoteResponse: Codable {
    let enitities: [Entity]
    let numberOfEntries: Int
}

struct Entity {

}

通过上述设置,编译器会报错,因为response.enitities.last 可以为 nil。问题是我可以用 Empty 发布者替换 nil 吗?如果不能,我可以用 Combine 链中的错误 emptyResponse 替换它吗?第一个选项更可取。

【问题讨论】:

    标签: swift reactive-programming combine


    【解决方案1】:

    这里有几个选项。

    如果您不希望发布者在实体为空的情况下发布任何内容,您可以使用coampactMap 代替地图:

    .compactMap { response in
       response.entities.last
    }
    

    如果您希望在这种情况下发布错误,可以使用tryMap,它允许您抛出Error。你需要mapError 来关注它:

    .tryMap { response in
        guard let entity = response.entities.last else {
            throw RemoteError.emptyResponse
        }
        return entity
    }
    .mapError { error -> RemoteError in
        switch error {
        case is URLError:
            return .networkError(error)
        case is DecodingError:
            return .parseError(error)
        default:
            return .emptyResponse
        }
    }
    

    【讨论】:

    • 你到底是什么意思?
    • 在 iOS 13 和 Combine 第一个版本中,它抱怨您应该返回 Entity 而不是 Entity?即使使用 CompactMap。我知道这听起来很奇怪,但这就是问题所在。
    • Weir compactMap 自 iOS 13 以来没有改变,您可以尝试更明确的方式:.compactMap { response -&gt; Entity? in guard let entity = response.entities.last else { return nil } return entity }
    【解决方案2】:

    您需要一个平面地图才能映射到另一个发布者

    .flatMap {
        $0.enitities.last.publisher
    }
    

    Optional 有一个方便的publisher 属性,它为您提供一个发布者,如果该值不为 nil,则该发布者只发布该值,如果该值为 nil,则为空发布者。这仅适用于 iOS 14+。如果您的目标是较低版本,则需要执行以下操作:

    .flatMap { (response) -> AnyPublisher<Entity, Never> in
        if let last = response.entities.last {
            return Just(last).eraseToAnyPublisher()
        } else {
            return Empty(completeImmediately: true).eraseToAnyPublisher()
        }
    }
    

    【讨论】:

    • 'flatMap(maxPublishers:_:)' 仅适用于 iOS 14.0 或更高版本
    • @SoheilNovinfard 嗯,显式类型可能会让它心烦意乱。查看编辑。
    • $0.enitities.last.publisher 仅适用于 iOS 14
    • @SoheilNovinfard 您可以添加if @available 检查,然后查看编辑。
    猜你喜欢
    • 2020-05-29
    • 2011-03-16
    • 2022-01-12
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-13
    相关资源
    最近更新 更多