大多数浏览器和
Developer App 均支持流媒体播放。
-
充分利用 CloudKit 共享
探索各个 app 如何使用 CloudKit 互相分享记录。我们将向您展示如何鼓励用户使用您的 app 展开协作,以及如何利用各个 Apple 框架为那些互动提供支持。了解如何创建和管理共享,探索公共权限等共享选项,以及探索如何使用 iOS 15 和 macOS Monterey 中的区域共享来共享整个数据记录区。为了充分利用这场讲座中介绍的知识,我们建议熟悉 CloudKit 并大概了解记录和数据类型。
资源
相关视频
WWDC23
WWDC21
-
下载
Simon Manning:嗨 我是 Simon 是 CloudKit 团队 的一名工程师 今天我将为大家演示 如何使用 CloudKit 支持在 app 用户之间共享数据 我首先会介绍我们的 Sharing 示例 app 它可在 GitHub 获得 然后逐步演示如何支持创建共享 并邀请其他用户在 app 中 参与这些共享 我将探讨 app 如何能够代表 参与的用户共享和接受邀请 如何获取共享的记录 如何让 app 提供自定共享体验 最后我将介绍 CloudKit 中 提供的基于区域的共享模式 首先是 Sharing 示例 app 友情提醒一下 CloudKit 是一种框架 能让您的 app 访问 iCloud 上的数据库 这在 API 中作为 CKContainer 进行公开 通过它您可以访问 多个 CKDatabase 每个容器有一个公共数据库 所有用户都有可能在 其中读取和写入记录 如果设备上 登录了 iCloud 帐户 您的 app 也能 访问私有数据库 其中会含有用户的数据 如果您的 app 支持共享 那么与当前 iCloud 用户 共享的数据会在 其共享 CKDatabase 中 提供给您的 app 为了演示如何 从拥有记录的用户发起共享 以及作为参与者接受共享邀请 我将引用一个示例 app 它可通过 Apple GitHub 帐户获取 Sharing 是通讯录 app 的 一个最简示例 它支持创建新的联系人记录 保存在用户的私有数据库中 与 app 的其他用户 共享单独的联系人 以及显示其他人共享给 当前用户的所有联系人 Sharing 的储存库 包含使用 Swift 并发 的实现以及基于 CompletionHandler 的实现 它们通过单独的分支提供 要开始共享 首先要 新建一条联系人记录 将它保存到所有者 的私有数据库中 addContact 函数 由 Sharing 的 Add New Contact UI 调用 这将创建一条新的 CKRecord 其 recordType 为 Contact 再存储姓名和电话号码 这条记录随后会 保存到私有数据库中 要与 Sharing app 的其他用户共享联系人记录 首先要创建共享 CloudKit 中的共享依赖于 一种专门类型的记录 称为 CKShare 当用户要与其他人共享记录时 您可以代表他们创建 CKShare 并将它保存到私有数据库中 由于此用户拥有 要共享的所有记录 因此也是共享记录本身的所有者 这一共享记录是事实来源 指定要共享哪些数据 与谁共享这些数据 以及共享参与者拥有什么权限 共享权限就是共享记录的所有者 如何控制对共享记录的访问权限 publicPermission 属性 代表具有共享链接的 任何用户的权限级别 仅有 URL 的用户 默认没有访问权限 将此属性设为容许度更高的值 可允许具有共享 URL 的 任何用户参与共享 每个受邀参与者 也有一个权限级别 具体由共享记录的所有者决定 Participant 对象描述 具体用户参与共享的情况 包括用户的身份标识 和他们的邀请接受状态 权限是对各个 Participant 对象逐一设置的 并且每个参与者都会 添加到 CKShare 上的 participants 数组中 要开始从用户私有数据库共享记录 首先要确定共享中的根记录 根记录及其所有子记录 将包含在这个共享中 接着要创建 CKShare 记录 并将根记录和 CKShare 一起 保存到当前用户的私有数据库中 在 Sharing 中 createShare 函数 使用要共享的联系人记录 来初始化 CKShare CKShare 和根记录一起 保存到所有者的私有数据库中 CKShare 再返回给 UI 以启动邀请过程 CKShare 和根记录必须 一起保存在同一运算中 只保存 CKShare 会出错 即使根记录已存在于 CloudKit 中也不例外 共享根记录也会共享其子记录 现在您的 app 可以创建共享了 下面我来介绍如何配置和发送 参与共享的用户邀请 示例 app 使用了 UIKit 提供的视图控制器 名为 UICloudSharingController 在 app 中实现这个控制器 可让用户轻松邀请参与者 访问他们的 CKShare 设置权限 查看和管理参与 或彻底停止共享记录 这里演示了 在 Sharing 示例 app 中 共享新的联系人记录时 UICloudSharingController 是如何显示的 用户可以选择共享邀请的发送方式 也可以在 Share Options 部分中 配置权限 实施控制器的委派协议 以提供其他共享相关信息 以及接收共享事件 和错误的相关通知 除了这里介绍的方法外 还有各种方式来配置 共享邀请中显示的 项目标题、项目类型和缩略图 现在共享记录的所有者 已将邀请发送给其他人 您的 app 需要代表受邀 参与者处理和接收共享 在 app 的 Info.plist 中启用 CKSharingSupported 布尔值 这会告知系统 app 支持共享 并且应当处理邀请 URL 并调用相关的 delegate 函数 共享邀请被打开和接受时 您的 app 会通过 delegate 方法回调收到一个 CKShare.Metadata 对象 您的 app 使用此元数据 通过容器上的 accept 函数 或通过 CKAcceptSharesOperation 告知 CloudKit 当前用户接受并参与此共享 在 applicationDelegate 的生命周期里 系统会启动您的 app 并调用 userDidAcceptCloudKitShareWith: shareMetadata delegate 方法 使用 shareMetadata 对象中 提供的标识符 初始化 CKContainer 对象 然后对 CKContainer 调用 accept 函数 传入 shareMetadata 对象 以通知 CloudKit 当前的用户已经接受共享 现在您的 app 可以发送 私有记录共享邀请 并且处理和接受这些邀请了 下面我来介绍 app 如何获取和修改用户 正在参与共享的记录 与当前 iCloud 用户 共享的记录在 app 容器的 sharedCloudDatabase 内提供 用于获取、查询和操作 私有和公共数据库内数据的 相同 CloudKit API 也可用于共享的数据库 示例 app 中的共享记录 在 fetchSharedContacts 函数中获取 它使用 recordZoneChanges 运算 来逐步获取和处理更改 每次完成此运算时 它会返回 moreComing 布尔属性 其表示是否有更多更改要获取 它也会返回 changeToken 这是该区域历史记录中 的具体更改的指针 使用这个 changeToken 也可以仅获取 在那个时间点后发生的更改 对于第一次获取 或要重新获取区域 历史记录中的所有更改 可将 changeToken 设为 nil 对活跃共享的管理取决于当前用户 对于共享记录的所有者 如果要停止共享 可以将 CKShare 记录 从所有者的私有数据库中删除 所有者也可彻底删除共享的根记录 CKShare 记录对象上的 removeParticipant 函数 为删除个别参与者提供了一种方法 可在 participants 属性中找到 最后 通过使用 UICloudSharingController UI 所有者可以停止 与所有参与者共享 对于参与共享的用户 若要停止参与 可以从用户的 共享数据库删除根 CKRecord 或者使用 UICloudSharingController UI 请注意 从用户的共享数据库 删除根 CKRecord 只会停止参与共享 原始的根记录仍然存在于 所有者的私有数据库中 虽然 Sharing 示例 app 使用 UICloudSharingController 来处理参与者查找和邀请 您也可以使用 CloudKit API 来实现此功能并且为共享记录的 所有者和参与者都打造自定用户体验 首先 若要搜索需要邀请到 CKShare 中的参与者 可以将 CKFetchShare ParticipantsOperation 或 shareParticipants 函数 用于相关的 CKContainer 电话号码、电子邮件 和用户记录 ID 都是受支持的参数 接下来 设置此项参与的 具体权限级别 并使用 addParticipant 函数 将参与者添加到 CKShare 中 务必将修改后的 CKShare 重新保存到私有数据库中 保存的 CKShare 记录 具有一个 url 属性 可以发送给受邀参与者 以便接受和处理 可以使用邀请 URL 来获取共享记录的 CKShare.Metadata 并通过 app 容器上的 accept(shareMetadata) 函数 来确认参与共享 Sharing app 演示了 采用分层共享模式的 CloudKit 共享 此模式将一条记录 作为根记录共享 同时也共享该记录 的所有子记录 区域共享是 iOS 15 中 引入的新模式 允许共享完整的记录区域 共享区域时 会共享区域中的所有记录 而不只是一条记录的层次结构 由于区域共享作用于 记录区域中的所有记录 这种类型的共享无法与 同一记录区域中的分层共享共存 您要么在一个区域中 使用一个或多个分层共享 要么使用一个区域范围的共享 区域共享和分层共享 各有各的优势 应当根据具体用例加以使用 在 Sharing 示例 app 的情景中 如果将联系人群组 整理到单独的记录区域中 那就可以使用区域共享 可以通过使用区域共享 与其他用户共享这些联系人群组 要进一步了解区域共享 请观看 WWDC21 中的 “CloudKit 中的新功能” 要创建新的区域范围共享 可以使用要共享的 自定记录区域 ID 进行初始化并 保存到私有数据库中 用于接受共享邀请以及 从共享数据库获取记录的过程 与前面介绍的相同 要判断共享是不是区域范围共享 请检查其 recordID 的 recordName 属性 如果值是 CKRecordNameZoneWideShare 那么此共享管理的 是共享记录区域 否则管理的是共享记录层次结构 现在您已知道如何 在 app 中支持共享 开始创建 CKShare 记录 并允许用户与其他人协作 立即使用 UICloudSharingController 着手处理您的共享 UI 或构建符合自己需求的自定实现 使用熟悉的 Fetch 和 Modify API 从共享云数据库访问共享记录 现在就开始在 app 中 探索新的共享用例 从实现区域共享中充分获益吧 谢谢观看!
-
-
1:58 - Create a new Contact record
// Create a new Contact record func addContact(name: String, phoneNumber: String) async throws { let id = CKRecord.ID(zoneID: recordZone.zoneID) let contactRecord = CKRecord(recordType: "Contact", recordID: id) contactRecord["name"] = name contactRecord["phoneNumber"] = phoneNumber try await privateCloudDatabase.save(contactRecord) }
-
4:09 - Preparing a CKShare
// Preparing a CKShare func createShare(contactRecord: CKRecord) async throws -> CKShare { let share = CKShare(rootRecord: contactRecord) try await privateCloudDatabase.modifyRecords( saving: [contactRecord, share], deleting: [] ) return share }
-
5:25 - UICloudSharingControllerDelegate
// UICloudSharingControllerDelegate public protocol UICloudSharingControllerDelegate { // ... // Called after the CloudKit sharing controller failed to save the share record. func cloudSharingController(UICloudSharingController, failedToSaveShareWithError: Error) // Called after the CloudKit sharing controller saves the share record. func cloudSharingControllerDidSaveShare(UICloudSharingController) // Called after the user decided to stop sharing the record. func cloudSharingControllerDidStopSharing(UICloudSharingController) }
-
6:27 - Processing an accepted share invitation
// Processing a user’s acceptance of a share invitation func application( _ application: UIApplication, userDidAcceptCloudKitShareWith shareMetadata: CKShare.Metadata ) { let container = CKContainer(identifier: shareMetadata.containerIdentifier) Task { do { try await container.accept(shareMetadata) } catch { // Handle errors that may occur } } }
-
7:24 - Fetching shared records
// Fetching records shared with the current iCloud user func fetchSharedContacts(in zone: CKRecordZone) async throws { var changeToken: CKServerChangeToken? = nil var moreChangesComing = true while moreChangesComing { let changes = try await sharedCloudDatabase.recordZoneChanges( inZoneWith: zone.zoneID, since: changeToken ) // Process changes as needed (modifications and deletions) processChanges(changes) moreChangesComing = changes.moreComing changeToken = changes.changeToken } }
-
9:16 - Search: Phone Number
// Search by phone number let phoneNumber = "417-555-9311" let participant = try await container.shareParticipant(forPhoneNumber: phoneNumber)
-
9:16 - Search: Email Address
// Search by email address let emailAddress = "dave_knox@icloud.com" let participant = try await container.shareParticipant(forEmailAddress: emailAddress)
-
9:16 - Search: Record ID
// Search by user record ID let participant = try await container.shareParticipant(forUserRecordID: recordID)
-
9:32 - Add participant to a share
// Add participant to existing CKShare record func addParticipant(_ participant: CKShare.Participant, to share: CKShare) async throws { participant.permission = .readWrite share.addParticipant(participant) try await privateCloudDatabase.save(share) }
-
9:47 - Confirm invitation acceptance
// Fetch CKShare.Metadata and confirm accepting share from a given URL func confirmShareParticipation(from url: URL) async throws { let shareMetadata = try await container.shareMetadata(for: url) try await container.accept(shareMetadata) }
-
11:14 - Share an entire record zone
// Create a CKShare sharing an entire record zone func createAndSaveShare(for zone: CKRecordZone) async throws -> CKShare { let share = CKShare(recordZoneID: zone.zoneID) try await privateCloudDatabase.save(share) if share.recordID.recordName == CKRecordNameZoneWideShare { // This is managing a shared record zone } return share }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。