Combine中使用Zip Publisher拉取数据后如何在同管线预下载车辆图片
解决方案
报错根因
No exact matches in call to instance method 'flatMap'报错的核心是Combine管线上下流的Failure类型不匹配:flatMap要求闭包内返回的Publisher的失败类型必须和上游Publisher完全一致。你代码中把Just的失败类型强制设置为Error.self,如果VehicleApiClient().getVehicles()返回的Publisher是自定义错误类型(比如ApiError),二者类型无法对齐就会触发编译错误。
修正后的车辆+图片下载管线
先统一错误类型,保证上下流类型对齐:
let vehiclesWithImagesPublisher = VehicleApiClient().getVehicles() .mapError { $0 as Error } // 将上游自定义错误转换为通用Error,对齐下游类型 .flatMap { vehicles -> AnyPublisher<[Vehicle], Error> in // 仅对存在有效imageUrl的车辆发起下载请求 let imageDownloadPublishers = vehicles.compactMap { vehicle in vehicle.imageUrl.flatMap { VehicleApiClient().getImage(at: $0) } } // 等待所有图片下载完成后返回原车辆数组 return Publishers.Zip( Just(vehicles).setFailureType(to: Error.self), Publishers.MergeMany(imageDownloadPublishers).collect() ) .map { $0.0 } // 如果需要将下载的图片赋值给车辆对象,可修改上面的map逻辑即可: // .map { vehicles, images in // var updatedVehicles = vehicles // // 按规则将图片赋值给对应车辆 // return updatedVehicles // } .eraseToAnyPublisher() }
整合到原有完整管线
替换原Zip操作符的第二个参数即可实现所有图片全部下载完成后再处理业务逻辑的需求:
func fetchUserAndvehicles() { Publishers.Zip( UserApiClient().getUser().mapError { $0 as Error }, vehiclesWithImagesPublisher ) .receive(on: DispatchQueue.main) .sink(receiveCompletion: { [weak self] completion in switch completion { case .failure(let error): self?.errorHandling.showErrorAlert(error) case .finished: break } }, receiveValue: { [weak self] user, vehicles in // 此时所有车辆图片已下载完成,可直接存储车辆数据到用户对象 user.vehicles = vehicles }) .store(in: &subscriptions) }
如果需要容忍部分图片下载失败不中断整个流程,可以给单个图片下载请求添加错误兼容处理:
let imageDownloadPublishers = vehicles.compactMap { vehicle in vehicle.imageUrl.flatMap { url in VehicleApiClient().getImage(at: url) .catch { _ in Just(UIImage(named: "placeholder") } // 下载失败返回占位图 .setFailureType(to: Error.self) } }
内容的提问来源于stack exchange,提问作者Ivan C Myrvold
相关产品推荐
相关产品推荐

