大多数浏览器和
Developer App 均支持流媒体播放。
-
集成你的自定义协作 App 到“信息”
探索 SharedWithYou 框架如何帮助提升你 App 的协作基础结构。我们将介绍如何向协作内容发送安全邀请,并同步参与者的变更。我们还将讨论如何在相关对话中显示内容更新。请观看 WWDC22 的“在你的 App 中添加‘与你共享'功能”,了解有关 SharedWithYou 的简介;并观看 WWDC22 的“利用‘信息'提升协作体验”,了解协作 UI API 的概述。(注:即将发布的 Beta 版将提供 API。)
资源
相关视频
WWDC22
-
下载
♪ 柔和乐器演奏的嘻哈音乐 ♪ ♪ Devin Clary:嗨 我是 Messages 团队的工程师 Devin Lance Parker:我是 Lance 也是 Messages 团队工程师 Devin:欢迎收看 “集成使用您的自定义协作 App 与 Messages ” 协作以会话为起点 在 iOS 16 和 macOS Ventura 中 您可将您 App 的自定义协作体验 直接带入会话结构 本次视频 我们首先 为您介绍协作的生命周期 其次 我们将向您展示如何准备 通过 Messages 共享 App 的协作内容 再次 我们将为您详细讲解如何 及时验证接收者的访问 以及响应参与者的变化 以上所有均不涉及隐私问题 最后 我们将 向您展示您的 App 如何 在 Message 会话中 直接发布有关内容的通知 假设您的 App 已具备协作基础设施 并且已经采用了通用链接 我们将以“将 SharedWithYou 添加到您的 App 中” 和“利用 Messages 增强协作体验”中 介绍的某些概念为基础 首先 我来介绍一下 自定义协作消息的生命周期 说明该 API 如何让您的用户 更加快速地开始协作 当用户决定通过 Messages 在您的 App 中共享协作时 您首先应创建表达内容的元数据 元数据包括用户在发送消息前 可配置的共享选项 以及可由您定义的其他属性 接下来 您将元数据提供给共享表 或拖放 这可让内容草稿暂存 在 Messages 撰写字段中 协作需要由通用链接表示 可立即创建 但最好推迟到 消息发送之前再创建 您 App 的链接创建 由所选的共享选项或接收者决定 如在 Messages 撰写字段中配置的那样 则该条件适合用 用户选择接收者和分享选项 并点击发送按钮 在发送消息前 Messages 要求 您的 App 提供通用链接 以及独立于设备的内容标识符 利用该标识符 Messages 提供了一组 代表该特定协作消息 接收者的加密身份 您的 App 稍后会利用这组身份 允许接收者在其任意设备上 立即打开链接 您的 App 还会将 这组身份存储在服务器上 并将其与共享内容相关联 一旦您的 App 完成该步骤 消息就会被发送给接收者 现在 接收设备上会发生以下情况 我们的目的是立即验证访问权限 将接收者身份 与您服务器上的帐户配对 当接收者打开链接时 您的 App 会收到 打开 URL 的调用 收到其他链接也是如此 当您的 App 检测到某个用户帐户 无权访问该文档时 它就会查询系统 获取 由接收设备加密签名的用户身份证明 您的 App 会发送 签名后的身份证明 至您的服务器进行验证 如果签名有效 服务器会对该证明 与发送设备先前提供的身份证明 做比较 如果匹配 您的服务器就会授予该用户帐户 访问权限 这样一来 接收者就可 即时安全地访问内容 无需交换帐户信息! 这就是协作消息的生命周期! 下面我们来进一步了解 用于启动协作的 API 系统需要某些关于协作的元数据 为此 您要在 SharedWithYou 框架中 使用一个名为 SWCollaborationMetadata 的新类 您需为该类配置属性 如内容标题 即本地标识符 用于在内容共享前参考 又如发起者名称和帐户句柄 用于为用户正在共享的账户 增加透明度 以及默认共享选项 供用户配置 以下是创建元数据对象 及配置其属性的方法 利用以字符串初始化的 SWLocalCollaborationIdentifier 创建本地标识符 该字符串只需供您的 App 在本地识别内容 无需跨设备识别 使用本地标识符初始化一个 新的元数据实例 使用基础框架中的 PersonNameComponents 设置内容标题 发起者账号句柄 及其名称 句柄和名称仅在本地显示 这样用户就可以确认其共享帐户 接下来 设置 defaultShareOptions 展示具体操作之前 我先讲一下选项工作的方式 共享选项是用户在 Messages 协作 或共享表上配置的设置 用户选择的选项将在发送消息之前 提供给您 共享选项可能包含的设置有 谁可对协作进行编辑 或谁有权访问内容 您可用一些类来定义选项 比如从 SWCollaborationOption 开始 选项取决于分组方式 它代表单个开关 或某个设置的互斥值 选项有一个标题和一个标识符 两者要么选中要么不选 有两个类来表示一组选项 即 SWCollaborationOptionsGroup 和 SWCollaborationOptionsPickerGroup 您可用 SWCollaborationOptionsGroup 表示开关的集合 同时用 SWCollaborationOptionsPickerGroup 表示设置的互斥值 最后 SWCollaborationShareOptions 定义了全套选项组 可在元数据的 defaultShareOptions 属性上设置 您还可以提供摘要字符串来描述选项 描述完选项类 我再来向您展示使用方法 这个代码定义了两个选项组 第一组用标识符和 两个可能的选项初始化 标识符是任意字符串 稍后可用于识别用户选择了哪个选项 由于是选择器组合 因此各个选项互斥 该组代表内容权限设置 即读写或只读 然后 默认选择该组的第一个选项 标题设置为描述该组的字符串 第二个选项组以同样的方式初始化 也包含两个选项 但因该选项组为通用选项组 用户可以配置 是否独立允许提及和评论 最后 这两个选项组可用于初始化 SWCollaborationShareOptions 实例 随后在元数据上设置该实例 接下来 根据用户分享内容的方式 将元数据提供给共享表 或拖放 如果您的 App 用的是 SwiftUI SWCollaborationMetadata 就可与新的 ShareLink API 兼容 观看“Meet Transferable” 和“SwiftUI 的新功能” 即可了解有关 Transferable 和 ShareLink 的更多信息 我们来看 在 SwiftUI 的代理模式上 支持协作有多么简单! 从 Transferable 模型对象中 设置一个 ProxyRepresentation 来返回协作元数据实例 然后从一个视图中 使用该模型对象初始化 ShareLink 对于 UIKit 和 AppKit App 您可使用 NSItemProvider 支持分享 且 SWCollaborationMetadata 符合 NSItemProviderReading 和 Writing 所以您只需用 itemProvider 注册一个元数据实例 以支持协作 还有个好方法是 注册多个内容表示 便于支持多个渠道分享 例如 如果您提供了文件表示 Messages 会自动 提供将内容以副本发送 这个选项 您可将 NSItemProvider API 与 iOS 和 iPadOS 上的 UIActivityViewController 和 UIDragItem 以及 macOS 上的 NSSharingServicePicker 结合使用 以下是在 iOS 上 使用共享表进行设置的方法 创建一个 NSItemProvider 实例 注册此前提到的 协作元数据 将可见性设置为系统上的所有进程 用 itemProvider 初始化 UIActivityItemsConfiguration 然后用同样的配置初始化 UIActivityViewController 最后 展示视图控制器 支持拖放也同样简单 初始化 NSItemProvider 并以相同方式注册元数据 然后使用 itemProvider 创建一个 UIDragItem 以便 与拖放 API 一起使用 API 与 macOS 上的 共享弹出框类似 再次设置 itemProvider 这一次 需用它来初始化 NSSharingServicePicker 然后显示涉及目标视图的选择器 macOS 上的拖放 可利用的是 NSPasteboardItem 而非 NSItemProvider 为提供支持 SharedWithYou 要导出一个 NSPasteboardItem 扩展 使用该扩展 直接在一个新的 NSPasteboardItem 实例上设置协作元数据 以支持拖放 以上就是在 Messages 中 设置您的协作内容草稿的全部操作! 接下来 当用户点击发送按钮时 系统会协调您的 App 以设置共享 该操作通过一个名为 SWCollaborationCoordinator 的新类实现 SWCollaborationCoordinator 是一个单例模式 意味着有一个全局共享实例 该共享实例通过您定义的名为 actionHandler 的委托来协调协作 为确保您的 App 始终可用于 协调合作 它会适时在后台启动 所以您应在启动后尽快注册委托 并立即处理操作 避免超时 以下是在您的 App 完成启动后 设置协作协调器的方法 通过共享属性访问 单例模式协调器实例 然后 在 App 委托的 didFinishLaunchingWithOptions 方法中 将 actionHandler 属性 设置为符合 SWCollaborationActionHandler 协议的对象 操作处理程序协议使用了一个 名为 SWAction 的新类 SWActions 代表 您的 App 需要执行的操作 操作完成后会标记为完成 否则会标记为失败 您的 App 需要处理的第一个操作 就是启动协作 SWStartCollaborationAction 包含您此前设置的协作元数据 元数据由用户 选择的共享选项进行更新 完成必要设置后 您可使用通用链接和 一个独立于设备的标识符 完成协作启动操作 如果您启动操作失败 消息就会取消发送 以下是使用示例服务器请求 处理启动操作的方法 首先 检索本地标识符 和来自操作元数据属性的 用户所选的共享选项 使用标识符和选项设置服务器请求 以准备协作 然后 将请求发送到服务器 此示例使用了异步等待功能 最后用通用链接以及响应中的 设备独立标识符来完成操作 如若出现错误 则消息取消发送 操作失败 如果启动操作成功 系统会向您的 App 发送第二个操作 用于更新协作参与者 SWUpdateCollaborationParticipantsAction 包含参与者的 加密身份 该身份来自上一步启动操作完成后 得到的协作标识符 请将与内容相关的身份 存储在您的服务器上 您将使用这些数据在接收设备上 验证访问权限 最后 操作完成后 会在 Messages 中发送通用链接 这个例子展示了 如何处理更新参与者操作 检索来自操作元数据的 协作标识符所使用的方法 在处理启动操作时 就得到了您填写的标识符 接下来 利用该操作的 addedIdentities 属性 可检索参与者数据 并将其存储在您的服务器上 每个身份都有一个 称为根哈希的 Data 属性 这是您应存储在服务器上的数据 稍后会用到 Lance 将为您详细介绍 Verifying Access 部分中有关此属性的内容 设置另一个服务器请求 这次是将 参与者添加到具有 目标标识符的协作中 和之前一样 将请求发送到您的服务器 要么操作完成 要么操作失败 这次 操作失败后不会有任何参数 既然您已经设置好协作 您的 App 就已万事俱备 可授予信息接收者 即时访问权限 接下来就由 Lance 为您介绍具体操作方法! Lance:谢谢 Devin 在这个部分 我将展示如何利用 您上一步存储在服务器上的身份数据 向接收者提供即时访问 SWPersonIdentity 上的 rootHash 属性 通常用来做此验证 rootHash 是唯一用于识别 参与者设备的安全值 为进行验证 您需要了解如何计算根哈希值 我来为您一一解释 发送协作消息时 实际上是单独发送到每个人的设备 Messages 利用加密公钥 识别每个设备 由于只允许 在这组设备上进行访问 才会从 每个接收者注册的 一组公钥中得到根哈希值 根哈希值是一个数据结构的根节点 该结构叫做梅克尔树 梅克尔树是一棵二叉树 通过执行一系列散列操作建造而成 为了根据用户公钥推导出其身份 这些公钥就被视为梅克尔树的叶子 梅克尔树中使用的散列算法确保 根节点只能从这组公钥中计算出来 本示例中 该用户有三台设备 三个公钥 对您的 App 所提供的 每个协作标识符来说 以上公钥都是唯一的 用的都是一个称为密钥多样化的程序 为了防止追踪用户注册的设备数量 这组公钥用随机密钥填充至一定大小 梅克尔树的叶节点由散列 填充的一组多样化密钥集合创建 SHA256 算法用于该树的 散列操作 然后 将每对叶节点连接起来 散列运算得出其父节点 在父节点上重复此过程 并再次重复至仅剩一个根节点 这个根哈希值仅用于表示 该接收者在其设备上的身份 请注意 根哈希值可用 一棵完整梅克尔树 上的节点子集合生成 这棵树中的根哈希值只需用 哈希值 H4 7 和 11 以及多样化的公钥 P3 即可再现 首先 对公钥进行散列运算 得到缺失的叶节点 H3 使用 H3 和 H4 生成 H8 使用给定的 H7 节点和 H8 生成 H10 最后 H10 和 H11 生成根哈希值 需要注意的是 无需重建整棵梅克尔树 您就可证明公钥 P3 可用于生成 给定的根哈希值 执行此操作所需的节点子集称为 proof of inclusion 您的 App 打开通用链接后 验证就开始了 为此 您首先需要检查 该链接为可协作状态 SWCollaborationHighlight 表示协作链接 可在 SWHighlightCenter 中检索 使用该链接 生成 proof of inclusion 为了表示 proof of inclusion 可使用一个名为 SWPersonIdentityProof 的类 为执行验证 您首先要生成 该对象以及加密签名 发送到您的服务器 使用 getSignedIdentityProof 方法 在 SWHighlightCenter 上检索证明 这需要一个 SWCollaborationHighlight 以及一些要由设备签名的任意数据 使用签名确保请求无法被 不良行为者重现以访问您的协作 数据可能是您从服务器上请求的挑战 或设备上生成的随机数值 此示例使用挑战方法讲解 URL 传递给您 App 上的 UIApplicationDelegate 方法 该 URL 是与协作有关的 通用链接 该 URL 用于从 SWHighlightCenter 获取相关 SWCollaborationHighlight 接下来 我将向我的服务器请求挑战 并将我得到的数据 和高亮部分一起传递给 SWHighlightCenter 上的 getSignedIdentityProof 方法 此方法会返回签名后的身份证明 稍后我将与您讨论服务器 验证该数据的操作 现在我可以将签名后的证明 发送到我的服务器进行验证 最后 我用该结果更新用户界面 App 将证明连同公钥和 签名数据发送到服务器 该数据使用 P-256 椭圆曲线上的 椭圆曲线数字签名算法进行签名 用 SHA256 作为哈希函数 用身份证明中的公钥 验证数据上的签名 您可用最常用的加密库 做到这一点 验证签名后 您可信任该身份证明是由 与该公钥相关的设备发送 接下来 您可使用 身份证明重新计算根哈希值 用我们之前看过的梅克尔树为例 可了解 SWPersonIdentityProof 中包含的内容 我们来用它来 重建梅克尔树的根哈希值 公钥是 P3 包含哈希值 H4 7 和 11 本地密钥索引为 2 表示 树中公钥的位置 以下示例展示了从证明上的属性 重构一个根哈希值的方式 递归算法在处理树数据结构时 效果很好 因此我在这里使用此算法 初次调用时 传入公钥的哈希值 包含哈希集和和公钥索引 接下来 第一个包含哈希被导出 检查公钥索引以查看密钥是否 位于其他密钥的左侧或右侧 选定的哈希值按正确的顺序 连接起来 然后进行散列 接下来 inclusionHashes 数组中的消费节点 被移除 其余的被传递给 同一函数的递归调用 公钥索引也一起更新 以便为树中的下一个节点做好准备 通过这个简单的函数 您可以快速计算 给定身份证明的根哈希值 服务器现在可以 检查这个生成的根哈希值 是否位于发送时上传的文档所有者的 根哈希列表中 哈希值存在于已知哈希列表中 因此服务器 可以授予对文档的访问权限 现在您可放心授予 对文档的访问权限! 回顾一下您验证身份所需的步骤 首先 在处理其通用链接时 为您的内容查找协作高亮部分 其次 签署某些数据 并检索 proof of inclusion 将签名后的数据和 证明发送到您的服务器 验证数据上的签名 使用 proof of inclusion 生成根哈希值 最后 将根哈希值和与该内容相关的 已知身份列表进行比较 现在您已经了解了 有关验证您的协作链接 访问权限的所有信息 我再来介绍一下 如何使用 Messages 协调参与者更改 当 Messages 组中的 参与者发生变化 且该组处于协作状态时 用户可以选择将更改传至您的 App 就传在消息线程中的横幅上 在此情况下 您的 App 会收到另一个 SWUpdateCollaborationParticipantsAction 其中包含添加和移除了的身份 您将使用在设置 协作时编写的相同代码 来处理此操作 但您还需要处理被移除的参与者 关于移除操作 只需查找与被移除身份 相关的任何账户 撤销其访问权限 如果还没有关联帐户 只需从数据库中删除根哈希值 这就是实现 Devin 之前 提到的更新参与者的操作 这个例子使用 操作上的移除身份属性 并将其传递给 类似的删除 API 请求 请注意 此代码仅处理已移除的身份 但一个完整的操作应该可以处理 已添加和已移除的身份 以上就是您处理 参与者更改时所需的全部操作! 最后 当对协作进行更改时 您的 App 会 发布有关这些更改的通知 直接显示在 Messages 中 接下来这部分我会为大家介绍 几种通知支持的类型 通知在共享链接的 对话中显示为横幅 横幅包括对更改内容的描述 以及变更人信息 在这个对话中 Charlie 对“烘焙食谱”文档 进行了编辑 点击显示按钮可直接将其 与内容联系起来 为了表示通知 SharedWithYou 框架 有一个名为 SWHighlightEvent 的协议 使用从 SWHighlightCenter API 检索的 SWHighlights 初始化高亮事件 Messages 可支持以下几类事件 内容更新或评论的更改事件 参与者加入或离开时的成员关系事件 在协作中提及用户时的提及事件 以及内容被移动 或删除时的持久性事件 以下示例展示如何对协作编辑 发布更改事件 使用高亮中心 API 为目标标识符 检索协作高亮部分 请记住 此标识符是您在 协作启动期间定义的 因此您的 App 应该有此标识符 以便在进行内容更改时使用 接下来 创建一个高亮更改事件实例 初始化器有高亮 和一个触发器枚举值 在本例中 将其设置为编辑类型 最后 再次使用高亮中心 发布该事件的通知 类似地 对于会员资格变更 会发布会员资格事件 这次是传递 addedCollaborator 或 removedCollaborator 触发器类型 接下来 如果您的 App 支持用户提及 您可以发布提及事件 利用被提及用户的 根哈希值初始化个人身份 请您回顾一下 您在验证访问权限时 用您 App 中的用户帐户 关联了一个人的身份 然后 以同样的方式发布提及事件 这次是将被提及身份作为参数传递 此通知在 Messages 中 仅显示给被提及用户 最后 当内容被移动 重命名 或删除时 使用持久性事件类型 在此 以重命名触发器类型为例 表示用户更改了内容的名称 这就是您的 App 能通知协作者的方式 以上都将在 Messages 中得到更新 Devin:这样一来 您就可以 通过以下几个步骤将您 App 的 协作体验与消息结合起来 即设置要协作共享的内容 以加密方式验证参与者的访问权限 追踪参与者变化 并在 Messages 中 发布通知将您的用户 与内容联系起来 请务必观看 “使用 Messages 增强协作体验”的视频 了解您可为协作显示的 新 UI 元素 Lance:我们迫不及待地 想与您的 App 开始协作了! Devin 和 Lance 您的加密签名协作伙伴 Devin:感谢收看! ♪
-
-
4:21 - Configure SWCollaborationMetadata
let localIdentifier = SWLocalCollaborationIdentifier(rawValue: "identifier") let metadata = SWCollaborationMetadata(localIdentifier: localIdentifier) metadata.title = "Content Title" metadata.initiatorHandle = "user@example.com" let formatter = PersonNameComponentsFormatter() if let components = formatter.personNameComponents(from: "Devin") { metadata.initiatorNameComponents = components } metadata.defaultShareOptions = ...
-
6:34 - Configure SWCollaborationShareOptions
let permission = SWCollaborationOptionsPickerGroup(identifier: UUID().uuidString, options: [ SWCollaborationOption(title: "Can make changes", identifier: UUID().uuidString), SWCollaborationOption(title: "Read only", identifier: UUID().uuidString) ]) permission.options[0].isSelected = true permission.title = "Permission" let additionalOptions = SWCollaborationOptionsGroup(identifier: UUID().uuidString, options: [ SWCollaborationOption(title: "Allow mentions", identifier: UUID().uuidString), SWCollaborationOption(title: "Allow comments", identifier: UUID().uuidString) ]) additionalOptions.title = "Additional Settings" let optionsGroups = [permission, additionalOptions] metadata.defaultShareOptions = SWCollaborationShareOptions(optionsGroups: optionsGroups)
-
7:58 - SWCollaborationMetadata SwiftUI TransferRepresentation
struct CustomCollaboration: Transferable { var name: String static var transferRepresentation: some TransferRepresentation { ProxyRepresentation { customCollaboration in SWCollaborationMetadata( localIdentifier: .init(rawValue: "com.example.customcollaboration"), title: customCollaboration.name, defaultShareOptions: nil, initiatorHandle: "johnappleseed@apple.com", initiatorNameComponents: nil ) } } }
-
8:16 - Using a collaboration metadata TransferRepresentation with ShareLink
struct ContentView: View { var body: some View { ShareLink(item: CustomCollaboration(name: "Example"), preview: .init("Example")) } }
-
9:08 - iOS Share Sheet
func presentActivityViewController(metadata: SWCollaborationMetadata) { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) let activityConfig = UIActivityItemsConfiguration(itemProviders: [itemProvider]) let shareSheet = UIActivityViewController(activityItemsConfiguration: activityConfig) present(shareSheet, animated: true) }
-
9:42 - iOS Drag and Drop
func createDragItem(metadata: SWCollaborationMetadata) -> UIDragItem { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) return UIDragItem(itemProvider: itemProvider) }
-
9:58 - macOS Sharing Popover
func showSharingServicePicker(view: NSView, metadata: SWCollaborationMetadata) { let itemProvider = NSItemProvider() itemProvider.registerObject(metadata, visibility: .all) let picker = NSSharingServicePicker(items: [itemProvider]) picker.show(relativeTo: view.bounds, of: view, preferredEdge: .minY) }
-
10:18 - macOS Drag and Drop NSPasteboardItem extension
func createPasteboardItem(metadata: SWCollaborationMetadata) -> NSPasteboardItem { let pasteboardItem = NSPasteboardItem() pasteboardItem.collaborationMetadata = metadata return pasteboardItem }
-
11:22 - Set up SWCollaborationCoordinator
private let collaborationCoordinator = SWCollaborationCoordinator.shared func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { // Conform to the SWCollaborationActionHandler protocol collaborationCoordinator.actionHandler = self }
-
12:27 - SWStartCollaborationAction
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWStartCollaborationAction) { let localID = action.collaborationMetadata.localIdentifier.rawValue let selectedOptions = action.collaborationMetadata.userSelectedShareOptions let prepareRequest = APIRequest.PrepareCollaboration(localID: localID, selectedOptions) Task { do { let response = try await apiController.send(request: prepareRequest) let identifier = response.deviceIndependentIdentifier action.fulfill(using: response.url, collaborationIdentifier: identifier) } catch { Log.error("Caught error while preparing the collaboration: \(error)") action.fail() // cancels the message } } }
-
13:40 - SWUpdateCollaborationParticipantsAction
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWUpdateCollaborationParticipantsAction) { let identifier = action.collaborationMetadata.collaborationIdentifier let participants: [Data] = action.addedIdentities.compactMap { $0.rootHash } let addParticipants = APIRequest.AddParticipants(identifier: identifier, participants) Task { do { try await apiController.send(request: addParticipants) action.fulfill() // sends the URL provided by the start action } catch { Log.error("Caught error while adding participants to collaboration: \(error)") action.fail() // cancels the message } } }
-
19:12 - Retrieve a signed identity proof for a highlight
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let highlightCenter: SWHighlightCenter = self.highlightCenter let challengeRequest = APIRequest.GetChallengeData() Task { do { let highlight = try highlightCenter.collaborationHighlight(for: url) let challenge = try await apiController.send(request: challengeRequest) let proof = try await highlightCenter.getSignedIdentityProof(for: highlight, using: challenge.data) let proofOfInclusionRequest = APIRequest.SubmitProofOfInclusion(for: proof) let result = try await apiController.send(request: proofOfInclusionRequest) documentController.update(currentDocument, with: result) } catch { Log.error("Caught error while generating proof of inclusion: \(error)") } } }
-
21:20 - Example code for root hash generation
func generateRootHashFromArray(localHash: SHA256Digest, inclusionHashes: [SHA256Digest], publicKeyIndex: Int) -> SHA256Digest { guard let firstHash = inclusionHashes.first else { return localHash } // Check if the node is the left or the right child let isLeft = publicKeyIndex.isMultiple(of: 2) // Calculate the combined hash var rootHash: SHA256Digest if isLeft { rootHash = hash(concatenate([localHash, firstHash]), using: .sha256) } else { rootHash = hash(concatenate([firstHash, localHash]), using: .sha256) } // Recursively pass in elements and move up the Merkle tree let newInclusionHashes = inclusionHashes.dropFirst() rootHash = generateRootHashFromArray( localHash: rootHash, inclusionHashes: Array(newInclusionHashes), publicKeyIndex: (publicKeyIndex / 2) ) return rootHash }
-
24:12 - SWUpdateCollaborationParticipantsAction - removing participants
func collaborationCoordinator(_ coordinator: SWCollaborationCoordinator, handle action: SWUpdateCollaborationParticipantsAction) { // Example of removing participants only. Handle the added identities here too. let identifier = action.collaborationMetadata.collaborationIdentifier let removed: [Data] = action.removedIdentities.compactMap { $0.rootHash } let removeParticipants = APIRequest.RemoveParticipants(identifier: identifier, removed) Task { do { try await apiController.send(request: removeParticipants) action.fulfill() } catch { log.error("Caught error while adding participants to collaboration: \(error)") action.fail() } } }
-
25:54 - Post an SWHighlightChangeEvent Notice
func postContentEditEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let editEvent = SWHighlightChangeEvent(highlight: highlight, trigger: .edit) highlightCenter.postNotice(for: editEvent) }
-
26:39 - Post an SWHighlightMembershipEvent Notice
func postContentEditEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let editEvent = SWHighlightChangeEvent(highlight: highlight, trigger: .edit) highlightCenter.postNotice(for: editEvent) }
-
26:50 - Post an SWHighlightMentionEvent Notice
func postMentionEvent(identifier: SWCollaborationIdentifier, mentionedRootHash: Data) throws { let mentionedIdentity = SWPerson.Identity(rootHash: mentionedRootHash) let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let mentionEvent = SWHighlightMentionEvent(highlight: highlight, mentionedPersonIdentity: mentionedIdentity) highlightCenter.postNotice(for: mentionEvent) }
-
27:23 - Post an SWHighlightPersistenceEvent Notice
func postContentRenamedEvent(identifier: SWCollaborationIdentifier) throws { let highlightCenter: SWHighlightCenter = self.highlightCenter let highlight = try highlightCenter.collaborationHighlight(forIdentifier: identifier) let renamedEvent = SWHighlightPersistenceEvent(highlight: highlight, trigger: .renamed) highlightCenter.postNotice(for: renamedEvent) }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。