1. 典型的 Combine 类型
在 Combine 中,常见的 Publisher 类型包括:
- Just<Output>:用于发布单个值,并且完成。失败时没有错误。
- Future 是一个会异步发出一个值或错误的 Publisher。
- PassthroughSubject<Output, Failure>:用于主动发布事件,适用于需要手动发送值的场景。
- CurrentValueSubject<Output, Failure>:与 PassthroughSubject 相似,但它会保留当前值,新的订阅者会立即收到该值。
- AnyPublisher<Output, Failure>:类型擦除的 Publisher,用于隐藏内部的实现细节,暴露统一的输出类型和错误类型。
以下是针对 Just<Output>
、PassthroughSubject<Output, Failure>
和 CurrentValueSubject<Output, Failure>
的三个简单示例,它们分别展示了这些类型的使用场景。
-
Just<Output>
示例
Just<Output>
用于发布一个单一的值,并在发布后完成。这是一个最简单的 Publisher 类型,适用于你只需要发送一个固定值并结束的场景。
import Combine
let publisher = Just("Hello, Combine!")
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed")
case .failure(let error):
print("Failed with error: \(error)")
}
}, receiveValue: { value in
print("Received value: \(value)")
})
// 输出:
// Received value: Hello, Combine!
// Completed
// Just 创建了一个 Publisher,这个 Publisher 发布了一个单一的值 "Hello, Combine!",然后就完成了。
// sink 用来订阅并处理值和完成事件。
-
Future
示例
Future 是一个会异步发出一个值或错误的 Publisher,它的类型是 Future<Output, Failure>,通常用于封装异步操作。
import Combine
let future = Future<String, Error> { promise in
// 模拟异步操作
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
promise(.success("Async Result"))
}
}
-
PassthroughSubject<Output, Failure>
示例
PassthroughSubject<Output, Failure>
是一个允许手动发布事件的 Publisher,它可以在运行时主动发布多个值。它是最常用的 Subject 类型之一。
import Combine
// 定义一个 PassthroughSubject
let passthroughSubject = PassthroughSubject<String, Never>()
// 订阅者
let subscriber = passthroughSubject
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed")
case .failure(let error):
print("Failed with error: \(error)")
}
}, receiveValue: { value in
print("Received value: \(value)")
})
// 主动发送值
passthroughSubject.send("First message")
passthroughSubject.send("Second message")
passthroughSubject.send("Third message")
// 完成发送
passthroughSubject.send(completion: .finished)
// 输出:
// Received value: First message
// Received value: Second message
// Received value: Third message
// Completed
// `PassthroughSubject` 可以在运行时主动发送多个值,直到通过 `.send()` 方法发送一个值。
// `sink` 用来订阅并处理这些值,直到 `PassthroughSubject` 发送 `.finished` 完成事件。
-
CurrentValueSubject<Output, Failure>
示例
CurrentValueSubject<Output, Failure>
类似于PassthroughSubject
,但它会保持当前值,当新的订阅者订阅时,它会立即接收到最新的值。
import Combine
// 定义一个 CurrentValueSubject,初始值为 "Initial Value"
let currentValueSubject = CurrentValueSubject<String, Never>("Initial Value")
// 订阅者1
let subscriber1 = currentValueSubject
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed")
case .failure(let error):
print("Failed with error: \(error)")
}
}, receiveValue: { value in
print("Subscriber1 received: \(value)")
})
// 主动发送新值
currentValueSubject.send("Updated Value")
// 订阅者2:将会立即接收到 "Updated Value"
let subscriber2 = currentValueSubject
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed")
case .failure(let error):
print("Failed with error: \(error)")
}
}, receiveValue: { value in
print("Subscriber2 received: \(value)")
})
// 发送新的值
currentValueSubject.send("Another Update")
// 输出:
// Subscriber1 received: Initial Value
// Subscriber1 received: Updated Value
// Subscriber1 received: Another Update
// Subscriber2 received: Updated Value
// Subscriber2 received: Another Update
// `CurrentValueSubject` 初始化时会有一个默认值(此处为 `"Initial Value"`)。当一个新的订阅者订阅时,它会立即接收到这个默认值。
// 随后,任何通过 `send()` 发送的新值都会被当前的订阅者接收到。
Just<Output>
:用于发布一个固定的单一值,并完成。适合简单的场景。
PassthroughSubject<Output, Failure>
:允许手动发布多个值,适合动态发布事件的场景。
CurrentValueSubject<Output, Failure>
:会保留当前值,新的订阅者会立即接收到当前的值。适合需要持有状态并向新订阅者提供当前状态的场景。
2. 如何创建 AnyPublisher
以下是一个简化版的例子,展示如何在 Combine 中使用 AnyPublisher 来隐藏具体的 Publisher 类型。
方法 eraseToAnyPublisher() 在这一过程中非常关键:
import Combine
enum DownloadError: Error {
case networkError
case fileNotFound
}
// 自定义 Publisher 类型
func downloadFile(from url: URL) -> AnyPublisher<Data, DownloadError> {
// 模拟网络请求的返回
let data = Data("Downloaded content".utf8)
// 使用 Just 来模拟返回数据
return Just(data)
.setFailureType(to: DownloadError.self) // 设置错误类型
.eraseToAnyPublisher() // 转换为 AnyPublisher
}
// 使用自定义 Publisher
let publisher = downloadFile(from: URL(string: "https://example.com/file")!)
publisher.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Download finished")
case .failure(let error):
print("Download failed with error: \(error)")
}
}, receiveValue: { data in
print("Received data: \(data)")
}).store(in: &cancellables)
组合多个 Publisher 并隐藏类型:假设你有两个异步任务,分别返回不同类型的 Publisher。你可以使用 eraseToAnyPublisher() 将它们组合为统一的类型,方便在外部使用。
import Combine
class DataFetcher {
var cancellables = Set<AnyCancellable>()
func fetchName() -> AnyPublisher<String, Error> {
Just("John")
.setFailureType(to: Error.self)
.eraseToAnyPublisher() // 转换为 AnyPublisher
}
func fetchAge() -> AnyPublisher<Int, Error> {
Just(30)
.setFailureType(to: Error.self)
.eraseToAnyPublisher() // 转换为 AnyPublisher
}
func fetchData() -> AnyPublisher<(String, Int), Error> {
// 使用 combineLatest 合并两个 Publisher
return Publishers.CombineLatest(fetchName(), fetchAge())
.eraseToAnyPublisher() // 将合并后的 Publisher 转换为 AnyPublisher
}
}
let fetcher = DataFetcher()
fetcher.fetchData()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Data fetch complete")
case .failure(let error):
print("Error occurred: \(error)")
}
}, receiveValue: { name, age in
print("Received data: \(name), \(age)")
})
.store(in: &fetcher.cancellables)
3. 总结
AnyPublisher<ProgressResult, APIError> 是 Combine 中的一个类型擦除的 Publisher,表示它发布 ProgressResult 类型的值,并且可能会失败并发布 APIError 类型的错误。
通过 eraseToAnyPublisher() 将具体的 Publisher 类型转换为统一的类型,这样外部代码只需要关心输出类型和错误类型,而不需要关心内部具体的实现类型。