Combine 异步事件流的组合和处理
这段代码是Swift语言中的一段使用Combine框架的代码,主要用于处理异步事件流的组合和处理。
/// 创建一个合并请求
private(set) lazy var combine = CombineAction<String?, (CreateConfigModel?, DetailModel?), APIError> { [unowned self] input in
let detailSignal = PhotoTalkApi.shared.publisher(.detail, type: JSON.self)
.tryMap({
if let data = try? $0.result?.rawData() {
AppDatabase.shared[.talkingDetailData] = data
return DetailModel.decode(with: data)
} else {
throw APIError.undefined
}
}).mapError({
APIError(error: $0)
}).eraseToAnyPublisher()
guard let input else {
return detailSignal
.map({ (nil, $0) }).eraseToAnyPublisher()
}
let tagSignal = PhotoTalkApi.shared.publisher(.toolTags(route: "phototalk://page/creator/detail?type=\(input)"), type: JSON.self)
.tryMap({
if let data = try? $0.result?.rawData() {
AppDatabase.shared[.talkingConfigData(input)] = data
return CreateConfigModel.decode(with: data)
} else {
throw APIError.undefined
}
}).mapError({
APIError(error: $0)
})
.eraseToAnyPublisher()
return tagSignal.combineLatest(detailSignal)
.eraseToAnyPublisher()
}
----------------------------------------------------------------
/// 详情数据 + 配置数据
combine.values
.prepend((configModel, detailModel))
.sink(receiveValue: { [weak self] tuple in
guard let self else { return }
// ...
}).store(in: &cancellables)
/// 错误处理
combine.errors
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] error in
// ...
}).store(in: &cancellables)
/// 发起请求
if type == // ... {
combine.execute(nil)
} else {
combine.execute(type.value)
}
下面是对代码的详细解读:
private(set) lazy var combine:这是一个懒加载的属性,其类型是CombineAction<String?, (CreateConfigModel?, DetailModel?), APIError>。这个属性使用了private(set),表示在类内部可以读写,但在外部只能进行读取操作。
CombineAction:这是一个自定义的Combine操作符,用于将两个不同类型的Publisher合并到一个Publisher中。它的泛型参数分别是输入类型(String?)、输出类型((CreateConfigModel?, DetailModel?))和错误类型(APIError)。
[unowned self]:这是一个捕获列表,用于避免循环引用。在Combine中,由于可能存在长时间的订阅,需要小心避免循环引用问题。
let detailSignal = ...:这里创建了一个名为detailSignal的Publisher,用于处理获取详细信息的API请求。具体步骤包括:
使用PhotoTalkAPI.shared.publisher创建一个Publisher,订阅了一个名为.detail的API端点,并指定返回类型为JSON。
使用tryMap操作符处理API的结果,将原始数据存储到本地数据库中,然后通过DetailModel.decode将数据解码为DetailModel。
使用mapError操作符将可能的错误映射为APIError。
使用eraseToAnyPublisher将整个操作链转换为AnyPublisher类型。
guard let input else { ... }:这是一个guard语句,用于检查输入是否为nil。如果输入为nil,则直接返回detailSignal的Publisher,其中使用map将输出映射为(nil, $0)。
let tagSignal = ...:这里创建了一个名为tagSignal的Publisher,用于处理获取标签信息的API请求。具体步骤与detailSignal类似。
return tagSignal.combineLatest(detailSignal):使用combineLatest操作符将tagSignal和detailSignal两个Publisher的最新值进行组合,生成一个新的Publisher,其输出类型为元组(CreateConfigModel?, DetailModel?)。
.eraseToAnyPublisher():最后,通过eraseToAnyPublisher将整个操作链转换为AnyPublisher类型,以符合combine属性的类型要求。
eraseToAnyPublisher 的作用:eraseToAnyPublisher 是 Combine 框架中的一个操作符,其主要作用是将一个具体类型的 Publisher 转换为 AnyPublisher 类型。在 Combine 中,AnyPublisher 是一个类型擦除的抽象,用于隐藏底层具体类型,使其更加通用和灵活。
Combine 中的 Publisher 类型是泛型的,它包含了特定的输出类型和错误类型。在某些情况下,你可能想要隐藏具体类型的 Publisher,以便在 API 的设计或函数签名中更加灵活,不暴露太多实现细节。
这是 eraseToAnyPublisher 的一些关键作用:
类型擦除:将一个具体类型的 Publisher 转换为 AnyPublisher,隐藏了底层具体类型,使其在 API 设计中更加通用。
接口灵活性:通过返回 AnyPublisher,可以更轻松地适应接受 Publisher 参数的函数或方法,而无需暴露太多的具体实现细节。
错误处理:AnyPublisher 会擦除具体的错误类型,将其统一为 Swift 的 Error 类型,这可以使错误处理更加灵活,不需要关心具体的错误类型。
示例代码中使用了 eraseToAnyPublisher 主要是为了符合 combine 属性的声明,因为该属性的类型是 CombineAction<String?, (CreateConfigModel?, DetailModel?), APIError>,而 combineLatest 操作返回的 Publisher 的具体类型可能是其他类型,为了满足类型的匹配,使用了 eraseToAnyPublisher 进行类型擦除。这使得 combineLatest(detailSignal, tagSignal) 的返回值变成了 AnyPublisher<(CreateConfigModel?, DetailModel?), APIError>,适应了 combine 属性的声明。
总体而言,这段代码的主要功能是通过Combine框架处理两个异步API请求,将它们的结果合并为一个Publisher,并在最后将整个操作链包装成一个懒加载的属性combine。