Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十

Combine 系列

  1. Swift Combine 从入门到精通一
  2. Swift Combine 发布者订阅者操作者 从入门到精通二
  3. Swift Combine 管道 从入门到精通三
  4. Swift Combine 发布者publisher的生命周期 从入门到精通四
  5. Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
  6. Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
  7. Swift 使用 Combine 进行开发 从入门到精通七
  8. Swift 使用 Combine 管道和线程进行开发 从入门到精通八
  9. Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
    在这里插入图片描述

1. 使用 dataTaskPublisher 发起网络请求

  • 目的: 一个常见的用例是从 URL 请求 JSON 数据并解码。

这可以通过使用 Combine 的 URLSession.dataTaskPublisher 搭配一系列处理数据的操作符来轻松完成。

最简单的,调用 URLSession 的 dataTaskPublisher,然后在数据到达订阅者之前使用 map 和 decode。

使用此操作的最简单例子可能是:

let myURL = URL(string: "https://postman-echo.com/time/valid?timestamp=2016-10-10")
// checks the validity of a timestamp - this one returns {"valid":true}
// matching the data structure returned from https://postman-echo.com/time/valid
fileprivate struct PostmanEchoTimeStampCheckResponse: Decodable, Hashable { // 1let valid: Bool
}let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!) // 2// the dataTaskPublisher output combination is (data: Data, response: URLResponse).map { $0.data } // 3.decode(type: PostmanEchoTimeStampCheckResponse.self, decoder: JSONDecoder()) // 4let cancellableSink = remoteDataPublisher.sink(receiveCompletion: { completion inprint(".sink() received the completion", String(describing: completion))switch completion {case .finished: // 5breakcase .failure(let anError):  // 6print("received error: ", anError)}}, receiveValue: { someValue in // 7print(".sink() received \(someValue)")})
  1. 通常,你将有一个结构体的定义,至少遵循 Decodable 协议(即使没有完全遵循 Codable protocol)。此结构体可以只定义从网络拉取到的 JSON 中你感兴趣的字段。 不需要定义完整的 JSON 结构。
  2. dataTaskPublisher 是从 URLSession 实例化的。 你可以配置你自己的 URLSession,或者使用 shared session.
  3. 返回的数据是一个元组:(data: Data, response: URLResponse)。 map 操作符用来获取数据并丢弃 URLResponse,只把 Data 沿管道向下传递。
  4. decode 用于加载数据并尝试解析它。 如果解码失败,它会抛出一个错误。 如果它成功,通过管道传递的对象将是来自 JSON 数据的结构体。
  5. 如果解码完成且没有错误,则将触发完成操作,并将值传递给 receiveValue 闭包。
  6. 如果发生失败(无论是网络请求还是解码),则错误将被传递到 failure 闭包。
  7. 只有当数据请求并解码成功时,才会调用此闭包,并且收到的数据格式将是结构体 PostmanEchoTimeStampCheckResponse 的实例。

2. 使用 dataTaskPublisher 进行更严格的请求处理

  • 目的: 当 URLSesion 进行连接时,它仅在远程服务器未响应时报告错误。 你可能需要根据状态码将各种响应视为不同的错误。 为此,你可以使用 tryMap 检查 http 响应并在管道中抛出错误。

要对 URL 响应中被认为是失败的操作进行更多控制,可以对 dataTaskPublisher 的元组响应使用 tryMap 操作符。 由于 dataTaskPublisher 将响应数据和 URLResponse 都返回到了管道中,你可以立即检查响应,并在需要时抛出自己的错误。

这方面的一个例子可能看起来像:

