在不影响发布者类型的情况下向发布者转换链添加额外的逻辑

Add additional logic to chain of publisher transformation without affecting Publisher type

Kotlin 的协程提供了编写非常扁平代码的能力。我正在尝试转换一些异步 iOS 代码以利用 Combine 将其展平。

Kotlin 看起来像:

private suspend fun getData(
        networkCall: Boolean = true,
        networkRequest: suspend () -> Either<FieldError, List<JobModel>>,
        localRequest: suspend () -> Either<FieldError, List<JobModel>>,
        cache: suspend (data: List<JobModel>) -> Unit
): Either<FieldError, List<JobModel>> {
    // getting of Jobs and passing in pair if result was from network
    var resultNetwork = false
    var result: Either<FieldError, List<JobModel>> = Left(GenericError)
    if (!networkCall) {
        result = localRequest()
    }
    // if it was not a network call and failed we will force network call
    if (networkCall || result.isLeft()) {
        resultNetwork = true
        result = networkRequest()
    }

    if (result is Either.Right && resultNetwork) {
        cache(result.b)
    }

    return result

}

Swift WIP 看起来像:

public func getData(isNetworkCall: AnyPublisher<Bool, Error>,
                    networkRequest: AnyPublisher<[Job], Error>,
                    localRequest: AnyPublisher<[Job], Error>,
                    cache: ([Job]) -> AnyPublisher<Void, Error>) -> AnyPublisher<[Job], Error>? {
    
    let getJobsRequest =  isNetworkCall.flatMap { (isCall) in
        return isCall
            ? networkRequest
            //.also { jobs in cache(jobs) }
            .catch{ _ in return localRequest }
            .eraseToAnyPublisher()
            : localRequest
    }.eraseToAnyPublisher()
    
    return getJobsRequest
}

如何将这种缓存数据的逻辑添加到此 AnyPublisher 中?我想让它在这个逻辑转换过程中缓存。理想情况下,可以有一个 also 函数在订阅者完成事务时附加逻辑。

解法:

private func getData(isNetworkCall: AnyPublisher<Bool, Error>,
                         networkRequest: AnyPublisher<[Job], Error>,
                         localRequest: AnyPublisher<[Job], Error>,
                         cache: @escaping ([Job]) -> AnyPublisher<Void, Error>) -> AnyPublisher<[Job], Error> {
    
    // Sequence of steps for when we should do a network call
    let networkCallFlow = networkRequest
        .flatMap { jobs in // cache jobs from network
            cache(jobs)
                .replaceError(with: ()) // fire and forget cache, replace error with Void to continue
                .map { _ in jobs } // need to give back jobs to flatMap
                .setFailureType(to: Error.self) // match failure type
        }
        .catch { _ in localRequest } // return local if network fails
        .eraseToAnyPublisher()
    
    // Sequence of steps for when we should get from local
    let localCallFlow = localRequest
        .catch { _ in networkCallFlow } // do network flow if local call fails
        .eraseToAnyPublisher()

    return isNetworkCall
        .flatMap { [=12=] ? networkCallFlow : localCallFlow }
        .eraseToAnyPublisher()
}

你可以用 flatMap 链接它。

如果 cache 也是一个 AnyPublisher<[Job], Error> 会更容易,那么你可以有以下内容:

return isCall
    ? networkRequest
        .flatMap { jobs in cache(jobs) }
        .catch{ _ in return localRequest }
        .eraseToAnyPublisher()
    : localRequest

否则,您需要将其 Void 返回值映射回 jobs:

return isCall
    ? networkRequest
        .flatMap { jobs in 
            cache(jobs).map { _ in jobs }
        }
        .catch{ _ in return localRequest }
        .eraseToAnyPublisher()
    : localRequest