已解决
AdvancedCombine/高级组合,Futures/转义闭包转换为未来发布者 的详细使用
来自网友在路上 168868提问 提问时间:2023-10-20 07:07:35阅读次数: 68
最佳答案 问答题库688位专家为你答疑解惑
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, 18let 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 inswitch completion {case .finished:breakcase .failure(let error):self.error = "ERROR: \(error)"break}} receiveValue: {[weak self] returnedValue in//self?.data.append(contentsOf: returnedValue)//self?.data = returnedValueself?.data.append(returnedValue)}.store(in: &cancellable)sharedPublisher.map({ $0 > 5 ? true : false }).sink { completion inswitch completion {case .finished:breakcase .failure(let error):self.error = "ERROR: \(error)"break}} receiveValue: {[weak self] returnedValue inself?.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.hklet url = URL(string: "https://www.baidu.com")var cancellable = Set<AnyCancellable>()init() {//download()//download2()download3()}// 下载数据1func download() {getCombinePublisher().sink { _ in} receiveValue: { [weak self] returnedValue inself?.title = returnedValue}.store(in: &cancellable)}// 下载数据2func download2(){getEscapingClosure { [weak self] returnedValue, error inself?.title = returnedValue}}// 下载数据3func download3(){getFuturePublisher().sink { _ in} receiveValue: { [weak self] returnedValue inself?.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({ _ inreturn "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 incompletionHandler("New value 2", nil)}// 执行实际的数据任务.resume()}// 转义闭包 转换为 未来发布者func getFuturePublisher() -> Future<String, Error>{Future { promise inself.getEscapingClosure { returnedValue, error inif 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")}}// 2func doSomethingInTheFuture() -> Future <String, Never>{Future { promise inself.doSomething { value inpromise(.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()}
}
查看全文
99%的人还看了
相似问题
猜你感兴趣
版权申明
本文"AdvancedCombine/高级组合,Futures/转义闭包转换为未来发布者 的详细使用":http://eshow365.cn/6-20144-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!