将 nil 替换为 Empty 或 Combine 中的 Error

Replace nil with Empty or Error in Combine

我有这样的 Combine 发布商:

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 publisher 替换 nil 吗?如果不能,我可以用 Combine 链中的 error emptyResponse 替换它吗?第一个选项更可取。

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

.flatMap {
    [=10=].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()
    }
}

这里有几个选项。

如果您不希望发布者在实体为空的情况下发布任何内容,您可以使用 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
    }
}