在不影响发布者类型的情况下向发布者转换链添加额外的逻辑
Posted
技术标签:
【中文标题】在不影响发布者类型的情况下向发布者转换链添加额外的逻辑【英文标题】:Add additional logic to chain of publisher transformation without affecting Publisher type 【发布时间】:2021-01-21 16:33:12 【问题描述】: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 $0 ? networkCallFlow : localCallFlow
.eraseToAnyPublisher()
【问题讨论】:
【参考方案1】:你可以用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
【讨论】:
这带来的问题是 - 当cache()
抛出错误时会发生什么? - 理想情况下,它应该传递网络的结果,但在这种情况下,它会抛出本地的任何内容,即使该数据由于缓存失败而已过时。我正在寻找一种本质上触发并忘记cache(...)
的方法,并且如果进行了网络调用,则始终传递网络的结果。这适用于我们希望 cache(...)
按预期工作但在其他情况下会出现问题的情况。
所以,如果你想忽略cache
抛出的错误,你可以这样做:cache(jobs).replaceError(with: jobs).map _ in jobs
天才!由于缓存是无效的,它将是cache(jobs).replaceError(with: ()).map _ in jobs
我将发布代码并在完全测试后接受。以上是关于在不影响发布者类型的情况下向发布者转换链添加额外的逻辑的主要内容,如果未能解决你的问题,请参考以下文章
在不使用 reloadData 的情况下向 UITableView 添加一行
如何在不锁定表的情况下向 Postgres 中的 ENUM 添加新值?
Jackson:如何在不修改 POJO 的情况下向 JSON 添加自定义属性
如何在不阻止 textView 触摸的情况下向所有 UITextView 添加一个 UIGestureRecognizer