Swift Combine — Notification、URLSession、Timer等Publisher的理解与使用

Notification Publisher

SwiftCombine框架中,可以使用NotificationCenter.Publisher来创建一个能够订阅和接收通知的Publisher

// 创建一个订阅通知的Publisher
let notificationPublisher = NotificationCenter.default.publisher(for: Notification.Name("CustomNotification"))

接下来,我们可以订阅这个Publisher,并处理接收到的通知。

// 订阅通知
let cancellable = notificationPublisher.sink { notification in// 处理接收到的通知print("Received notification: \(notification)")
}

发送通知

// 发送通知
NotificationCenter.default.post(name: Notification.Name("CustomNotification"), object: nil)

下面代码中就是一个完整的例子:

class NotificationViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()func setUpNotification() {let notificationPublisher = NotificationCenter.default.publisher(for: Notification.Name("CustomNotification"))notificationPublisher.sink { notification inprint("Received notification: \(notification)")}.store(in: &cancellable)}func sendNotification() {NotificationCenter.default.post(name: Notification.Name("CustomNotification"), object: nil)}
}struct NotificationDemo: View {@StateObject private var viewModel = NotificationViewModel()var body: some View {Button("Send Notification") {viewModel.sendNotification()}.buttonStyle(BorderedProminentButtonStyle()).onAppear {viewModel.setUpNotification()}}
}

上面代码中在ViewModel中定义并且订阅了Notification Publisher,在SwiftUI界面触发NotificationCenter发送通知,随后在sink方法中收到了该通知。
在这里插入图片描述
除了这种用法外,有的时候也可以直接在SwiftUI界面通过onReceive方式使用。
现在SwiftUI界面定义一个Notification

// app 进入前台前的通知
let willEnterForegroundPublisher = NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)

然后设置onReceive方法:

.onReceive(willEnterForegroundPublisher, perform: { notification inprint("Received App will enter foreground notification")
})

这样在App从后台回前台的时候就触发了这个通知,onReceive的闭包中的打印就会输出。

完整代码如下:

struct NotificationDemo1: View {// app回前台的通知let willEnterForegroundPublisher = NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)var body: some View {VStack {Text("Hello World")}.onReceive(willEnterForegroundPublisher, perform: { notification inprint("Received App will enter foreground notification")})}
}

如果想在这个界面添加多个通知,那是不是要加多个onReceive方法呢?也可以不是的,比如像这样:

.onReceive(Publishers.MergeMany(willEnterForegroundPublisher, didEnterBackgroundPublisher), perform: { notification inprint("Received App \(notification)")
})

可以通过Publishers.MergeMany方法将多个Publisher合并,然后在一个回调中处理收到通知事件。

struct NotificationDemo1: View {// app回前台的通知let willEnterForegroundPublisher = NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)// app进入后台通知let didEnterBackgroundPublisher = NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)var body: some View {VStack {Text("Hello World")}.onReceive(Publishers.MergeMany(willEnterForegroundPublisher, didEnterBackgroundPublisher), perform: { notification inprint("Received App \(notification)")})}
}

在这里插入图片描述

URLSession Publisher

SwiftCombine框架中,URLSession.DataTaskPublisher提供了一种方便的方式来执行网络请求并处理返回的数据。

首先创建一个Publisher

// 创建一个网络请求Publisher
let url = URL(string: "https://......")!
let request = URLRequest(url: url)
let dataTaskPublisher = URLSession.shared.dataTaskPublisher(for: request)

接下来,我们可以订阅这个Publisher,并处理接收到的数据和错误。

// 订阅网络请求
let cancellable = dataTaskPublisher.map(\.data) // 提取返回的数据.decode(type: MyResponse.self, decoder: JSONDecoder()) // 解码数据为自定义类型.receive(on: DispatchQueue.main) // 切换到主线程处理结果.sink(receiveCompletion: { completion inswitch completion {case .finished:print("Request completed successfully")case .failure(let error):print("Request failed with error: \(error)")}}, receiveValue: { response inprint("Received response: \(response)")})

dataTaskPublisher 发送一个新的事件值时,我们将其中的 Data 通过 map 的方式提取出来,并交给 decode 这个 Operator 进行处理。decode 要求上游 PublisherOutput 类型是 Data,它会使用参数中接受的 decoder (本例中是 MyResponse) 来对上游数据进行解析,生成对应类型的实例,并作为新的 Publisher 事件发布出去。然后切换到主线程处理结果,包括刷新UI等等。

把上面的代码优化一下,具体化一下,实现一个真实的网络请求示例:

