大多数浏览器和
Developer App 均支持流媒体播放。
-
联网改进 - 第 1 部分
在所有 Apple 平台上采用现代联网框架并遵循效率和性能方面的最佳做法,紧跟新兴和不断演进的网络协议和标准。在这个讲座中,了解低数据模式、URLSession 中的 Combine 框架、WebSocket,以及网络移动性改进。
资源
相关视频
WWDC23
WWDC21
WWDC20
WWDC19
-
下载
欢迎来到网络发展专场 我是 Joshua Graessley 我和我的同事都来自 互联网技术部 我们很高兴向你们介绍去年我们在 我们平台的网络方面 所做的一些改进
第一部分 我们要讲讲低数据模式 这是一个令人惊喜的新功能 我们可以一起协作 来帮助保存用户的网络数据 如果它很重要的话 我们会跟大家谈谈 你是如何编写异步 用 Combine 在 URLSession 里编写 非常漂亮的异步网络代码 我们也会谈到 WebSocket API 去年 这是你们在答疑会上 提出的一系列问题中的一个 今年 我们很高兴 能为你们带来一个解决方案
最后 我们会说说 一些在移动性上的改进 我们还将讨论如何彼此协作 以在设备从一个网络转换到另一个时 给我们的用户提供 一个更加无缝的体验
在第二部分 我们将涉及到 许多额外的话题 将会在今天的 5 点进行 我希望你们届时能够 回来继续参加 那么在我们进入低数据模式之前 我想要花几分钟 来说说我们平台上 使用的网络 API
在我们的平台上使用的 最好的网络 API 是 URLSession 和 Network.framework 接下来你会在这一场 和这个部分的第二场中听到 我们做了许多很棒的改进 所有这些改进 都可以在你的 App 里获得 如果你正在使用这些框架的话
但如果你使用套接字 那么你将不能利用这个 因为很不巧 套接字并不具备 我们所要求的丰富性 以在该层提供 这个功能 那么 如果你正在做一个 VPN 或者一个内容过滤器 那么 NetworkExtension 框架 是一个很好的解决方案 我们在这方面也做了很多改进 我们将会在明早 9 点 为大家介绍 现在 我要为大家介绍低数据模式 低数据模式是一个非常棒的 iOS 13 里的新功能
我觉得要介绍低数据模式 最好的方法就是 你可以回想一下 你是怎么来到 WWDC 的 也许你会乘坐飞机 飞到明媚的圣何塞
而在那班飞机上 也许有一个 Wi-Fi 网络 而这个 Wi-Fi 网络 可能有点昂贵 而且可能很拥挤 这时你可能很希望 你能有办法 告诉你的 App 和你设备上的系统 说使用这个网络 真的要在非常节省地 使用网络数据 低数据模式就是用来解决这个问题的
低数据模式给用户一种能力 一种偏好选择 来给你的 App 和系统 发出信号并告诉它们 你真的很想在用这个网络时 十分节省数据
这个可以在每一个 基于 SSID 的 Wi-Fi 网络上 和基于 SIM 的蜂窝移动网络上进行设定
当你的设备处于 低数据模式网络时 会有两个主要的影响 包括对系统规则的一个更改 以及对采用了 低数据模式的 App 的一个更改 在系统规则方面 当我们打开低数据模式网络时 我们会推迟所有的 后台自主型任务 因此 如果你在飞机上 打开了低数据模式 我们会推迟所有的 后台自主任务 当你下了飞机 入住酒店之后 然后连上了酒店的 Wi-Fi 网络 那儿的网络可能不是那么拥挤 也更加便宜 然后我们会将所有这些后台任务 重新开始并继续进行下去 我们所做的另一个更改是 禁用后台 App 刷新 这帮助我们避免了 后台的 App 占用网络数据 而这些后台 App 和用户真正感兴趣的 可能一点关系都没有 这样我们已经节省了很多数据 但我们认为如果你在 你的 App 中采用 低数据模式 那么它所带来的改变 还能节省更多的数据 因此 我想花一些时间 来谈谈你可以使用的技术 首先我们要看看 你的 App 是怎样 使用网络数据的 当你开始深入了解时 一定要时刻记住 你可以节省数据 但不要影响你的用户体验 务必要做到这一点
我知道虽然这看上去很明显 但你也许会惊讶于 能减少你正在使用的 网络数据的数量 的优化方式
好 一旦你达成了 这些容易实现的目标 即可以在节省数据的同时 保证用户体验不受影响 那么你就必须要开始 做出一些权衡的决定
在很多情况下 你有机会使用更多的数据 这给你带来很好的体验 而使用更少的数据 仍然能给你一个很好的体验 但也有可能会让你的用户体验打折扣 低数据模式就是一种方法 让用户来向你的 App 发送信号 说明你真的想要节省该数据 然后在选择该选项的同时 要保证一个好的用户体验 但也许没有那么好 那么 我们来说说你一些 你能用到的技术 第一个是减少图片质量 如果你的 App 里并不以图片为主 那么通过降低图片质量 你可以节省大量数据 你仍然可以让用户 做他们想要做的事 但在此过程中节省数据
你可以减少预载入 预载入是一个很棒的技术 能够提高性能 但它也有个缺点 即有时你所预载的资源 是用户从来不需要的 同时 如果他们真的在意 自己使用了多少 网络数据的话 预载入确实是会增加数据使用量
那么 当你在低数据模式时 你可以使用 你可以消除预载入 这样就能节省数据 而如果用户将该内容 滚到视图中等待它加载 那他们则必须等得久一点 你可以少进行同步 数据将会延迟 稍微长一点 但你仍然会有数据 用户仍然可以完成 他们打算去做的事 但是在一长段时间内 你其实可以节省相当多的数据 通过降低同步的频率 你可以标记后台任务 为自主型任务 你会感到惊讶 有多少你设置的后台任务 并不是真的需要立即完成
标记一个后台任务 为自主型任务 这使得系统在实际的 调度操作时具有很大的灵活性 正如我之前提到过的 当你坐在飞机上 系统就有机会 推迟那个任务 直到我们连上一个 非低数据模式网络 另一个很棒的解决方案是禁用自动播放 这样做真的很棒 因为它不会妨碍用户 播放他们感兴趣的内容 但是它意味着用户 不需要为他们 根本不感兴趣的内容花费数据 这就引出了另一个 非常重要的问题 当你选择如何实现 低数据模式时 你不能够阻碍 用户发起的活动 低数据模式就是 告诉系统来减少 所使用的网络数量 但是要确保 用户仍然可以完成 他们打算要做的
当你查看你的 App 时 你可能意识到 你所进行的一些操作 的确使用了很多网络数据 而你对此无能为力
虽然我们想用任何可能的方式 来减少用于这些操作的 网络数据的使用量 但是我们并不想 突然弹出一个对话框 来问你确定要这么做吗 我看到你在使用网络数据 而这将会花费很多数据 不要质疑用户 他们已经打开了低数据模式 同时已经要求你的 App 来执行该操作了 那就直接照做吧 那么 我们来再说说 支持你的 App 执行 低数据模式的 API
我们已经为 URLSession 和 Network.framework 增加了 API 我们要记住的是 当一个网络处于 低数据模式时 该数据网络上则会设置约束属性
那么 下面就是我们提供的 所有基于该约束属性的 API 在 URLSession 中我们 添加了一个属性 allowsConstrainedNetworkAccess 在默认情况下 此属性设置为真 你的 App 就可以在默认情况下 使用低数据模式网络 然后可以将其设置为假来退出 你可以将其设置 在 URLSession 请求上 和 URLSession 配置上
在 URLSession 中 我们真的 鼓励你继续尝试 大型资源获取 或者预取并设置 allowsConstrainedNetworkAccess 为假 如果操作出现错误 且该错误有一个网络不可用的 原因限制 那就意味着 该操作的失败 是因为你正处于低数据模式 而此时你要做的事应该是 返回并执行 低数据模式操作 如果是一个大型资源获取的情况 它可能就反而获取一个 更小的资源 而在预取的例子中 只要等到用户真正需要该内容
这样做的另一个好处是 你可以利用缓存中 可能已经存在的任何内容 那么在 Network.framework 中 有一个相似的属性 叫做 prohibitConstrainedPaths 你可以设置其为真 来限制你的连接 及其他网络对象 使用低数据模式
不过在 Network.framework 里 你还有另一个选择 如果你要连接相同的主机 无论你是否处于低数据模式 你都可以继续 并建立该连接 一旦连接建立起来 你可以获得当前的路径 而在当前路径上 你可以查看它是否受到限制 这会告诉你这个连接 是否建立在一个 低数据模式网络下
好 如果你照此方法做 一定要确保 你处理了路径的更新
因为很有可能 限制属性会改变 你的连接的使用寿命
那么 我花了很多时间 讨论了限制属性 和低数据模式 还有一些其他的属性 你的 App 可以用它们 来在给定的网络上 判断该做什么
还有一个昂贵属性 我们去年在 Network.framework 中引入了它 今年我们将在 URLSession 中引入 allowsExpensiveNetworkAccess 属性
也有特定接口类型的检查 比如蜂窝移动网络或 Wi-Fi
如果你还没有 采用其中任何一个 你可以将重点放在 采用低数据模式上 这绝对是一条正确的路 低数据模式 与限制属性相互关联 因而用户拥有控制权 他们有自己控制的设定 另一方面 昂贵属性 是一个系统设置的属性 它几乎总是为 蜂窝移动网络而设置 它同时也为 Wi-Fi 网络设置 当该网络连接到个人热点时
你也可以检查蜂窝移动网络 但那也是用户 无法控制的事 所以 如果你正在进行 基于蜂窝移动网络接口 或昂贵属性的检查 这个时候转去 使用限制属性 来利用低数据模式 是非常好的想法 如果你已经着眼于这些选项 并决定了你仍然想要 基于昂贵属性 或蜂窝移动网络来做决定 我们强烈建议你使用昂贵属性
昂贵属性更为灵活 在许多方面 它都能够 有效地保证你的 App 不过时 现在 蜂窝移动网络几乎 总是被标记为昂贵的 但是在未来也许不会 也许还会有其他的接口类型 它们也可能很昂贵 通过使用昂贵属性 你的 App 会在将来 用不同的接口来保证正常运行 如果你正在检查 一个特定的接口 比如蜂窝移动网络 那么你就不能够利用它 我真的很期待看到 你们都真的采用了低数据模式 非常感谢大家的聆听 下面 我想要请 Guoye 上台来跟大家谈谈 URLSession 中的 Combine 谢谢 Josh 早上好 我是 Guoye Zhang 今天很高兴与那么分享 我们是如何支持一个 新的 Swift 框架的 也就是 URLSession 里的 Combine 以及你如何利用 Combine 来简化 你的网络代码
Combine 将装饰异步编程带进 Swift 中 要给大家解释这是什么 我就要从一个构建一个 响应式检索栏的例子开始 因此 无论何时用户 在这里键入和接收订阅 同时接收 URL 以启动搜索 搜索栏都会发布值 在这中间 我们用 映射操作符来为 URL 映射值
那么 假设我只想 在有足够内容的时候 启动一个搜索 我们可以使用过滤器
在这种情况下 过滤器会 删除所有小于三个字符的字符串
现在我们已经消除了 用户类似于一个 H 这样的查询 但是 该搜索仍然时常发生 如果我只想在用户 暂时停止输入时进行搜索呢 我们可以用去 debounce() 操作符
这样 去抖动就延迟了该值 并且只在有显著延迟时转发它 在这个例子中是 0.2 秒
然而 使用去抖动 如果用户键入了一些东西 然后删除掉了 我们最终可能 会在该链上发送相同的值 然后一次又一次地开始 相同的搜索 为了解决这个问题 我们可以添加 removeDuplicates() 操作符
removeDuplicates 会记住 接收到的最后的值 然后当它发生改变的时候只转发一个新的值
现在 我们有了最后的 检索栏的版本 通过更改这些操作符 你的异步代码就会变成线性且可组合的代码了
Combine 会处理一段时间内的值
它由发布器 操作符 和订阅器组成
这个链是由订阅器 所发送的需求驱动的
为了对需求做出响应 发布器向该链发送值 这就是 Combine 处理压力的方法 如果你想要学习更多 近期会有一个会议叫做 介绍 Foundation 中的 Combine 和 Advances 同时我还建议你去参加 周四下午的 Combine 实践会议 网络在本质上是异步的 这就是它为什么 很适合采用 Combine
今年 我们在 URLSession 中 引入 DataTaskPublisher 它是一个单一的值发布器 它的工作原理类似于 我们现有的基于闭包的便利方法 这意味着你可以从一个共享的 URLSession 或从你自己的 URLSession 中创造它 同时接收你的委托上的 身份验证挑战和软件度量 这是 DataTaskPublisher 的界面 它遵循发布器协议 一旦成功 它会向你发送一个 真正的数据池和响应 如果失败它会发送给你一个 URLError
现在 让我给你演示一下 Combine 在 URLSession 中是如何工作的
那么 为了做这个演示 我已经禁用了 URL 缓存 所以所有的资源都要 通过网络获取 我也已经使用网络连接调节器 来模拟一个真实的 3G 环境
因此 我正在为一个 叫 PubSocket 的酒吧构建一个 App
这个 App 在栏中显示了 每个项目的名字 图像和项目价格
那么 在听完 Josh 讲的低数据模式后 我决定为低数据模式提供 一个高分辨率的图像 和一个低分辨率的图像 现在我正处于低数据模式 那你可以看这些黑白的图像
而如果我关掉低数据模式
这些图片就会被换成 高分辨率的图片
让我们来看看没有 Combine 现在这个是怎么完成的
那么该界面是 在 UITableView 中构建的 这里我们有数据资源方法 cellForRowAt indexPath 在这个方法中 我们取回 一个可重用单元格 并配置单元格上的每个项目的名称和价格
然后我们开始一个 URLRequest 来提取高分辨率的图像 同时禁止限制网络访问
这里的 pubSession 是 共享全球会话 我们在《PubSocket》App 中使用它 同时用它从请求中创建数据任务
当任务结束时 我们查看状态码是否是 200 好 我们将数据转换为图片 然后将图片放进单元格中
当任务因为低数据模式而失败时 我们创建一个新的数据任务 来提取低分辨率的图片
我们在这里做同样的事 我们检查状态码 转换成图片 然后将它放入单元格
不要忘记重新开始这些任务
那么 作为一个网络工程师 我知道这里的网络逻辑是可靠的
我坚持用最好的方法 以不进行任何预取操作 但是 我对现在的代码 还不是很满意 因为它有很多的重复 我们要两次查看状态码 然后转换图片 同时 你也许注意到了 我犯了最常见的错误 即采集单元格 然后异步地将图像 放入单元格中
该单元格此时可能已经 被 UIKit 重新使用了
那么 接下来为大家展示这个 Bug 我会很快速地向下滚动 请注意菜单上的最后一些项目 正如你看到的 热狗和鸡腿的图片给放错了
让我再来演示一遍 我会滚动到顶端 然后注意前几个项目
对 沙士和炸薯条 摆放的图像也是错误的 在它们被正确的图像替换之前
好 我们来看看如何使用 Combine 来解决所有这些问题
首先 我要删除这个用来提取的代码
这里我们用 MenuItemTableViewCell 类 那么 单元格接收到一个图像 单元格是很适合放 订阅器的地方
这里的订阅器适合 任何可取消的协议
我们可以取消该订阅器
准备使用重用方法 取消会立即发生 这意味着我们将不会 有机会将任何图像 放置在错误的单元格上
现在我们回到 TableView 数据资源方法 和 cellForRowAt indexPath
我们首先要做同一件事情 即创建一个 URL 请求 然后提取高分辨率的图像 并禁用限制网络访问 但是我们不创建数据任务 而是一个请求的数据任务发布器
然后我们在 Combine 中使用 新的 tryCatch 操作符 这个 tryCatch 操作符 会捕捉 DataTaskPublisher 所产生的错误 如果该任务因为低数据模式 而失败 我们就用新的发布器 来更换该发布器 以提取低分辨率的图像
否则 我们只是将一样的错误 重新抛给了该链条
接下来我们用 tryMap 操作符 来处理我们接收该数据 和该响应的成功案例 我们查看了状态码 然后从该数据中创建一个图像
而此图可以同时处理 高分辨率图像 和低分辨率图像 这样就消除了代码的复制
最后 我们将该错误 用一个图片占位符代替 切换主队列 然后使用指定的订阅器 将该图像放入该单元格
现在 这样很不错 我们完成了一样的逻辑 用更短的代码和线性的代码
但是我们可以完成更多吗 还有一个操作符 我想分享给大家 它叫做 retry
想象一下在支持 retry 之前 你应该要做些什么呢 你必须要么递归地调用 数据任务创建器 要么维护一个状态机
现在在 Combine 里我可以 直接把 retry 操作符放在这里 在我们替换该错误之前
retry 会捕捉 抛在这里的错误 它通过重启 操作符的链条进行重试 然后再次提取该图像 在这个例子里 我只重试了一次
那么 所有平台上的网络 API 都设计得十分可靠 所以通常来说 你不需要进行重试 但是 你的 App 也许需要 连接一些不可靠的服务器 或者 Meta Box 它经常会 出现 500 服务器错误 这种情况下这个 tryMap 操作符 将会抛出一个无效的 服务器响应错误 而这会被 retry 捕捉
但一定要注意 网络操作符是很昂贵的 retry 也不例外 所以如果可以 就尽量避免 retry 如果必须要 retry 就从一个非常低的数字开始
同时 我们还要注意 你的请求的幂等性
在我的 App 中两次下载 一个图像没问题 但如果你的 App 要处理 如付款等交易的话 盲目进行重试是很危险的
现在我来再次打开低数据模式
我们让 App 再次运行起来
正如你所看到的 我们像以前一样提取了低分辨率的图像
而如果我关闭低数据模式
会提取一样的高分辨率图像 我们的单元格里 不会有任何放错的图像
好 让我们回到幻灯片
总的来说 我已经给你们展示了 如何用 Combine 使你的网络代码 变为简单的线性的 同时更少出错的代码 我也为大家讲了 这些 Combine 操作符 是多么容易组合 你可以通过添加一次性代码 来支持 retry 但是一定要注意 使用少量 retry 次数 且只重试幂等的请求 最后我向大家展示了 你可以如何在低数据模式中 使用 Combine 且不用做任何预取检查
这是我从我的演示中 抽取出的代码 为低数据模式进行自适应加载 它接受一个常规 URL 和一个低数据 URL 并返回一个数据发布器 首先 我们创建一个请求 来提取一个常规 URL 并禁用限制网络访问
我们用 URL 用该请求 创建一个数据任务发布器
然后 我们马上处理 低数据模式所引起的错误 然后我们用一个新的发布器 来代替该发布器 来提取低数据 URL 下面 我们一起处理这两个 成功案例 检查状态码 然后将数据返回给你
你可以将这个代码 用作一个 Combine 和低数据模式的起点 然后根据需要定制此代码
在这里我必须要提到 在现有的 SDK 中还无法获得 其中一些 API 我们正在努力将它们放入 未来的测试版中
接下来我想邀请 我的同事 Jiten 来谈谈 WebSocket 谢谢你 Guoye 大家早上好 我的名字叫 Jiten Mehta 很高兴能你们谈谈 网络框架中的 iOS 13 和 macOS Catalina 里 全新的 WebSocket 协议
过去几年 有大量的开发者 向我们询问 关于 Apple 框架 对 WebSocket 的支持 事实上 这是我们去年进行的 一项关于网络的调查中 开发者们提出的第一个要求
WebSocket 允许通过 单个 HTTP 连接进行双向通信
这使得开发者们能够 编写一些 App 如聊天 多人游戏 和其他实时的 App WebSocket 在众所周知的 HTTP 端口上工作 它完全兼容于 现有的网络基础设施 允许你连接到代理服务器 CDN 和防火墙
在过去 WebSocket 协议 已经作为 JavaScript API 在网络浏览器中可用
但是看到 WebSocket 给网络 App 带来的益处 我们决定将这种益处 扩大到我们的网络框架中 不知是在网络视图中 已有的 JavaScript API 中
这让你能够使用 你现有的网络基础设施 并将它带到 Apple 平台上的 本地 App 中
之前我们说过 WebSocket 那么现在我们来看一个 我们今天使用的能够 实现双向通信的普通技术
我们以一个聊天 App 为例 当一个客户端想要从服务器 接收一个响应 它会发出一个请求
该服务器用一个 200 OK 的状态码 来立即做出响应 但它并没有发出响应体 因为它在这时候没有响应体
在未来的某个时候 一旦服务器具备为客户端 准备的响应 它便会将它发给客户端 就在这时 客户端 会发送一个新的请求 表示它想要接受下个消息
这被称作长期轮询 但是长期轮询 也有一些缺点
当它们想发送消息时 两个端点都必须发送 一个 HTTP 请求 或一个 HTTP 响应 这是很大的开销
此外我们必须要 在服务器上维护复杂性 以启用长期轮询
我们来看看 WebSocket 是怎么 解决这个问题的吧 在 WebSocket 进行握手的 第一步的第一部分 客户端发送给 服务器一个请求 说明想要升级这个 到 WebSocket 的连接
服务器用 101 切换协议来响应 就在这个时候 我们在两个端口中间 有一个双向流 现在 两个端点都可以 自由地向任何方向发送消息 它们可以发送数据串 或 Ping/Pong 帧之类的消息 且不需要任何 HTTP 开销 URLSession 是 Apple 推荐的 HTTP API 今年我们很高兴宣布 URLSessionWebSocketTask 一个基础框架中新的 API 为了创建一个 WebSocket 任务 你可以简单地传入 你想要的连接和调用 resume() 的 URL 我们将开始进行握手 你不需要担心 要处理任何的状态码 WebSocket 握手的第一部分 使用的是 HTTP 语义 这意味着你的 URLSessionWebSocketTask 将会使用 你现有的 URLSession 配置对象
它还将使用你的网络存储 来做 Cookie 及约定查找 我们将处理你代理 收到的任何挑战
一旦你成功连接 你就可以在该任务上发送消息
你还可以通过传送一个 完成处理程序来接收 该任务上的消息 一旦我们 从服务器接收到整个消息 该处理程序将被异步调用
WebSockets 中的 URLSession API 很接近 JavaScript API 而后者是基于 完整的消息和回调
但是一些开发者需要的 不止如此 比如服务器支持 或读取部分消息 为此 我们很高兴介绍 Network.framework 中的 WebSocketSupport 通过 NWConnection 和 NWListener 对象 同时给你客户端和服务器支持
有了它 你可以有一个 面向消息的传输协议 该协议可以扩展为对等通信
你也可以通过 为 give 和 receive 操作值 指定最小和最大字节 来接收部分消息 要将 WebSocket 添加到 对象的网络框架中 你可以只创造一个 启用了 TLS 的参数对象 接着 创建一个 websocketOptions 然后将其设置为 参数的默认协议栈
一旦你创建了参数 接下来你就可以将 这些参数传入 NWConnection 构造器 来创建一个 NWConnection 对象 或者如果你想要创建 一个监听器 你只要将这些参数 传入该监听器的构造器中
发送和接收 API 与去年相比没有变化 你可以继续使用这些 来发送和接收 WebSocket 消息
好 下面我们来看看 WebSocket 的实际操作 我将会在 Guoyue 刚刚 向你们展示的 App 《PubSocket》上进行创建 但我想对《PubSocket》的商业模式 进行一点点改变 现在项目的价格将会变为动态的 而且会根据需求而变化 那么 想一想股票市场 但这里是交易食物和饮品的 那么 在左边你可以看到 《PubServer》它是 调酒师可以在其中 编辑商品或者 更改价格的一款 App
在右边是我们 已经看到的酒吧菜单 这是你的客户端 或走进酒吧的客户 可以看到的 App 动态定价的新功能 将被称作 PubSocket+ 因此 我们来看 PubSocket+ 是如何与我们当前的 服务器和客户端实现一起工作的 我们假设调酒师想要 把沙士的价格 提升到 6.99 美元 我单击更新
现在 客户端就要下拉刷新 只要他们这么做了 沙士的价格就更新了
好了 但我觉得我们肯定能 做得比这个更好 我想让《PubSocket》的客户 有一个无缝的体验 他们不需要下拉刷新 就可以得到实时的价格更新 让我们来看看 WebSocket 是如何 帮助我们实现这个的 下面我们前往 Xcode 首先我要停止
服务器和客户端 接着我们前往服务器 那里有一个 NWListener 它作为我的 TCB 服务器
这里有一些参数 这些参数是我用设置了 TLS 选项的方法创建的
首先 我要在这里做一个更改 来创建一些 WebSocket 选项 然后将它设置在 参数的协议栈上 这样就告诉我的服务器 怎样完成 WebSocket 与想要 与之相连接的用户端的握手 我将会在我的服务器上 做出的下一个改变 在这个叫做 sendPriceChanges 的函数里
这个函数原本发送 WebSocket 消息给所有 想要连接到它的客户端 在服务器上每次更改 一个项目的一个价格 现在 我使用 defaultStream 实现了一个 send()
这意味着我传送到 send() 方法的数据 是作为一个字节包 在这个 TCB 连接中被发送的 它没有任何的消息结构 我要更改这里的上下文 并创建一个新的上下文 用一些与之相联系的元数据 现在 这就告诉我的连接 要以 WebSocket 消息结构的形式发送数据
有了这两点变化 我的服务器应该准备好 向我的客户端发送 WebSocket 消息了
现在 我们来看 我可以在创建服务器的同时 前往客户端
那么 在客户端上
我首先要更改 connect() 函数 connect() 将会连接到 一个新的服务器上 因此我要用 URLSessionWebSocketTask 我会简单地将 URL 传入任务中 然后调用重新开始 这样就可以开始进行握手 一旦我连接上了 我就会调用 readMessage 它将会收到来自服务器的消息 现在 我们看看应该如何 完成 readMessage
在 readMessage 里我将 在任务上调用 receive() 然后通过一个完成块
在成功的案例里 我将更新我的 UI 更改价格 之后我会立刻再次调用 readMessage 来读取 下一条从服务器传回的消息 一旦我在客户端上做了 这两处更改 我应该完全 准备好连接我的服务器 并接收 WebSocket 消息
好 现在来运行一个新的服务器 和客户端 看看它们是如何工作
首先我要运行我的服务器 现在我们的新客户端在读取 PubSocket+ 它有新的 改良的 WebSocket 能力 那么 我们假设现在是 饮品减价供应的时间 调酒师想将沙士的价格 降低到 1.99 美元 那么 让我来完成这个更改
我会点击更新 然后你可以看到 客户端上的价格就会更新 客户不需要再刷新
那么 对于那些错过的人 我将降低炸薯条的价格 降到和沙士一样 那么现在仍然是降价时间 而只要我点击更新 你就会看到在客户端上 炸薯条的价格发生了改变 我并不用下拉刷新 我再做一次 那里薯条的价格就改变了 那么 这是没有 任何 HTTP 开销的 WebSockets 双向通信
你们中的一些人也许想知道 右上角的“Stats”按钮是用来干什么的 如果我单击它 这里就会有一些我们 从 URLSessionTaskMetrics API 中 收集的新统计数据
此外 在底部 RTT 是我的客户端 和服务器之间的往返时间 我在 WebSockets 中使用 Ping/Pong 帧进行计算 我目前使用网络连接调节器 来模仿一个繁忙的酒吧环境 你看可以用像这样的东西 来监控你的客户端 和服务器之间的连接
如果你有兴趣了解更多 关于添加 Metrics API 中的新特性 或者如何使用 网络连接调节器 请在今天下午 5 点 来到我们的网络会场
现在让我们回到幻灯片
让我们快速回顾一下 PubSocket+ 对于我们的服务器 我们使用了设置在协议栈上的 WebSocket 选项中的 NWListener 而对于客户端 我们使用了 URLSessionWebSocketTask 来连接我们的服务器读取消息 对于传输 我们使用了 双向 WebSocket 消息 最后 所有操作的优点是 双向消息传递 这样做的话 开销非常小 现在 让我们回顾一下 可以为你的 App 添加 WebSockets 的 API
让你能够在你的 网络 App 和网络视图中 现有的 JavaScript API 中 添加 WebSockets 而今年有所不同的是 我们可以将创建在 Network.framework 顶端的 URLSessionWebSocketTask 嵌入 URLSession 它与你现有的 URLSession 配置对象一起工作 同时提供自动的 Cookie 和身份验证支持 它还提供一个便利的方法 用 Ping/Pong 来测量往返时间
此外 今年 在 Network.framework 中的 WebSocketSupport 可以通过 NWConnection 和 NWListener 对象 为你提供客户端和服务器支持
它让你直接存储 完整和部分的消息 包括 Ping/Pong 帧 你可以通过 webSocketOptions 对象 随意地设置自定 Header 如 Cookie 或其他的 身份验证 Header
我们很高兴为所有的开发者们 开放这项技术 我们很想看看你们 要拿这个来做什么 接下来就有请 Christoph 来为大家讲讲 移动性的改进
谢谢 Jiten 大家好
我是 Christoph 我现在要为你 展示我们在 iOS 13 中 对移动性所做的改进
那么 用户经常会遇到这种情况 当他们走出家门 并远离他们的 Wi-Fi 接入点时 Wi-Fi 的信号变得更弱 通常这时 App 就会变得更慢 因为网络变得更慢了 有时 App 会完全失效 所以人们已经习惯了 这种状况 所以当他们 走出家门的时候 他们就会向上滑出控制中心 然后关闭 Wi-Fi
好 我相信我们都经历过 这种情况 对吗 所以我想要改变这种现状 我认为用户应该永远 不需要关掉 Wi-Fi 当他们走出家门的时候 而你的 App 也应该时刻正常运行 甚至是在 Wi-Fi 信号很差的时候
我相信这个屋子里 在座的每个人都想达到这一目标 那么让我来展示我们要怎么做
我们常用这种方法来表述 Wi-Fi 我们在中间有一个 Wi-Fi 接入点 它周围的同心圆 向我们显示 Wi-Fi 信号 是如何逐渐消失的 随着手机越来越远 信号就变得越来越弱
在这种场景中 要手机决定是使用 Wi-Fi 还是切换到蜂窝移动网络 这是非常简单的 对吗 好 现在问题是 这种对 Wi-Fi 外观的描述 实际上与现实相去甚远 事实上它看上去更像这样 在中间有Wi-Fi接入点 然后在它周围的是非常 参差不齐的 Wi-Fi 信号 而这些斑点是因为 房间里的物体干扰了信号 房间 墙壁 所有的东西 都在使 Wi-Fi 信号成为 一个非常不确定的质量指标 手机只要有一点小的移动 就可能将它从一个好的位置 移到一个坏的位置 所以 手机的确很难 知道 Wi-Fi 信号 仍然是好或坏 它也许仍然接收到 来自接入点的信标 但是尽管如此 信号其实可能会很差 以至于无法发送或接收任何数据
所以 在这种环境下 手机需要决定 自己使用 Wi-Fi 还是 蜂窝移动网络 Wi-Fi 信号的不确定性 正是移动性所面临的重大挑战
那么 在 Apple 中我们已经 意识到这个问题有一段时间了 我要向你展示在过去 我们已经采取了多少步骤 来改进这样的场景 这一切都开始于 iOS 7 的 Siri
在 iOS 7 中 我们为 Siri 引入了多路 TCP 多路 TCP 允许同时使用 Wi-Fi 和蜂窝移动网络 所以 从 iOS 7 开始 无论何时 当人们 走出家门使用 Siri 时 多路 TCP 都会保证 要么使用 Wi-Fi 要么使用蜂窝移动网络 以减少 Siri 用户的延迟 同时减少出错率
通过引入多路 TCP 我们看到了很不错的结果
那么 一个真正的端对端的 多路径协议 如多路 TCP 要求客户端 和服务器都知道它 二者需要一起工作 对吧 它们需要共同决定 是通过 Wi-Fi 发送流量 还是通过蜂窝移动网络
所以我们问自己 如何在提高移动性的同时 不需要让客户端 和服务器一起工作 也不需要修改 服务器配置
答案出现在两年后的 带有 Wi-Fi Assist 的 iOS 9 中 Wi-Fi Assist 为所有的 App 和所有的流量处理移动性 与任意的服务器对话 而它所用的方法是 首先启动 Wi-Fi 当信号不好连接不上时 它会设法足够快速地 建立起连接 我们只需要通过蜂窝移动网络 连接来建立另一个连接 从 iOS 9 开始 自从我们引入了 Wi-Fi Assist 你所有的使用 高级 API 的应用 都可以从 Wi-Fi Assist 中获益 而当用户移动时 会带来更好的体验 这适用于任何的在网上 与服务器对话的服务器
那么 Wi-Fi Assist 也许 仍会被 Wi-Fi 困住 例如 如果它设法 建立该连接 但是之后信号变弱了 那么那些流量仍然会被堵住 为了更好地处理 那些场景 我们仍然需要 真实的端对端的多路径 和我们在 Siri 中使用的方法一样 所以 有了四年的 Siri 中的 多路 TCP 的经验后 我们决定向你们每个人 开放这个 API 那么 从 iOS 11 开始 你就可以 开始使用切换或者 URLSession 或 Network.framework 中的交互方式
所以 当你能够确定 你的服务器准备好了 你就可以启动多路 TCP 然后获得与 Siri 所获相同的好处
那么 在这些版本中的每一个 包括 iOS 7 iOS 9 和 iOS 11 中 我们都集中一个指定区域来提高移动性
我们集中在多路 TCP 集中在 Siri 集中在 Wi-Fi Assist 那么现在是 iOS 13
在 iOS 13 中我们改进了 许多东西 这张幻灯片 甚至放不下它们
这就是在 iOS 13 中 移动性的改进 谢谢 谢谢 在 iOS 13 中 移动性的改进贯穿整个系统 包括许多不同的框架 后台程序和 App 从固件到驱动程序 所有东西的移动性都得到了改进 那么在这场的这个部分里 我要谈一谈其中的两个 即 Wi-Fi Assist 和多路径传输
那么 首先是 Wi-Fi Assist 传统上来说 Wi-Fi Assist 只是考虑到 非常有限的信息 来判断 Wi-Fi 信号是否足够好
在 iOS 13 里我们改变了这一点
我们使系统中的 所有组件都提供信息 到 Wi-Fi Assist 中去 使 Wi-Fi Assist 有一个 完整的跨层的移动性检测
较低层 Wi-Fi 和蜂窝移动网络 都提供关于信号质量的信息 以一种比 iOS 12 中 粒度更细的方式
同时 高层次的框架 比如 Network.framework 和 URLSession 及系统其他的后台程序 它们都为 Wi-Fi Assist 提供关于它们的流量 如何前进的信息 所有进入到 Wi-Fi Assist 中的 信息让他能够检测到 我们是否在一个 移动性的情景内 同时我们是否应该开始 使用蜂窝移动网络 那么有了所有这些信息 之后 Wi-Fi Assist 就会 做出它的决定 并反馈给系统 它告诉较低层 Wi-Fi 和蜂窝移动网络 去努力提高信号质量 它还与其他层对话 如 Network.framework 和 URLSession 来开始恢复数据 所有这些都大大改善了 流量的恢复
那么即使已经在 Wi-Fi 上 建立了流量 并开始交换数据 如果之后信号质量下降 我们也能够移动 下一个会在 Wi-Fi 上 使用的请求 我们能够将该请求 移动到蜂窝移动网络
因此你的 App 现在应该 不太容易被困在 Wi-Fi 上 现在 你们的问题 当然是如何从 Wi-Fi Assist 中获得好处 如何从我们在 iOS 13 中所做的 所有这些改进中获得好处呢
那么首先 你可以获得好处 通过使用高级别的 API 比如 URLSession 和 Network.framework 所有这些 API 都是在考虑 Wi-Fi Assist 的情况下构建的 它们从中得到了充分的好处 那么 你只要确保 你的 App 在使用那些 API
接下来 你们中的一些人正在用 SCNetworkReachability 之类的 API 来进行活跃的接口管理 你也许在做启动时检查 以知道你的请求 最终会到达哪里 它会打开 Wi-Fi 还是蜂窝移动网络呢
但这些启动时检查 有一个问题就是 在你检查的时候和 你确实使用连接时
接口有可能已经改变了 Wi-Fi Assist 可能已经决定 将你的流量发送到蜂窝移动网络 或者 Wi-Fi 信号可能已经 明显地改善了 因此 启动时检查是一个 表明你的流量最终 会流向哪里非常糟糕的指标 那么 我们建议你回想一下 你所使用的启动时检查 然后在之后的答疑会中 告诉我们为什么你需要它 我们将会和你一起 找到一个替代
现在如果你仍然需要 掌控流量 比如说不使用 蜂窝移动网络 原因也许是数据传输太大 或者流量对用户体验 不是很重要 你就可以使用 allowsExpensiveNetworkAccess 之类的 Helper 并将它们设置为假 这样一来 你的请求 就不会进入蜂窝移动网络连接
那么 这是 Wi-Fi Assist 我们做了很多努力 来使它在 iOS 13 里变得更好 你还可以通过使用高级别的 API 来获得其好处 那么我要进入下一个 多路径传输
Siri 已经用它有一段时间了 我们已经在两年前 开放了这个 API 并鼓励你浏览自己的 App 查看哪个流量可能让你 从多路径 TCP 中获益最多 那么今年在 iOS 13 中 这个运行得很好 浏览我们自己的 App 我们决定看看如何 从多路径 TCP 中获益最多 当移动设备对用户体验 非常关键 且难以恢复时 我们通常使用哪一个流量
那么一个 App 是 Apple 的《地图》 大多数用户 当他们 寻找他们的方向时 都是走出家门的时候 这时候才需要搜索 那么在 iOS 13 中 我们为 《地图》启用了 Multipath TCP 现在 无论你什么时候 走出家门 使用 Apple《地图》 来查找方向 并尝试搜索餐馆 你都会使用 MP TCP 你的流量将会从 Wi-Fi 移动到蜂窝移动网络 从周一开始 从第一个 测试版开始 我们就一直 在启用它 我们将看到 Apple《地图》的响应能力将大大提高
在接下来这个 App 中我们有一个 非常关键的用户体验 这也是经常使用的 当你走出家门 你会播放音乐 对吗 当你播放音乐时 你就是在下载一个 充满音乐内容的大文件 你不想让这个音乐暂停 因为当它暂停时 用户会收到打扰 所以在 iOS 13 中我们 为 Apple Music 启用了多路径 TCP 我们看到音乐播放的暂停 越来越少 因为多路径 TCP 现在正在 将流量移到蜂窝移动网络 当我们接近暂停时 所以用户就会有 一个更好的体验 现在 关于这一点 我们已经在 Siri 地图 和音乐中启用了它 你也可以这么做
我们推荐你 看看你自己的 App 看看出门的时候 经常使用的是哪个流量 哪一个有很重要的流量 对用户体验至关重要 并且很难恢复呢 这些流量的种类 他们都很适合 多路径服务类型 你可以切换选择他们 或者在 URLSession 和 Network.framework 中进行交互
那么不要忘记 如果你真的进入多路径 它仍然需要客户端 和服务器一起工作 所以去看看这个 URL 确定正确配置了你的服务器
那么移动性的部分 到这里就结束了
如果我们想要你记住一件事 那就是无论何时 一个用户走出他的家门 他都不应该有一个 不好的体验 导致他关掉 Wi-Fi 那么 当你开发你的 App 时 当你在测试它们的时候 当你在配置一个 糟糕的 Wi-Fi 网络的时候 当你走出家门去测试它的时候 不要期望你的 App 会变得很慢 甚至失灵 或者你的流量 会基本上永远持续下去 原则上它应该就是正常运行 如果不能运行 就要确保 那些失效的流量 正在使用高层次的 API 这些 API 充分地 受益于 Wi-Fi Assist 受益于 iOS 13 中我们做的 所有改进 如果你正在进行活跃的 接口管理 请来答疑会告诉我们 或者发送给我们一个 Apple Bug 报告 告诉我们你的使用案例 我们将会和你一起 找到一个替代 这样你就可以避免进行 这种活跃的接口管理 并充分获益于 Wi-Fi Assist 最后 如果你有流量 仍然堵在 Wi-Fi 上 并且很难恢复 试着看看你能否开始 使用一个多路径服务类型 准备好服务器 然后获得和 Apple Music 和《地图》 以及 Siri 一样的益处 那么 这就是本场的这一部分 我们看过了用低数据模式 用户现在可以打开 标记了的网络 这样就能 减少那些网络上的数据使用 我们展示了一个新的 API 你们的 App 也可以从中获益 如果你在构建一个发布器 订阅器风格的 App 在 URLSession 中使用 Combine 你就能够创建一个很好的 App 就像 Guoyue 今天向你们展示的那样 最后是 WebSockets 最受欢迎的功能就是 将它引入了 iOS 13 中 这样你可以很容易地 在你的 App 中建立双向通信 在 iOS 13 中我们对 App 的 移动性进行了大量的改进 这样你就可以获得 与使用高级的 API 一样的益处
那么 今天下午晚些时候 我们将会进行网络 专场的第二部分 我们会看到更令人兴奋的 给你的 App 带来益处的新的 API 明天 对于那些 正在 macOS 上开发的人 网络扩展上我们有新的 API 我们也在早上 9 点有答疑会 明天早上 9 点开始 你可以届时前来 向我们提出问题 我们很高兴能够 帮你解决问题 那么 就到这里吧 希望你们喜欢今天的内容 谢谢大家 [掌声]
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。