• AdvancedCombine/高级组合,Futures/转义闭包转换为未来发布者 的详细使用


    1. 创建详细使用的高级组合 View AdvancedCombineBootcamp.swift

    1. import SwiftUI
    2. import Combine
    3. /// 数据服务
    4. class AdvancedCombineDataService{
    5. // @Published var basicPublisher: String = "first publish"
    6. // CurrentValueSubject 通用函数
    7. // let currentValuePublisher = CurrentValueSubject("first publish")
    8. // 发布者数据
    9. let passThroughPublisher = PassthroughSubject<Int, Error>()
    10. // 发布者数据
    11. let boolPublisher = PassthroughSubject<Bool, Error>()
    12. // 发布者数据
    13. let intPublisher = PassthroughSubject<Int, Error>()
    14. init(){
    15. publishFakeData()
    16. //publishFakeData2()
    17. }
    18. //发布模拟数据
    19. private func publishFakeData(){
    20. // Array(1 ..< 11)
    21. // 重复数据用来过滤重复数据使用,并且是连续的才有效果
    22. // let items: [Int] = [1, 2, 3, 4, 4, 5, 5, 4, 6, 7, 8, 9, 10]
    23. //, 11, 12, 13, 14, 15, 16, 17, 18
    24. let items: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    25. for x in items.indices {
    26. DispatchQueue.main.asyncAfter(deadline: .now() + Double(x)) {
    27. self.passThroughPublisher.send(items[x])
    28. if x > 4 && x < 8 {
    29. self.boolPublisher.send(true)
    30. self.intPublisher.send(999)
    31. }else {
    32. self.boolPublisher.send(false)
    33. }
    34. if x == items.indices.last{
    35. //获取最后一个数据,计算最大值/最小身上 必须打开完成关闭操作,需要知道数据的区间范围
    36. self.passThroughPublisher.send(completion: .finished)
    37. //self.boolPublisher.send(completion: .finished)
    38. }
    39. }
    40. }
    41. }
    42. // 发布模拟数据2,配合 .debounce 使用
    43. private func publishFakeData2(){
    44. DispatchQueue.main.asyncAfter(deadline: .now() + 0) {
    45. self.passThroughPublisher.send(1)
    46. }
    47. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    48. self.passThroughPublisher.send(2)
    49. }
    50. DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
    51. self.passThroughPublisher.send(3)
    52. }
    53. }
    54. }
    55. /// ViewModel
    56. class AdvancedCombineBootcampViewModel: ObservableObject{
    57. @Published var data: [String] = []
    58. @Published var dataBools: [Bool] = []
    59. @Published var error: String = ""
    60. let dataService = AdvancedCombineDataService()
    61. var cancellable = Set<AnyCancellable>()
    62. // 多播数据
    63. let multiCastPublisher = PassthroughSubject<Int, Error>()
    64. init() {
    65. addSubscribers()
    66. }
    67. // 添加订阅
    68. private func addSubscribers(){
    69. //.$basicPublisher currentValuePublisher
    70. // dataService.passThroughPublisher
    71. // Sequence Operations: 序列运算
    72. /*
    73. // 第一个数据操作
    74. // .first()
    75. // .first(where: {$0 > 4})
    76. // .tryFirst(where: { value in
    77. // if value == 3 {
    78. // throw URLError(.badServerResponse)
    79. // }
    80. // return value > 1
    81. // })
    82. // 最后一个数据操作
    83. // last: 获取最后一个值,监听值需要加 .send(completion: .finished),表示执行到最后
    84. // .last()
    85. // .last(where: { $0 < 4})
    86. // .tryLast(where: { value in
    87. // if value == 13{
    88. // throw URLError(.badServerResponse)
    89. // }
    90. // print("\(value)")
    91. // value < 4 = 3
    92. // value > 4 = 10
    93. // return value > 4
    94. // })
    95. // 删除操作
    96. // 删除第一个值
    97. // .dropFirst()
    98. // 删除前三个
    99. // .dropFirst(3)
    100. // drop 保留小于号,不支持大于号,因为大于号开始就删除,返回 false,表示执行闭包结束,删除成功,没有实际的意义
    101. // .drop(while: { $0 < 5 })
    102. // .tryDrop(while: { value in
    103. // if value == 5{
    104. // throw URLError(.badServerResponse)
    105. // }
    106. // return value < 6
    107. // })
    108. // 返回前几个操作
    109. // prefix(4) 取前 4 个
    110. // $0 > 5 闭包立即返回 false,前缀实际上完成了,所以不返回
    111. // $0 < 5 返回数组中的前 五 的元素,第一个条件必须为真,否则不返回
    112. // .prefix(while: { $0 < 5 })
    113. // .tryPrefix(while: )
    114. // 根据下标索引,输出数值
    115. // .output(at: 3)
    116. // 根据 输出范围
    117. // .output(in: 2 ..< 4)
    118. */
    119. // Mathematic Operations: 数学运算
    120. /*
    121. // 获取最大值
    122. // .max()
    123. // 两者相比较,取最大值: 10
    124. // .max(by: { value1, value2 in
    125. // return value1 < value2
    126. // })
    127. // .tryMax(by: )
    128. // 获取最小值
    129. // .min()
    130. // 两者相比较取最小值
    131. // .tryMin(by: { value1, value2 in
    132. // return value1 < value2
    133. // })
    134. */
    135. // Filter / Reducing Operations: 过滤 / 减少运算
    136. /*
    137. // 遍历数据
    138. // .map({ String($0) })
    139. // 遍历到 5 抛出异常,并结束
    140. // .tryMap({ value in
    141. // if value == 5 {
    142. // throw URLError(.badServerResponse)
    143. // }
    144. // return String(value)
    145. // })
    146. // 遍历去除值为 5 的数据
    147. // .compactMap({ value in
    148. // if value == 5 {
    149. // return nil
    150. // }
    151. // return String(value)
    152. // })
    153. // 遍历到 5 抛出异常,并结束
    154. // .tryCompactMap({ value in
    155. // if value == 5 {
    156. // throw URLError(.badServerResponse)
    157. // }
    158. // return String(value)
    159. // })
    160. // 过滤器 输出大于3 小于7 的值
    161. // .filter({ ($0 > 3) && ($0 < 7)})
    162. // 输出到 3 直接抛出异常,并结束
    163. // .tryFilter({ value in
    164. // if value > 3 && value < 7{
    165. // throw URLError(.badServerResponse)
    166. // }
    167. // return true
    168. // })
    169. // 删除重复项
    170. // 删除数据中相同的数据,必须是相邻的,否则不起作用
    171. // .removeDuplicates()
    172. // 删除重复项 于 removeDuplicates 删除重复项的情况完全相同
    173. // .removeDuplicates(by: { value1, value2 in
    174. // return value1 == value2
    175. // })
    176. // .tryRemoveDuplicates(by: )
    177. // 传递值时 nil 替换为 指定的值,
    178. // 数值改为可选项卡 PassthroughSubject(),
    179. // let items: [Int?] = [1, nil, 3, 4, 4, 5, 4, 6, 7, 8, 9, 10]
    180. // .replaceNil(with: 5)
    181. // 为空值时 替换为指定的数据
    182. // .replaceEmpty(with: 5)
    183. // 搭配 try 开头的语句使用,如 tryMap 抛出异常为 throw URLError(.badServerResponse)
    184. // 抛出的异常文字替换为指定的为 Default Value 的字符串值
    185. // .replaceError(with: "Default Value")
    186. // 扫描 现原有值,新值
    187. // .scan(0, { existingValue, newValue in
    188. // 0:设定原有值 + 1: 接收的新值 = 1:原有值
    189. // 1:原有值 + 2: 接收的新值 = 3: 原有值
    190. // 3: 原有值 + 3: 接收的新值 = 6:原有值
    191. // return existingValue + newValue
    192. // })
    193. // 简写扫描操作
    194. // .scan(0, { $0 + $1 })
    195. // 更简洁的写法
    196. // .scan(0, +)
    197. // 抛出异常写法
    198. // .tryScan(,)
    199. // 减少运算
    200. // .reduce(0, { existingValue, newValue in
    201. // 集合中数据相加总和
    202. // return existingValue + newValue
    203. // })
    204. // 简写方法
    205. // .reduce(0, +)
    206. // 收集数据
    207. // 收集所有的发布,一次性返回数据集合,调用此方法,要在 .map 方法后
    208. // //self?.data = returnedValue
    209. // .collect()
    210. // 三个一组收集发送
    211. // 接收值 self?.data.append(contentsOf: returnedValue)
    212. // .collect(3)
    213. // 所有的值是否满足,满足条件为 true,否则为 false
    214. // .allSatisfy({ $0 > 0 })
    215. // 跟之前 try 功能相似
    216. // .tryAllSatisfy()
    217. */
    218. // Timing Operations: 计时运算
    219. /*
    220. // 反弹操作,用于输入入文本操作,等待设定的时间结束再返回
    221. // 0.75 秒等待,如果期间返回两次,取最后返回的一次值
    222. // .debounce(for: 0.75, scheduler: DispatchQueue.main)
    223. // 延时操作,延时两秒后,在接收发送过来的值
    224. // .delay(for: 2, scheduler: DispatchQueue.main)
    225. // 测量间隔主要是测试用的,查看每次拿到数据的时间间隔
    226. // .measureInterval(using: DispatchQueue.main)
    227. // stride 间隔距离
    228. // .map({ stride in
    229. // return "\(stride.timeInterval)"
    230. // })
    231. // 点节流 for: 10: 可以每 10 秒打开和关闭它一次,然后 10 秒钟内不打开 ,latest: 是否输出最新值
    232. // let items: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    233. // for: 10, latest: true, 输出值为 1 , 10
    234. // for: 10, latest: false, 输出值为 1 , 2
    235. // .throttle(for: 30, scheduler: DispatchQueue.main, latest: true)
    236. // 用在网络请求数据发生异常,指定重试次数,制定次数范围内,都是异常,才返回异常
    237. // .retry(3)
    238. // 超时操作,超过设定时长,则不返回
    239. // .timeout(0.75, scheduler: DispatchQueue.main)
    240. */
    241. // Multiple Publishers / Subscribers: 多个 发布 / 订阅
    242. /*
    243. // 组合
    244. // 组合多个数据
    245. // .combineLatest(dataService.boolPublisher, dataService.intPublisher)
    246. // .compactMap({ (int, bool) in
    247. // if bool {
    248. // return String(int)
    249. // }
    250. // return nil
    251. // })
    252. // 简写
    253. // .compactMap({ $1 ? String($0) : "n/a" })
    254. // 删除重复项,必须是连续的,由于发布数据,两个都是独立的,为 true/false 时,两个都会更新订阅的数据,所以会显示两次
    255. // .removeDuplicates()
    256. // 完成三个发布者数据 compactMap: 对给定数组的每个元素,执行闭包中的映射,将非空的映射结果放置在数组中返回
    257. // 三个数据都有的情况下,返回数据,否则返回,最小的数组
    258. // .compactMap({ (int1, bool, int2) in
    259. // if bool {
    260. // return String(int1)
    261. // }
    262. // return "n/a"
    263. // })
    264. // 合并
    265. // 数据类型相同的合并处理
    266. // .merge(with: dataService.intPublisher)
    267. // 压缩
    268. // 将两个不同数据类型进行压缩
    269. // .zip(dataService.boolPublisher, dataService.intPublisher)
    270. // tuple: (Int, Bool) 组
    271. // 三个数据都有的情况下,返回数据,否则根据最小的数组返回
    272. // .map({ tuple in
    273. // return String(tuple.0) + tuple.1.description + String(tuple.2)
    274. // })
    275. // 捕捉
    276. // 如果映射数据发生异常,通过 .catch 函数 捕捉异常,返回对应的指定的数据,进行再次映射返回数据
    277. // .tryMap({ int in
    278. // 为 5 时返回捕捉异常数据,并结束,否则返回原数据
    279. // if int == 5 {
    280. // throw URLError(.badServerResponse)
    281. // }
    282. // return int
    283. // })
    284. // .catch({ error in
    285. // return self.dataService.intPublisher
    286. // })
    287. // .map({ String($0) })
    288. */
    289. let sharedPublisher = dataService.passThroughPublisher
    290. // 删除前三项
    291. // .dropFirst(3)
    292. // 输出共享给多个订阅者
    293. .share()
    294. // 多播数据,更改了发布者的数据,自动连接数据,自动开始发布
    295. // .multicast {
    296. // PassthroughSubject()
    297. // }
    298. // 传递新建的多播发布者
    299. .multicast(subject: multiCastPublisher)
    300. sharedPublisher
    301. .map({ String($0) })
    302. .sink { completion in
    303. switch completion {
    304. case .finished:
    305. break
    306. case .failure(let error):
    307. self.error = "ERROR: \(error)"
    308. break
    309. }
    310. } receiveValue: {[weak self] returnedValue in
    311. //self?.data.append(contentsOf: returnedValue)
    312. //self?.data = returnedValue
    313. self?.data.append(returnedValue)
    314. }
    315. .store(in: &cancellable)
    316. sharedPublisher
    317. .map({ $0 > 5 ? true : false })
    318. .sink { completion in
    319. switch completion {
    320. case .finished:
    321. break
    322. case .failure(let error):
    323. self.error = "ERROR: \(error)"
    324. break
    325. }
    326. } receiveValue: {[weak self] returnedValue in
    327. self?.dataBools.append(returnedValue)
    328. }
    329. .store(in: &cancellable)
    330. /// 测试取消多播发布的数据
    331. DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    332. sharedPublisher
    333. .connect()
    334. // 取消数据里可能是多播发布的数据
    335. .store(in: &self.cancellable)
    336. }
    337. }
    338. }
    339. /// 高级组合
    340. struct AdvancedCombineBootcamp: View {
    341. // View Model
    342. @StateObject private var viewModel = AdvancedCombineBootcampViewModel()
    343. var body: some View {
    344. ScrollView {
    345. HStack {
    346. VStack {
    347. ForEach(viewModel.data, id: \.self) {
    348. Text($0)
    349. .font(.largeTitle)
    350. .fontWeight(.black)
    351. }
    352. if !viewModel.error.isEmpty{
    353. Text(viewModel.error)
    354. }
    355. }
    356. VStack {
    357. ForEach(viewModel.dataBools, id: \.self) {
    358. Text($0.description)
    359. .font(.largeTitle)
    360. .fontWeight(.black)
    361. }
    362. }
    363. }
    364. }
    365. }
    366. }
    367. struct AdvancedCombineBootcamp_Previews: PreviewProvider {
    368. static var previews: some View {
    369. AdvancedCombineBootcamp()
    370. }
    371. }

    2. Futures 用于转义闭包转换为未来发布者,作用于统一接口

      2.1 创建实例 View,FuturesBootcamp.swift
    1. import SwiftUI
    2. import Combine
    3. // download with Combine: 使用组合下载
    4. // download with @escaping closure: 使用转义闭包
    5. // convert @escaping closure to combine: 转换转义闭包到组合
    6. class FuturesBootcampViewModel: ObservableObject{
    7. // 发布者,标题文本
    8. @Published var title: String = "Starting title"
    9. // https://www.google.com.hk
    10. let url = URL(string: "https://www.baidu.com")
    11. var cancellable = Set<AnyCancellable>()
    12. init() {
    13. //download()
    14. //download2()
    15. download3()
    16. }
    17. // 下载数据1
    18. func download() {
    19. getCombinePublisher()
    20. .sink { _ in
    21. } receiveValue: { [weak self] returnedValue in
    22. self?.title = returnedValue
    23. }
    24. .store(in: &cancellable)
    25. }
    26. // 下载数据2
    27. func download2(){
    28. getEscapingClosure { [weak self] returnedValue, error in
    29. self?.title = returnedValue
    30. }
    31. }
    32. // 下载数据3
    33. func download3(){
    34. getFuturePublisher()
    35. .sink { _ in
    36. } receiveValue: { [weak self] returnedValue in
    37. self?.title = returnedValue
    38. }
    39. .store(in: &cancellable)
    40. }
    41. // 获取组合发布者
    42. func getCombinePublisher() -> AnyPublisher<String, URLError>{
    43. // 判断是否异常
    44. guard let url = url else { return PassthroughSubject<String, URLError>().eraseToAnyPublisher() }
    45. // 进行请求
    46. return URLSession.shared.dataTaskPublisher(for: url)
    47. .timeout(1, scheduler: DispatchQueue.main)
    48. .map({ _ in
    49. return "New value"
    50. })
    51. .eraseToAnyPublisher()
    52. }
    53. // 获取转义闭包
    54. func getEscapingClosure(completionHandler: @escaping (_ value: String, _ error: Error?) -> ()){
    55. guard let url = url else {
    56. completionHandler("", nil)
    57. return
    58. }
    59. URLSession.shared.dataTask(with: url) { data, response, error in
    60. completionHandler("New value 2", nil)
    61. }
    62. // 执行实际的数据任务
    63. .resume()
    64. }
    65. // 转义闭包 转换为 未来发布者
    66. func getFuturePublisher() -> Future<String, Error>{
    67. Future { promise in
    68. self.getEscapingClosure { returnedValue, error in
    69. if let error = error {
    70. promise(.failure(error))
    71. }else{
    72. promise(.success(returnedValue))
    73. }
    74. }
    75. }
    76. }
    77. // 一些案例 1 简单操作
    78. func doSomething(completion: @escaping (_ value: String) -> ()){
    79. DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
    80. completion("NEW STRING")
    81. }
    82. }
    83. // 2
    84. func doSomethingInTheFuture() -> Future <String, Never>{
    85. Future { promise in
    86. self.doSomething { value in
    87. promise(.success(value))
    88. }
    89. }
    90. }
    91. }
    92. // 表示未来的值
    93. struct FuturesBootcamp: View {
    94. @StateObject private var viewModel = FuturesBootcampViewModel()
    95. var body: some View {
    96. Text(viewModel.title)
    97. }
    98. }
    99. struct FuturesBootcamp_Previews: PreviewProvider {
    100. static var previews: some View {
    101. FuturesBootcamp()
    102. }
    103. }
  • 相关阅读:
    从零开始:新手快速在国产操作系统中搭建高可用K8S(V1.28)集群落地实践
    iOS开发Swift-10-位置授权, cocoapods,API,天气获取,城市获取-和风天气App首页代码
    【CV】可变形卷积:用于目标检测和语义分割的卷积层
    PageRank实战---西游记人物节点重要度
    Python实验三
    (2596. 检查骑士巡视方案leetcode,经典深搜)-------------------Java实现
    腾讯待办关停,导出的数据怎么恢复到手机上面?
    HTML期末学生大作业:中华传统文化【苏绣手工艺】带psd设计图(15页)
    《活着》思维导图
    2022-11-10 工作记录--HTML-video视频播放
  • 原文地址:https://blog.csdn.net/u011193452/article/details/133905240