iOS 13备忘录App文件夹共享实现及CloudKit容器应用技术咨询
我来拆解一下iOS 13备忘录的文件夹共享逻辑,以及如何基于公开CloudKit API复刻这个功能——毕竟苹果官方也明确说自家App用的是公开CloudKit能力,咱们完全可以做到:
一、iOS 13备忘录文件夹共享的核心逻辑
备忘录的文件夹共享本质是把文件夹对应的CloudKit记录(以及关联内容)共享给指定用户,同时处理权限管控、跨设备同步和实时更新。核心就是利用CloudKit的「共享记录(CKShare)」和「共享数据库(Shared Database)」这两个公开能力,没有什么神秘的内部API。
二、CloudKit容器的角色分工
CloudKit容器默认包含两个核心数据库,备忘录的共享功能就是基于这两个库的协作:
- 私有数据库(Private Database):存储用户私人的文件夹、备忘录,只有当前用户能访问,是所有内容的“原始数据源”。
- 共享数据库(Shared Database):专门存放被共享的内容,所有被授权的用户都能访问这里的记录。
当用户发起文件夹共享时,备忘录会做这几件事:
- 把文件夹的根记录(以及关联的所有备忘录记录)和私有数据库做关联,并同步一份到共享数据库。
- 创建一个
CKShare对象,定义这个共享的权限(只读/可编辑)、共享用户列表,以及共享的显示名称(比如文件夹名)。 - 通过系统原生的共享UI发送邀请,其他用户接受后,CloudKit会自动把共享内容同步到他们的共享数据库里。
三、具体实现步骤(复刻备忘录的共享功能)
下面是基于公开API的落地流程,你可以直接参考:
1. 先搞定CloudKit数据模型
首先在CloudKit Dashboard里设计对应的记录类型:
- FolderRecord:字段包括文件夹名称、创建时间,还要加一个「关联类型」字段,用来关联所有子备忘录的
NoteRecord。 - NoteRecord:字段包括备忘录内容、修改时间,同样加一个关联字段指向所属的
FolderRecord。
重要:一定要给这两个记录类型开启「可共享」属性(在Dashboard的记录类型设置里勾选),否则无法创建共享。
2. 发起文件夹共享
当用户点击“共享文件夹”按钮时,执行以下操作:
- 先从私有数据库里获取要共享的
FolderRecord实例。 - 创建
CKShare对象,设置共享的基本信息和权限:// 假设folderRecord是从私有库拿到的文件夹记录 let share = CKShare(rootRecord: folderRecord) // 设置共享显示名称(会在受邀用户的列表里显示) share[CKShareTitleKey] = folderRecord["name"] as? CKRecordValue // 设置权限:.readWrite是可编辑,.readOnly是只读 share.permission = .readWrite // 把文件夹记录和共享对象一起保存到共享数据库 let saveOperation = CKModifyRecordsOperation(recordsToSave: [folderRecord, share], recordIDsToDelete: nil) saveOperation.database = CKContainer.default().sharedCloudDatabase saveOperation.modifyRecordsCompletionBlock = { savedRecords, deletedIDs, error in if let error = error { // 处理保存失败,比如提示用户 print("创建共享失败:\(error.localizedDescription)") return } // 保存成功,打开系统共享UI发送邀请 self.presentSharingUI(with: share) } CKContainer.default().add(saveOperation) - 用苹果原生的
UICloudSharingController来展示邀请UI,这个控制器会自动处理邀请发送、权限选择等逻辑:
记得实现func presentSharingUI(with share: CKShare) { let sharingController = UICloudSharingController(share: share, container: CKContainer.default()) sharingController.delegate = self // 允许受邀用户选择的权限(可选) sharingController.availablePermissions = [.allowReadOnly, .allowReadWrite] // 适配iPad的弹出样式 if let popover = sharingController.popoverPresentationController { popover.sourceView = self.shareButton popover.sourceRect = self.shareButton.bounds } present(sharingController, animated: true) }UICloudSharingControllerDelegate的必要方法,比如处理共享完成或取消的逻辑。
3. 处理共享邀请与内容同步
当受邀用户接受邀请后,CloudKit会自动把共享的文件夹和备忘录同步到他们的共享数据库里。你需要做两件事:
- 监听共享数据库变更:用
CKQuerySubscription或者CKFetchDatabaseChangesOperation来监听共享数据库的记录变化,一旦有新的共享内容或修改,就实时更新App的UI。 - 加载共享内容:当用户打开共享文件夹列表时,从共享数据库里查询所有关联的
FolderRecord,再根据关联字段加载对应的NoteRecord。
4. 权限管理(和备忘录一致)
共享发起者可以修改参与者的权限,或者移除参与者,这部分也是基于公开API实现:
- 获取
CKShare对象的participants数组,找到对应的参与者,修改其permission属性。 - 把更新后的
CKShare保存回共享数据库:// 假设要把某个参与者改成只读权限 if let participant = share.participants.first(where: { $0.userIdentity.email == "user@example.com" }) { participant.permission = .readOnly let updateOperation = CKModifyRecordsOperation(recordsToSave: [share], recordIDsToDelete: nil) updateOperation.database = CKContainer.default().sharedCloudDatabase updateOperation.modifyRecordsCompletionBlock = { _, _, error in // 处理更新结果 } CKContainer.default().add(updateOperation) }
5. 私有与共享内容的同步
备忘录里的文件夹被共享后,用户在私有文件夹里修改内容,共享用户能实时看到——这是因为CloudKit会自动同步关联的记录。你需要确保:
- 私有数据库里的
NoteRecord和共享的FolderRecord保持正确的关联。 - 修改私有记录时,CloudKit会自动同步到共享数据库(只要记录的共享状态正确)。
四、几个关键注意事项
- 容器配置:在Xcode里要确保你的App启用了CloudKit,
Info.plist里添加NSUbiquitousContainers配置,并且填写正确的容器ID。 - 权限请求:别忘了在
Info.plist里添加NSCloudKitUsageDescription,说明App使用iCloud的目的,否则会被系统拒绝权限。 - 冲突处理:当多个用户同时修改同一条共享记录时,CloudKit会返回冲突,你需要在App里实现冲突解决逻辑(比如提示用户选择版本,或者自动合并文本内容)。
- 离线支持:CloudKit自带离线操作能力,用户离线时修改的内容会在联网后自动同步,你需要在UI上给用户提示离线状态。
内容的提问来源于stack exchange,提问作者Congruent Tech. UG