import SwiftUI
import Combine
import Foundationstruct Photo: Identifiable, Decodable {let id: Intlet albumId: Intlet title: Stringlet url: Stringlet thumbnailUrl: String
}class URLSessionViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()@Published var photos: [Photo] = []@Published var isFetching: Bool = falsefunc fetchPhotoData() {guard let url = URL(string: "https://jsonplaceholder.typicode.com/photos") else {return}isFetching = truelet request = URLRequest(url: url)URLSession.shared.dataTaskPublisher(for: request).map(\.data).decode(type: [Photo].self, decoder: JSONDecoder()).receive(on: DispatchQueue.main).sink { completion inswitch completion {case .finished:print("Request completed successfully")case .failure(let error):print("Request failed with error: \(error)")}} receiveValue: { photos inprint("Received response: \(photos)")self.isFetching = falseself.photos = photos}.store(in: &cancellable)}
}struct URLSessionDemo: View {@StateObject private var viewModel = URLSessionViewModel()var body: some View {VStack {if viewModel.photos.isEmpty {if viewModel.isFetching {ProgressView()} else {Button("Fetch photos data") {viewModel.fetchPhotoData()}.buttonStyle(BorderedProminentButtonStyle())}} else {List(viewModel.photos) { photo inPhotoView(photo: photo)}.listStyle(PlainListStyle())}}}
}struct PhotoView: View {let photo: Photovar body: some View {HStack(spacing: 16) {AsyncImage(url: URL(string: photo.thumbnailUrl)) { image inimage.resizable().aspectRatio(contentMode: .fill)} placeholder: {Rectangle().fill(Color.gray.opacity(0.3))}.frame(width: 80, height: 80)VStack {Text(String(photo.id)).font(.title).frame(maxWidth: .infinity, alignment: .leading)Text(photo.title).font(.headline).frame(maxWidth: .infinity, alignment: .leading).multilineTextAlignment(.leading).lineLimit(2)}}}
}

上面代码中定义了一个Photo类型的数据,代码中采用了URLSession Publisher的方式请求数据,并在SwiftUI上显示,效果如下:
在这里插入图片描述

Timer Publisher

Timer 类型也提供了一个方法,来创建一个按照一定间隔发送事件的 Publisher。之前有一篇文章已经详细介绍过了,详见:SwiftUI中结合使用Timer和onReceive

写在最后

本文主要介绍了CombineNotificationURLSession Publisher的使用,尤其是配合SwiftUI界面的使用。不管是Notification还是URLSession都大大简化了代码,将代码流程集中化,实现了链式处理方式。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/31382.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenTenBase入门

什么是OpenTenBase OpenTenBase 是一个提供写可靠性&#xff0c;多主节点数据同步的关系数据库集群平台。你可以将 OpenTenBase 配置一台或者多台主机上&#xff0c; OpenTenBase 数据存储在多台物理主机上面。数据表的存储有两种方式&#xff0c; 分别是 distributed 或者 re…

让我来告诉初学者到底什么叫嵌入式系统?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;我们在刚刚开始学习电子学…

通过git命令查询某个用户提交信息

要查询某个用户通过 Git 提交了多少行代码&#xff0c;可以使用以下步骤和命令来实现。这些命令将统计该用户的添加和删除的代码行数。 1、切换到你的 Git 仓库&#xff1a; cd /path/to/your/repositorygit命令结果&#xff1a; 2、查询所有用户&#xff1a; git log --pr…

国外开源字典集(wordlists)

Assetnote Wordlists Wordlists that are up to date and effective against the most popular technologies on the internet.https://wordlists.assetnote.io/

opencv c++ 检测图像尺寸大小,标注轮廓

1. 项目背景 本项目旨在开发一个图像处理程序&#xff0c;通过使用计算机视觉技术&#xff0c;能够自动检测图像中物体的尺寸并进行分类。项目利用了开源的计算机视觉库 OpenCV&#xff0c;实现了图像的灰度处理、二值化、轮廓检测、边界框绘制以及尺寸分类等功能。通过这些功…

在智星云租用算力时,如何选择适合的GPU?

智星云平台分配GPU、CPU、内存的机制为&#xff1a;按租用的GPU数量成比例分配CPU和内存&#xff0c;算力市场显示的CPU和内存均为每GPU分配的CPU和内存&#xff0c;如果租用两块GPU&#xff0c;那么CPU和内存就x2。此外GPU非共享&#xff0c;每个实例对GPU是独占的。 一. CPU…

NSSCTF-Web题目12

目录 [SWPUCTF 2021 新生赛]finalrce 1、题目 2、知识点 3、思路 [UUCTF 2022 新生赛]ez_rce 1、题目 2、知识点 3、思路 [羊城杯 2020]easycon 1、题目 2、知识点 3、思路 [SWPUCTF 2021 新生赛]finalrce 1、题目 2、知识点 命令执行&#xff0c;tee命令 3、思路…