let myURL = URL(string: "https://postman-echo.com/time/valid?timestamp=2016-10-10")
// checks the validity of a timestamp - this one returns {"valid":true}
// matching the data structure returned from https://postman-echo.com/time/valid
fileprivate struct PostmanEchoTimeStampCheckResponse: Decodable, Hashable {let valid: Bool
}
enum TestFailureCondition: Error {case invalidServerResponse
}let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!).tryMap { data, response -> Data in  // 1guard let httpResponse = response as? HTTPURLResponse,  // 2 httpResponse.statusCode == 200 else {  // 3throw TestFailureCondition.invalidServerResponse  // 4}return data  // 5}.decode(type: PostmanEchoTimeStampCheckResponse.self, decoder: JSONDecoder())let cancellableSink = remoteDataPublisher.sink(receiveCompletion: { completion inprint(".sink() received the completion", String(describing: completion))switch completion {case .finished:breakcase .failure(let anError):print("received error: ", anError)}}, receiveValue: { someValue inprint(".sink() received \(someValue)")})

在 上个模式 中使用了 map 操作符, 这里我们使用 tryMap,这使我们能够根据返回的内容识别并在管道中抛出错误。

  1. tryMap 仍旧获得元组 (data: Data, response: URLResponse),并且在这里定义仅返回管道中的 Data 类型。
  2. tryMap 的闭包内,我们将响应转换为 HTTPURLResponse 并深入进去,包括查看特定的状态码。
  3. 在这个例子中,我们希望将 200 状态码以外的任何响应视为失败。HTTPURLResponse.statusCode 是一种 Int 类型,因此你也可以使用 httpResponse.statusCode > 300 等逻辑。
  4. 如果判断条件未满足,则会抛出我们选择的错误实例:在这个例子中,是 invalidServerResponse
  5. 如果没有出现错误,则我们只需传递 Data 以进行进一步处理。

3. 标准化 dataTaskPublisher 返回的错误

当在管道上触发错误时,不管错误发生在管道中的什么位置,都会发送 .failure 完成回调,并把错误封装在其中。

此模式可以扩展来返回一个发布者,该发布者使用此通用模式可接受并处理任意数量的特定错误。 在许多示例中,我们用默认值替换错误条件。 如果我们想要返回一个发布者的函数,该发布者不会根据失败来选择将发生什么,则同样 tryMap 操作符可以与 mapError 一起使用来转换响应对象以及转换 URLError 错误类型。

enum APIError: Error, LocalizedError {  // 1case unknown, apiError(reason: String), parserError(reason: String), networkError(from: URLError)var errorDescription: String? {switch self {case .unknown:return "Unknown error"case .apiError(let reason), .parserError(let reason):return reasoncase .networkError(let from):  // 2return from.localizedDescription}}
}func fetch(url: URL) -> AnyPublisher<Data, APIError> {let request = URLRequest(url: url)return URLSession.DataTaskPublisher(request: request, session: .shared)  // 3.tryMap { data, response in  // 4guard let httpResponse = response as? HTTPURLResponse else {throw APIError.unknown}if (httpResponse.statusCode == 401) {throw APIError.apiError(reason: "Unauthorized");}if (httpResponse.statusCode == 403) {throw APIError.apiError(reason: "Resource forbidden");}if (httpResponse.statusCode == 404) {throw APIError.apiError(reason: "Resource not found");}if (405..<500 ~= httpResponse.statusCode) {throw APIError.apiError(reason: "client error");}if (500..<600 ~= httpResponse.statusCode) {throw APIError.apiError(reason: "server error");}return data}.mapError { error in  // 5// if it's our kind of error already, we can return it directlyif let error = error as? APIError {return error}// if it is a TestExampleError, convert it into our new error typeif error is TestExampleError {return APIError.parserError(reason: "Our example error")}// if it is a URLError, we can convert it into our more general error kindif let urlerror = error as? URLError {return APIError.networkError(from: urlerror)}// if all else fails, return the unknown error conditionreturn APIError.unknown}.eraseToAnyPublisher()  // 6
}
  1. APIError 是一个错误类型的枚举,我们在此示例中使用该枚举来列举可能发生的所有错误。
  2. .networkError 是 APIError 的一个特定情况,当 URLSession.dataTaskPublisher 返回错误时我们将把错误转换为该类型。
  3. 我们使用标准 dataTaskPublisher 开始生成此发布者。
  4. 然后,我们将路由到 tryMap 操作符来检查响应,根据服务器响应创建特定的错误。
  5. 最后,我们使用 mapError 将任何其他不可忽视的错误类型转换为通用的错误类型 APIError

参考

https://heckj.github.io/swiftui-notes/index_zh-CN.html

代码

https://github.com/heckj/swiftui-notes

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

旅游|基于Springboot的旅游管理系统设计与实现(源码+数据库+文档)

旅游管理系统目录 目录 基于Springboot的旅游管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户管理 2、景点分类管理 3、景点信息管理 4、酒店信息管理 5、景点信息 6、游记分享管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xf…

【从Python基础到深度学习】4. Linux 常用命令

1.配置root用户密码 root用户为系统默认最高权限用户&#xff0c;其他用户密码修改命令与root用户修改密码命令相同 sudo passwd root 2.添加用户&#xff08;henry&#xff09; sudo useradd -m henry -s /bin/bash 3.配置henry用户密码 Xshell下连接新用户&#xff08;hen…

小巨人大爆发:紧凑型大型语言模型效率之谜揭晓!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

图像处理常用算法—6个算子 !!

目录 前言 1、Sobel 算子 2、Isotropic Sobel 算子 3、Roberts 算子 4、Prewitt 算子 5、Laplacian算子 6、Canny算子 前言 同图像灰度不同&#xff0c;边界处一般会有明显的边缘&#xff0c;利用此特征可以分割图像。 需要说明的是&#xff1a;边缘和物体间的边界并不…

Django问题报错:TypeError: as_view() takes 1 positional argument but 2 were given

一、错误位置 from django.urls import pathfrom users_app.views import RegisterView, LoginView, LogoutViewapp_name users urlpatterns [path("register/", RegisterView.as_view, name"register"),path("login/", LoginView.as_view, n…

机器学习---学习与推断,近似推断、话题模型

1. 学习与推断 基于概率图模型定义的分布&#xff0c;能对目标变量的边际分布&#xff08;marginal distribution&#xff09;或某些可观测变量 为条件的条件分布进行推断。对概率图模型&#xff0c;还需确定具体分布的参数&#xff0c;称为参数估计或学习问 题&#xff0c;…

读千脑智能笔记08_人工智能的未来(下)

1. 机器智能存在的风险 1.1. “人工智能”这个名字应用到几乎所有涉及机器学习的领域 1.2. 技术专家对人工智能的态度也从“人工智能可能永远不会实现”快速转变为“人工智能可能在不久的将来毁灭所有人类” 1.3. 每一项新技术都可能会被滥用…

专业课135+总分400+西安交通大学815/909信号与系统考研电子信息与通信工程,真题,大纲,参考书。

经过将近一年的考研复习&#xff0c;终于梦圆西安交大&#xff0c;今年专业可815(和909差不多)信号与系统135&#xff0c;总分400&#xff0c;回想这一年的复习还是有很多经验和大家分享&#xff0c;希望可以对大家复习有所帮助&#xff0c;少走弯路。 专业课&#xff1a; 这…

18:蜂鸣器

蜂鸣器 1、蜂鸣器的介绍2、编程让蜂鸣器响起来3、通过定时控制蜂鸣器4、蜂鸣器发出滴滴声&#xff08;间歇性鸣叫&#xff09; 1、蜂鸣器的介绍 蜂鸣器内部其实是2个金属片&#xff0c;当一个金属片接正电&#xff0c;一个金属片接负电时&#xff0c;2个金属片将合拢&#xff…

大数据应用对企业的价值

目录 一、大数据应用价值 1.1 大数据技术分析 1.2 原有技术场景的优化 1.2.1 数据分析优化 1.2.2 高并发数据处理 1.3 通过大数据构建新需求 1.3.1 智能推荐 1.3.2 广告系统 1.3.3 产品/流程优化 1.3.4 异常检测 1.3.5 智能管理 1.3.6 人工智能和机器学习 二、大数…

【深度学习: ChatGPT 】经验教训:使用 ChatGPT 作为 ML 工程师一天

【深度学习&#xff1a; ChatGPT 】经验教训&#xff1a;使用 ChatGPT 作为 ML 工程师一天 介绍设置过程标杆ChatGPT 做机器学习ChatGPT 能否真正实施这些解决方案&#xff1f;结果结论 TLDR;在最近使用 AI 应用程序 ChatGPT 的用例激增中&#xff0c;我们询问它是否可用于改进…

肯尼斯·里科《C和指针》第12章 使用结构和指针(1)链表

只恨当时学的时候没有读到这本书&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; 12.1 链表 有些读者可能还不熟悉链表&#xff0c;这里对它作一简单介绍。链表(linked list)就一些包含数据的独立数据结构&#xff08;通常称为节点&#xff09;的集…

【数学建模】【2024年】【第40届】【MCM/ICM】【A题 七鳃鳗性别比与资源可用性】【解题思路】

我们通过将近半天的搜索数据&#xff0c;查到了美国五大湖中优势物种的食物网数据&#xff0c;以Eric伊利湖为例&#xff0c;共包含34各优势物种&#xff0c;相互之间的关系如下图所示&#xff1a; 一、题目 &#xff08;一&#xff09; 赛题原文 2024 MCM Problem A: Reso…

704. Binary Search(二分查找)

题目描述 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 问题分析 确定左右界&#xff0c;然后按规则进行更新即可 代…

H12-821_73

73.某台路由器Router LSA如图所示&#xff0c;下列说法中错误的是&#xff1f; A.本路由器的Router ID为10.0.12.1 B.本路由器为DR C.本路由器已建立邻接关系 D.本路由器支持外部路由引入 答案&#xff1a;B 注释&#xff1a; LSA中的链路信息Link ID&#xff0c;Data&#xf…

Linux探秘:如何用 find 命令发现隐藏的宝藏

&#x1f31f;&#x1f30c; 欢迎来到知识与创意的殿堂 — 远见阁小民的世界&#xff01;&#x1f680; &#x1f31f;&#x1f9ed; 在这里&#xff0c;我们一起探索技术的奥秘&#xff0c;一起在知识的海洋中遨游。 &#x1f31f;&#x1f9ed; 在这里&#xff0c;每个错误都…

python 爬虫篇(3)---->Beautiful Soup 网页解析库的使用(包含实例代码)

Beautiful Soup 网页解析库的使用 文章目录 Beautiful Soup 网页解析库的使用前言一、安装Beautiful Soup 和 lxml二、Beautiful Soup基本使用方法标签选择器1 .string --获取文本内容2 .name --获取标签本身名称3 .attrs[] --通过属性拿属性的值标准选择器find_all( name , at…

动漫风博客介绍页面源码

动漫风博客介绍页面源码&#xff0c;HTML源码&#xff0c;图片背景有淡入切换特效 蓝奏云&#xff1a;https://wfr.lanzout.com/iIDZu1nrmjve

Web前端框架-Vue(初识)

文章目录 web前端三大主流框架**1.Angular****2.React****3.Vue**什么是Vue.js 为什么要学习流行框架框架和库和插件的区别一.简介指令v-cloakv-textv-htmlv-pre**v-once**v-onv-on事件函数中传入参数事件修饰符双向数据绑定v-model 按键修饰符自定义按键修饰符别名v-bind(属性…

RocketMQ生产常见问题

RocketMQ如何保证消息不丢失 1、哪些环节会有丢消息的可能&#xff1f; 其中&#xff0c;1&#xff0c;2&#xff0c;4三个场景都是跨网络的&#xff0c;而跨网络就肯定会有丢消息的可能。关于3这个环节&#xff0c;通常MQ存盘时都会先写入操作系统的缓存page cache中&#xf…