1. 错误类型的转换
假设我们使用自定义的错误类型作为流的 Failure 类型。如果 tryMap 抛出的错误类型和流定义的 Failure 类型不匹配,Combine 会自动尝试将错误转化为流的 Failure 类型。
比如:
enum MyError: Error {
case invalidResponse
}
struct MyPublisher: Publisher {
typealias Output = String
typealias Failure = MyError
}
let publisher = MyPublisher()
.tryMap { value -> String in
// 假设这里抛出一个自定义的错误
throw MyError.invalidResponse
}
在这种情况下,tryMap 抛出的 MyError.invalidResponse 错误会被包装成流的 Failure 类型 MyError。
tryMap 捕获错误:tryMap 捕获你在闭包中抛出的错误。
错误转换为 Failure 类型:tryMap 会确保你抛出的错误符合 Publisher 定义的 Failure 类型。
错误传播:错误在流中被传播到下游订阅者,通常是通过 .sink 的 receiveCompletion 闭包。
这种机制使得 Combine 在处理错误时非常一致和方便,确保了流中的错误类型始终保持一致。
2. 两个Publisher融合,传递给第三个Publisher
假设我们有两个 Publisher
,一个是用户信息(User
),另一个是用户的地址信息(Address
)。我们希望在收到这两个数据之后,将它们合并为一个包含用户信息和地址的新的数据类型(UserWithAddress
)。
假设我们有如下的数据结构:
struct User {
let id: Int
let name: String
}
struct Address {
let userId: Int
let street: String
let city: String
}
struct UserWithAddress {
let id: Int
let name: String
let address: String
}
- 一个
Publisher
发出User
类型的对象 - 另一个
Publisher
发出Address
类型的对象
将它们合并,返回一个新的对象 UserWithAddress
,其中包含了用户的名称和地址。
import Combine
import Foundation
// 模拟获取用户信息
func fetchUser() -> AnyPublisher<User, Never> {
return Just(User(id: 1, name: "John Doe"))
.delay(for: .seconds(1), scheduler: DispatchQueue.main)
.eraseToAnyPublisher()
}
// 模拟获取用户地址信息
func fetchAddress() -> AnyPublisher<Address, Never> {
return Just(Address(userId: 1, street: "123 Main St", city: "Springfield"))
.delay(for: .seconds(2), scheduler: DispatchQueue.main)
.eraseToAnyPublisher()
}
// 合并这两个信号,将用户信息和地址信息组合成一个 UserWithAddress
let userPublisher = fetchUser()
let addressPublisher = fetchAddress()
let combinedPublisher = userPublisher
.combineLatest(addressPublisher)
.map { user, address in
// 将用户信息和地址合并为一个新的 UserWithAddress 对象
return UserWithAddress(id: user.id, name: user.name, address: "\(address.street), \(address.city)")
}
// 订阅合并后的结果
var cancellables = Set<AnyCancellable>()
combinedPublisher
.sink(receiveValue: { userWithAddress in
print("User: \(userWithAddress.name), Address: \(userWithAddress.address)")
})
.store(in: &cancellables)
// 输出结果:
// User: John Doe, Address: 123 Main St, Springfield
fetchUser
和fetchAddress
:这两个方法分别模拟了返回User
和Address
的网络请求或数据源。我们使用Just
发布者来模拟返回的数据,并延迟 1 到 2 秒来模拟网络请求延时。combineLatest
操作符:我们使用combineLatest
来等待userPublisher
和addressPublisher
都发出数据。只有当这两个Publisher
都发出数据时,我们才会触发map
操作符。map
操作符:在map
中,我们将User
和Address
数据合并成一个新的UserWithAddress
对象。这里我们将地址的街道和城市拼接成一个字符串。订阅:最终,我们通过
.sink(receiveValue:)
订阅合并后的结果,并打印出UserWithAddress
的信息。
总结:
这种情况是将两个数据类型通过某种关系合并为一个新的数据类型。可以使用 combineLatest
或者 zip
操作符来结合来自不同 Publisher
的数据流,然后通过 map
操作符进行转换。在 map
中,我们可以把两个数据类型融合成一个新的对象,通常这种情况适用于存在包含关系的场景,比如将用户信息和地址信息结合为一个包含用户和地址的对象。