深度学习算法informer(时序预测)(三)(Encoder)

一、EncoderLayer架构如图&#xff08;不改变输入形状&#xff09; 二、ConvLayer架构如图&#xff08;输入形状中特征维度减半&#xff09; 三、Encoder整体 包括三部分 1. 多层EncoderLayer 2. 多层ConvLayer 3. 层归一化 代码如下 class AttentionLayer(nn.Module):de…

淘宝扭蛋机小程序:互联网时代下行业的发展动力

近几年&#xff0c;扭蛋机在潮玩市场风靡&#xff0c;与各类IP合作&#xff0c;推出各种新颖有趣的扭蛋商品&#xff0c;吸引了众多的IP粉丝&#xff0c;他们会通过扭蛋机进行抽奖&#xff0c;获得喜欢的商品。 目前&#xff0c;移动应用程序不断升级优化&#xff0c;“互联网…

idea中的git在clone文件提示 filename too long

一 解决版本 1.1 问题描述以及解决办法 当在Windows系统下使用Git时出现“filename too long”错误&#xff1a; git config --system core.longpaths true

思科ospf+rip重发布配置命令

——————————————————————————————————————————— 基础配置 R1 Router>en #进入配置模式 Router#conf #进入配置模式 Router(config)#h…

如何在 MySQL 中创建和使用事务?

目录 1. 环境准备 2. 创建事务 3. 事务执行 4. 事务撤消 5. 总结 事务是数据库区别于文件系统的重要特征之一&#xff0c;当我们有了事务就会让数据库始终保持一致&#xff0c;同时我们还能通过事务机制恢复到某个时间点&#xff0c;这样可以保证已提交到数据库的修改不会…

人工智能在肿瘤检测以及癌症早筛中的最新研究|顶刊速递·24-06-21

小罗碎碎念 推文主题&#xff1a;人工智能在癌症检测以及早筛中的最新研究进展 之前有一篇推文介绍了哈佛发表的3D病理&#xff0c;当时应该有不少老师/同学对于数据的获取是有些懵的&#xff0c;那么今天你在第一篇文章中或许能找到答案。 一直看我推送的&#xff0c;并且不跳…

骁龙相机启动流程分析

一、骁龙相机启动流程分析 1. 相机启动阶段关键TAG 关键字解释deliverInputEvent点击事件bindApplicationApp 冷启动 创建applicationactivityStart创建camera activityactivityResumecamera UI界面开始显示connectDevicecameraFWK 开始链接并open sensorCameraHal::openSessio…

MySQL系列-安装配置使用说明(MAC版本)

1、前言 本文将介绍MySQL的安装配置以及基本语法操作说明 环境&#xff1a;mac 版本&#xff1a;MySQL 8.0.28 之前电脑安装卸载过&#xff0c;后面在装的时候遇到一些问题&#xff0c;用了四五天才解决&#xff0c;主要是参考 https://blog.csdn.net/zz00008888/article/deta…

大厂晋升学习方法一:海绵学习法

早晨 30 分钟 首先&#xff0c;我们可以把起床的闹钟提前 30 分钟&#xff0c;比如原来 07:30 的闹钟可以改为 07:00。不用担心提前 30 分钟起床会影响休息质量&#xff0c;习惯以后&#xff0c;早起 30 分钟不但不会影响一天的精力&#xff0c;甚至可能反而让人更有精神。早起…

SAP ScreenPersonas

https://developers.sap.com/mission.screen-personas.html 跟着这个练习做一遍就了解了Personas 访问SAP提供的Personas练习系统 申请用户 登录练习系统 随便找一个可以支持Personas的程序搞起来&#xff0c;比如IW51 执行后等它出现这个图标就可以开始了.

js中的window和Window

示例&#xff1a; window.name name; console.log(window.name) // name console.log(Window.name) // Window由此可见Window和window是有区别的。 console.log(Object.prototype.toString.call(Window)); // [object Function] console.log(Object.prototype.toString.c…

中服云产品远程运维系统

中服云产品远程运维系统主要针对设备售后市场服务的管理&#xff0c;利用工业物联网技术&#xff0c;一方面面向设备生产厂商&#xff0c;将分散的经销商、客户、销售出去的设备统一管理&#xff1b;另一方面面向设备使用厂家&#xff0c;实现设备实时运行监控&#xff1b;系统…

融资融券有哪些优势和风险,融资融券利息怎么算,利率最低是?4.0

融资融券的优势 1. 提高资金利用率&#xff1a;获得额外的资金或股票交易&#xff0c;提高资金利用率&#xff0c;扩大投资规模。 2. 降低投资风险&#xff1a;通过融资融券买入多只股票分散风险&#xff0c;降低单一股票持仓风险。 3. 增加投资收益&#xff1a;提供更多的交…