如何快速理解 swift Combine
Combine 是 Swift 中的一个框架,用于处理和组合异步和事件驱动的操作。
要快速理解 Combine,可以从以下几个关键概念入手:
Publisher: 发布者是一个对象,它可以发出一系列的事件,比如值、错误或完成信号;
Subscriber: 订阅者是一个对象,它可以订阅一个或多个发布者,并对发布者发出的事件做出响应;
Operators(操作符):Combine 提供了许多操作符,可以用于转换、组合和处理发布者发出的事件。例如,map 用于映射值,filter 用于过滤事件,merge 用于合并多个发布者的事件等。
Cancellable(可取消):订阅一个发布者会返回一个 Cancellable 对象,你可以使用这个对象来取消订阅,释放资源;
Subject(主题):主题是 Combine 中的一种特殊类型的发布者,既可以发送值也可以接收值。有 PassthroughSubject 和 CurrentValueSubject 等类型;
Schedulers(调度器):Combine 使用调度器来确定在哪个线程上执行操作。有一些内置的调度器,比如 DispatchQueue.main 和 DispatchQueue.global();
下面是一个简单的例子,演示了 Combine 中的一些概念:
import Combine
// 创建一个发布者,发出一系列整数
let publisher = [1, 2, 3, 4, 5].publisher
// 订阅者,处理发布者发出的事件
let subscriber = Subscribers.Sink<Int, Never>(receiveValue: { value in
print("Received value: \(value)")
})
// 订阅发布者
let cancellable = publisher.sink(receiveValue: { value in
print("Received value: \(value)")
})
// 取消订阅
cancellable.cancel()
一个复杂一点的例子,演示了一个 viewModel 中combine的使用:
enum ContentMode: Equatable {
case `default`
case text(text: String)
case voice(path: String)
}
class ViewModel {
/// 当前选中图片index
@Published
var currentPhotoIndex: Int = 0
/// 文字脚本内容
@Published
var textScript: String = ""
var textLanguage: String = ""
/// 文字脚本 对应的语音音色
@Published // TODO 类型修改为 详情中对应Model
var speechVoice: SpeechVoiceView.Voice?
/// 语音脚本内容
@Published
var audioScript: String = ""
/// 输入内容样式Mode
@Published
var contentMode: ContentMode = .default
/// 是否能够生成
@Published
var enableGenerate: Bool = false
/// 详情数据
@Published
var detail: DetailModel?
/// 用户图
@Published
var userPhotos: [PTStylePhoto] = []
/// Demo图
@Published
var demoPhotos: [PTStylePhoto] = []
/// 用户图+Demo图
@Published
var allPhotos: [PTStylePhoto] = []
// 是否结束播放
@Published
var isEndVideoPlay: Bool = false
// 是否结束播放
@Published
var isEndAudioPlay: Bool = false
let itemWidth = ScreenWidth - 16.0 * 4
var contentModeName: String {
switch contentMode {
case .default:
return ""
case .text(_):
return "text"
case .voice(_):
return "voice"
}
}
var cancellables = Set<AnyCancellable>()
init() {
initialize()
}
private func initialize() {
$demoPhotos
.sink { [weak self] in
guard let self else { return }
allPhotos = userPhotos + $0
}.store(in: &cancellables)
// 查询用户图
PTUserPhotoManager.manager.$allUserPhotos
.map({ $0.map { PTStylePhoto(userPhoto: $0) } })
.sink { [weak self] value in
guard let self else { return }
userPhotos = value
}
.store(in: &cancellables)
//
PTUserPhotoManager.manager.queryUserPhoto()
// 请求详情数据
PhotoTalkAPI.shared.publisher(.detail, type: DetailModel.self)
.loading("loading...")
.map { $0.result }
.retry(2)
.sink(receiveCompletion: { error in
}, receiveValue: { [weak self] value in
guard let self else { return }
detail = value
if let demoList = value?.demoList {
demoPhotos = demoList.map({
PTStylePhoto(demo: $0)
})
}
})
.store(in: &cancellables)
$userPhotos.combineLatest($demoPhotos)
.dropFirst()
.map { $0 + $1 }
.assign(to: &$allPhotos)
// 输入内容状态
let textPublisher = $textScript.filter({
!$0.isEmpty
}).map {
ContentMode.text(text: $0)
}.removeDuplicates()
let audioPublisher = $audioScript.filter({
!$0.isEmpty
}).map {
ContentMode.voice(path: $0)
}.removeDuplicates()
textPublisher
.merge(with: audioPublisher)
.assign(to: &$contentMode)
// 能否合成状态
$contentMode.sink { [unowned self] x in
switch x {
case .default:
enableGenerate = false
case let .text(text):
enableGenerate = !text.isEmpty
case let .voice(path):
enableGenerate = !path.isEmpty
}
}.store(in: &cancellables)
}
}
这只是 Combine 的入门,你可以通过深入学习每个概念、查阅文档以及进行实际的应用来更全面地理解 Combine。 Combine 的强大之处在于它提供了一种声明式的方式来处理异步操作,使得代码更易于理解和维护。
想详细了解Combine,请转以下链接:
https://icodesign.me/posts/swift-combine/
Combine中的操作符,逐个举例说明
Combine 框架中提供了许多强大的操作符,用于处理和组合异步事件流。下面是一些常用的 Combine 操作符,逐个举例说明它们的用法:
map 操作符:用于映射每个元素。
let publisher = [1, 2, 3].publisher
_ = publisher.map { $0 * 2 }
.sink { value in
print(value) // 输出:2, 4, 6
}
filter 操作符:用于过滤元素。
let publisher = [1, 2, 3, 4, 5].publisher
_ = publisher.filter { $0 % 2 == 0 }
.sink { value in
print(value) // 输出:2, 4
}
combineLatest 操作符:用于合并多个发布者的最新元素。
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<String, Never>()
_ = publisher1.combineLatest(publisher2)
.sink { value in
print(value)
}
publisher1.send(1)
publisher2.send("A")
// 输出:(1, "A")
merge 操作符:
用于合并多个发布者的元素。
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
_ = Publishers.Merge(publisher1, publisher2)
.sink { value in
print(value)
}
publisher1.send(1)
publisher2.send(2)
// 输出:1, 2
flatMap 操作符:
用于将每个元素映射到一个新的发布者,然后将这些发布者的元素合并成一个新的发布者。
let publisher = [1, 2, 3].publisher
_ = publisher.flatMap { value in
Just(value * 2)
}
.sink { value in
print(value) // 输出:2, 4, 6
}
scan 操作符:
用于对元素进行累积操作。
let publisher = [1, 2, 3].publisher
_ = publisher.scan(0) { accumulator, value in
accumulator + value
}
.sink { value in
print(value) // 输出:1, 3, 6
}
这只是一小部分 Combine 操作符,它们提供了强大的工具,用于处理和组合异步事件流。
根据具体的业务需求,你可以组合使用这些操作符来构建复杂的异步数据处理流程。