You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS 13备忘录App文件夹共享实现及CloudKit容器应用技术咨询

我来拆解一下iOS 13备忘录的文件夹共享逻辑,以及如何基于公开CloudKit API复刻这个功能——毕竟苹果官方也明确说自家App用的是公开CloudKit能力,咱们完全可以做到:

一、iOS 13备忘录文件夹共享的核心逻辑

备忘录的文件夹共享本质是把文件夹对应的CloudKit记录(以及关联内容)共享给指定用户,同时处理权限管控、跨设备同步和实时更新。核心就是利用CloudKit的「共享记录(CKShare)」和「共享数据库(Shared Database)」这两个公开能力,没有什么神秘的内部API。

二、CloudKit容器的角色分工

CloudKit容器默认包含两个核心数据库,备忘录的共享功能就是基于这两个库的协作:

  • 私有数据库(Private Database):存储用户私人的文件夹、备忘录,只有当前用户能访问,是所有内容的“原始数据源”。
  • 共享数据库(Shared Database):专门存放被共享的内容,所有被授权的用户都能访问这里的记录。

当用户发起文件夹共享时,备忘录会做这几件事:

  1. 把文件夹的根记录(以及关联的所有备忘录记录)和私有数据库做关联,并同步一份到共享数据库。
  2. 创建一个CKShare对象,定义这个共享的权限(只读/可编辑)、共享用户列表,以及共享的显示名称(比如文件夹名)。
  3. 通过系统原生的共享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

火山引擎 最新活动