1. 创建详细使用的高级组合 View AdvancedCombineBootcamp.swift
import SwiftUI
import Combine
/// 数据服务
class AdvancedCombineDataService{
// @Published var basicPublisher: String = "first publish"
// CurrentValueSubject 通用函数
// let currentValuePublisher = CurrentValueSubject<Int, Error>("first publish")
// 发布者数据
let passThroughPublisher = PassthroughSubject<Int, Error>()
// 发布者数据
let boolPublisher = PassthroughSubject<Bool, Error>()
// 发布者数据
let intPublisher = PassthroughSubject<Int, Error>()
init(){
publishFakeData()
//publishFakeData2()
}
//发布模拟数据
private func publishFakeData(){
// Array(1 ..< 11)
// 重复数据用来过滤重复数据使用,并且是连续的才有效果
// let items: [Int] = [1, 2, 3, 4, 4, 5, 5, 4, 6, 7, 8, 9, 10]
//, 11, 12, 13, 14, 15, 16, 17, 18
let items: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for x in items.indices {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(x)) {
self.passThroughPublisher.send(items[x])
if x > 4 && x < 8 {
self.boolPublisher.send(true)
self.intPublisher.send(999)
}else {
self.boolPublisher.send(false)
}
if x == items.indices.last{
//获取最后一个数据,计算最大值/最小身上 必须打开完成关闭操作,需要知道数据的区间范围
self.passThroughPublisher.send(completion: .finished)
//self.boolPublisher.send(completion: .finished)
}
}
}
}
// 发布模拟数据2,配合 .debounce 使用
private func publishFakeData2(){
DispatchQueue.main.asyncAfter(deadline: .now() + 0) {
self.passThroughPublisher.send(1)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.passThroughPublisher.send(2)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
self.passThroughPublisher.send(3)
}
}
}
/// ViewModel
class AdvancedCombineBootcampViewModel: ObservableObject{
@Published var data: [String] = []
@Published var dataBools: [Bool] = []
@Published var error: String = ""
let dataService = AdvancedCombineDataService()
var cancellable = Set<AnyCancellable>()
// 多播数据
let multiCastPublisher = PassthroughSubject<Int, Error>()
init() {
addSubscribers()
}
// 添加订阅
private func addSubscribers(){
//.$basicPublisher currentValuePublisher
// dataService.passThroughPublisher
// Sequence Operations: 序列运算
/*
// 第一个数据操作
// .first()
// .first(where: {$0 > 4})
// .tryFirst(where: { value in
// if value == 3 {
// throw URLError(.badServerResponse)
// }
// return value > 1
// })
// 最后一个数据操作
// last: 获取最后一个值,监听值需要加 .send(completion: .finished),表示执行到最后
// .last()
// .last(where: { $0 < 4})
// .tryLast(where: { value in
// if value == 13{
// throw URLError(.badServerResponse)
// }
// print("\(value)")
// value < 4 = 3
// value > 4 = 10
// return value > 4
// })
// 删除操作
// 删除第一个值
// .dropFirst()
// 删除前三个
// .dropFirst(3)
// drop 保留小于号,不支持大于号,因为大于号开始就删除,返回 false,表示执行闭包结束,删除成功,没有实际的意义
// .drop(while: { $0 < 5 })
// .tryDrop(while: { value in
// if value == 5{
// throw URLError(.badServerResponse)
// }
// return value < 6
// })
// 返回前几个操作
// prefix(4) 取前 4 个
// $0 > 5 闭包立即返回 false,前缀实际上完成了,所以不返回
// $0 < 5 返回数组中的前 五 的元素,第一个条件必须为真,否则不返回
// .prefix(while: { $0 < 5 })
// .tryPrefix(while: )
// 根据下标索引,输出数值
// .output(at: 3)
// 根据 输出范围
// .output(in: 2 ..< 4)
*/
// Mathematic Operations: 数学运算
/*
// 获取最大值
// .max()
// 两者相比较,取最大值: 10
// .max(by: { value1, value2 in
// return value1 < value2
// })
// .tryMax(by: )
// 获取最小值
// .min()
// 两者相比较取最小值
// .tryMin(by: { value1, value2 in
// return value1 < value2
// })
*/
// Filter / Reducing Operations: 过滤 / 减少运算
/*
// 遍历数据
// .map({ String($0) })
// 遍历到 5 抛出异常,并结束
// .tryMap({ value in
// if value == 5 {
// throw URLError(.badServerResponse)
// }
// return String(value)
// })
// 遍历去除值为 5 的数据
// .compactMap({ value in
// if value == 5 {
// return nil
// }
// return String(value)
// })
// 遍历到 5 抛出异常,并结束
// .tryCompactMap({ value in
// if value == 5 {
// throw URLError(.badServerResponse)
// }
// return String(value)
// })
// 过滤器 输出大于3 小于7 的值
// .filter({ ($0 > 3) && ($0 < 7)})
// 输出到 3 直接抛出异常,并结束
// .tryFilter({ value in
// if value > 3 && value < 7{
// throw URLError(.badServerResponse)
// }
// return true
// })
// 删除重复项
// 删除数据中相同的数据,必须是相邻的,否则不起作用
// .removeDuplicates()
// 删除重复项 于 removeDuplicates 删除重复项的情况完全相同
// .removeDuplicates(by: { value1, value2 in
// return value1 == value2
// })
// .tryRemoveDuplicates(by: )
// 传递值时 nil 替换为 指定的值,
// 数值改为可选项卡 PassthroughSubject<Int?, Error>(),
// let items: [Int?] = [1, nil, 3, 4, 4, 5, 4, 6, 7, 8, 9, 10]
// .replaceNil(with: 5)
// 为空值时 替换为指定的数据
// .replaceEmpty(with: 5)
// 搭配 try 开头的语句使用,如 tryMap 抛出异常为 throw URLError(.badServerResponse)
// 抛出的异常文字替换为指定的为 Default Value 的字符串值
// .replaceError(with: "Default Value")
// 扫描 现原有值,新值
// .scan(0, { existingValue, newValue in
// 0:设定原有值 + 1: 接收的新值 = 1:原有值
// 1:原有值 + 2: 接收的新值 = 3: 原有值
// 3: 原有值 + 3: 接收的新值 = 6:原有值
// return existingValue + newValue
// })
// 简写扫描操作
// .scan(0, { $0 + $1 })
// 更简洁的写法
// .scan(0, +)
// 抛出异常写法
// .tryScan(,)
// 减少运算
// .reduce(0, { existingValue, newValue in
// 集合中数据相加总和
// return existingValue + newValue
// })
// 简写方法
// .reduce(0, +)
// 收集数据
// 收集所有的发布,一次性返回数据集合,调用此方法,要在 .map 方法后
// //self?.data = returnedValue
// .collect()
// 三个一组收集发送
// 接收值 self?.data.append(contentsOf: returnedValue)
// .collect(3)
// 所有的值是否满足,满足条件为 true,否则为 false
// .allSatisfy({ $0 > 0 })
// 跟之前 try 功能相似
// .tryAllSatisfy()
*/
// Timing Operations: 计时运算
/*
// 反弹操作,用于输入入文本操作,等待设定的时间结束再返回
// 0.75 秒等待,如果期间返回两次,取最后返回的一次值
// .debounce(for: 0.75, scheduler: DispatchQueue.main)
// 延时操作,延时两秒后,在接收发送过来的值
// .delay(for: 2, scheduler: DispatchQueue.main)
// 测量间隔主要是测试用的,查看每次拿到数据的时间间隔
// .measureInterval(using: DispatchQueue.main)
// stride 间隔距离
// .map({ stride in
// return "\(stride.timeInterval)"
// })
// 点节流 for: 10: 可以每 10 秒打开和关闭它一次,然后 10 秒钟内不打开 ,latest: 是否输出最新值
// let items: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// for: 10, latest: true, 输出值为 1 , 10
// for: 10, latest: false, 输出值为 1 , 2
// .throttle(for: 30, scheduler: DispatchQueue.main, latest: true)
// 用在网络请求数据发生异常,指定重试次数,制定次数范围内,都是异常,才返回异常
// .retry(3)
// 超时操作,超过设定时长,则不返回
// .timeout(0.75, scheduler: DispatchQueue.main)
*/
// Multiple Publishers / Subscribers: 多个 发布 / 订阅
/*
// 组合
// 组合多个数据
// .combineLatest(dataService.boolPublisher, dataService.intPublisher)
// .compactMap({ (int, bool) in
// if bool {
// return String(int)
// }
// return nil
// })
// 简写
// .compactMap({ $1 ? String($0) : "n/a" })
// 删除重复项,必须是连续的,由于发布数据,两个都是独立的,为 true/false 时,两个都会更新订阅的数据,所以会显示两次
// .removeDuplicates()
// 完成三个发布者数据 compactMap: 对给定数组的每个元素,执行闭包中的映射,将非空的映射结果放置在数组中返回
// 三个数据都有的情况下,返回数据,否则返回,最小的数组
// .compactMap({ (int1, bool, int2) in
// if bool {
// return String(int1)
// }
// return "n/a"
// })
// 合并
// 数据类型相同的合并处理
// .merge(with: dataService.intPublisher)
// 压缩
// 将两个不同数据类型进行压缩
// .zip(dataService.boolPublisher, dataService.intPublisher)
// tuple: (Int, Bool) 组
// 三个数据都有的情况下,返回数据,否则根据最小的数组返回
// .map({ tuple in
// return String(tuple.0) + tuple.1.description + String(tuple.2)
// })
// 捕捉
// 如果映射数据发生异常,通过 .catch 函数 捕捉异常,返回对应的指定的数据,进行再次映射返回数据
// .tryMap({ int in
// 为 5 时返回捕捉异常数据,并结束,否则返回原数据
// if int == 5 {
// throw URLError(.badServerResponse)
// }
// return int
// })
// .catch({ error in
// return self.dataService.intPublisher
// })
// .map({ String($0) })
*/
let sharedPublisher = dataService.passThroughPublisher
// 删除前三项
// .dropFirst(3)
// 输出共享给多个订阅者
.share()
// 多播数据,更改了发布者的数据,自动连接数据,自动开始发布
// .multicast {
// PassthroughSubject<Int, Error>()
// }
// 传递新建的多播发布者
.multicast(subject: multiCastPublisher)
sharedPublisher
.map({ String($0) })
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
self.error = "ERROR: \(error)"
break
}
} receiveValue: {[weak self] returnedValue in
//self?.data.append(contentsOf: returnedValue)
//self?.data = returnedValue
self?.data.append(returnedValue)
}
.store(in: &cancellable)
sharedPublisher
.map({ $0 > 5 ? true : false })
.sink { completion in
switch completion {
case .finished:
break
case .failure(let error):
self.error = "ERROR: \(error)"
break
}
} receiveValue: {[weak self] returnedValue in
self?.dataBools.append(returnedValue)
}
.store(in: &cancellable)
/// 测试取消多播发布的数据
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
sharedPublisher
.connect()
// 取消数据里可能是多播发布的数据
.store(in: &self.cancellable)
}
}
}
/// 高级组合
struct AdvancedCombineBootcamp: View {
// View Model
@StateObject private var viewModel = AdvancedCombineBootcampViewModel()
var body: some View {
ScrollView {
HStack {
VStack {
ForEach(viewModel.data, id: \.self) {
Text($0)
.font(.largeTitle)
.fontWeight(.black)
}
if !viewModel.error.isEmpty{
Text(viewModel.error)
}
}
VStack {
ForEach(viewModel.dataBools, id: \.self) {
Text($0.description)
.font(.largeTitle)
.fontWeight(.black)
}
}
}
}
}
}
struct AdvancedCombineBootcamp_Previews: PreviewProvider {
static var previews: some View {
AdvancedCombineBootcamp()
}
}
2. Futures 用于转义闭包转换为未来发布者,作用于统一接口
2.1 创建实例 View,FuturesBootcamp.swift
import SwiftUI
import Combine
// download with Combine: 使用组合下载
// download with @escaping closure: 使用转义闭包
// convert @escaping closure to combine: 转换转义闭包到组合
class FuturesBootcampViewModel: ObservableObject{
// 发布者,标题文本
@Published var title: String = "Starting title"
// https://www.google.com.hk
let url = URL(string: "https://www.baidu.com")
var cancellable = Set<AnyCancellable>()
init() {
//download()
//download2()
download3()
}
// 下载数据1
func download() {
getCombinePublisher()
.sink { _ in
} receiveValue: { [weak self] returnedValue in
self?.title = returnedValue
}
.store(in: &cancellable)
}
// 下载数据2
func download2(){
getEscapingClosure { [weak self] returnedValue, error in
self?.title = returnedValue
}
}
// 下载数据3
func download3(){
getFuturePublisher()
.sink { _ in
} receiveValue: { [weak self] returnedValue in
self?.title = returnedValue
}
.store(in: &cancellable)
}
// 获取组合发布者
func getCombinePublisher() -> AnyPublisher<String, URLError>{
// 判断是否异常
guard let url = url else { return PassthroughSubject<String, URLError>().eraseToAnyPublisher() }
// 进行请求
return URLSession.shared.dataTaskPublisher(for: url)
.timeout(1, scheduler: DispatchQueue.main)
.map({ _ in
return "New value"
})
.eraseToAnyPublisher()
}
// 获取转义闭包
func getEscapingClosure(completionHandler: @escaping (_ value: String, _ error: Error?) -> ()){
guard let url = url else {
completionHandler("", nil)
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
completionHandler("New value 2", nil)
}
// 执行实际的数据任务
.resume()
}
// 转义闭包 转换为 未来发布者
func getFuturePublisher() -> Future<String, Error>{
Future { promise in
self.getEscapingClosure { returnedValue, error in
if let error = error {
promise(.failure(error))
}else{
promise(.success(returnedValue))
}
}
}
}
// 一些案例 1 简单操作
func doSomething(completion: @escaping (_ value: String) -> ()){
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
completion("NEW STRING")
}
}
// 2
func doSomethingInTheFuture() -> Future <String, Never>{
Future { promise in
self.doSomething { value in
promise(.success(value))
}
}
}
}
// 表示未来的值
struct FuturesBootcamp: View {
@StateObject private var viewModel = FuturesBootcampViewModel()
var body: some View {
Text(viewModel.title)
}
}
struct FuturesBootcamp_Previews: PreviewProvider {
static var previews: some View {
FuturesBootcamp()
}
}
文章来源地址https://www.toymoban.com/news/detail-722626.html
文章来源:https://www.toymoban.com/news/detail-722626.html
到了这里,关于AdvancedCombine/高级组合,Futures/转义闭包转换为未来发布者 的详细使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!