最近给落格输入法加入了一个叫做“对数云”的东西,其实不难,比使用 iCloud Document 要简单,不过网上的资料不太多,你通过那些上手教程来现充应该不是问题,但想要提升用户体验,就不是那么容易了。这里我们就一起来看看,怎么样才能让 CloudKit 运行得更畅快。
CKDatabaseOperation
一般来说,你获取一条数据可能是这样的:
1 2 3 4 |
let publicDatabase = CKContainer(identifier: "iCloud.com.xxx").publicCloudDatabase publicDatabase.fetch(withRecordID: recordId) { (record, _) in .... } |
使用 fetch 方法足够简单,但是它是一个便捷选项,默认的话它以极低的优先级执行,这时候你应该知道, CloudKit 的网络通信不是由你的程序决定的,而是由 ios 来处理的,所以,这样低的优先级,会导致你的数据获取速度非常之慢,如果是稍微大一点的(比如1M大小的图片)数据,那么你可能会等待菊花到以为你的程序死机了。
现在你终于知道究竟是什么让你的 CloudKit 这么慢了。这还真不是 iCloud 的锅。
所以,这个时候使用手工设定的优先级来强制获取是个不错的选择——当然了,考虑到大多数情况下我们不是在后台慢慢获取而是需要立即呈现给用户对吧?
那么你可以考虑使用 CKDatabaseOperation 的子类,它允许你创建一系列的任务,比如上传、下载、删除等等,继续先前的例子,这里我们来获取一条记录(或者多条):
1 2 3 4 5 6 7 |
let publicDatabase = CKContainer(identifier: "iCloud.com.xxx").publicCloudDatabase let operation = CKFetchRecordsOperation(recordIDs: [recordId]) operation.queuePriority = .veryHigh operation.fetchRecordsCompletionBlock = { (records,e) in ... } publicDatabase.add(operation) |
这里我们创建了一个 CKDatabaseOperation 的子类, CKFetchRecordsOperation ,它接收一个 [CKRecordID] 类型的数组,也就是说,如果你想的话,可以一次性查询多个记录。这个类的处理方式与你熟悉的直接传入闭包不太相同,但灵活性更高,你有三个选择:
-
perRecordProgressBlock
这个会根据你查询的每一个记录来返回进度,如果你想要进度条的话; -
perRecordCompletionBlock
这个会在每一个记录获取成功后调用; -
fetchRecordsCompletionBlock
这个在最终任务完成后调用,会返回一个 [CKRecordID : CKRecord] 类型的字典供你获取记录的具体内容。
将你要执行的闭包传入这三个属性,它们会在合适的时候被调用。
总之,最后我们还要设置一下这个任务的优先级,以便它在子线程中运行时得到足够的资源:
1 |
operation.queuePriority = .veryHigh |
这样,你的下载就明显变快了。
对于上传(修改和删除),你可以使用 CKModifyRecordsOperation ,使用的方式大同小异,要保存的放在保存的形式参数里,要删除的,就放在要删除的形式参数里:
1 2 3 |
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [self.record!.recordID]) operation.queuePriority = .high self.publicDatabase.add(operation) |
只要有用的字段
一般来说一条数据并不大,不过,CloudKit 是非关系型数据库,所以每一条记录都可以有一个或者多个字段用来做为附件——对于对数云来说,附件则放置了对应的码表配置文件,上文说了,如果直接获取这条记录,那么ios会“好心”地帮你把附件也一起撸到本地,如果是一条还好,我们提高了获取的优先级,可是,如果要展现一个列表呢??♂️简直就是灾难。
所以,我们有必要在获取的时候把不需要的字段(尤其是比较大的附件字段)排除掉,只有在需要的时候才下载完整的字段内容,不是吗?
这里还是需要用到上文中的 CKDatabaseOperation ,它有一个子类是 CKQueryOperation ,当你写好了 CKQuery ,不要急着去执行,把它放到 CKQueryOperation 里,设置优先级,以及一个叫做 desiredKeys 的属性,把你想要获取的字段名写进去就好啦:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
let predicate = NSPredicate(value: true) let query = CKQuery(recordType: recordType, predicate: predicate) query.sortDescriptors = [NSSortDescriptor(key: "DownloadTimes", ascending: false)] let queryOperation = CKQueryOperation(query: query) queryOperation.desiredKeys = ["Description","Name","DownloadTimes","UploadUserName"] queryOperation.queuePriority = .high queryOperation.recordFetchedBlock = {(record) in ... } queryOperation.completionBlock = { ... } publicDatabase.add(queryOperation) |
这样,就避开了比较大的附件类,如此一来,即使是获取几百条数据,也能轻松响应,同时,由于可以轻松获取到数据了,我们也就可以做到速度极快的本地查询,当需要具体下载附件的时候,再加载特定的完整记录即可。
权限问题
这里要说明,在 CloudKit DashBoard 里,权限分为三类,它们分别是:
- World
- Authenticated
- Creator
World 很好理解,就是所有人;Creator 也不难,就是文件的创建者;这个 Authenticated 就有点晕了——它是指所有登录了 iCloud 账号的人。
这里对应的, World 就是没有登录 iCloud 账号的人。
所以,如果你在一条记录里加入了比如“下载量”这样的计数器,那么你就应该给“Authenticated”加入写的权限,当然了,这样有点不安全,不过对数云这里意在公开分享,所以就还好了,如果你有更严格的安全需求,那么你应该单独去设计一下角色问题。如果不给写的权限(默认没有),那么其他用户的下载计数就将不能更新。
对于 CloudKit 来说,如果你的用户没有登录 iCloud ,那也是可用的,只是没有了写的权限,所以这些人的下载不会被记录数字了——因为没有权限更新。
CloudKit 的生产环境
初次使用 CloudKit 的开发者可能会头疼,这个蓝色的开发环境和绿色的生产环境到底区别在哪里? ——最明显的区别:你用 Xcode 直接编译运行的版本都会默认连接到开发版本里,而你上传到 iTunes Connect 里的程序则默认连接到生产环境。
值得注意的是,即使是 TestFlight ,也使用的是生产环境,只有你自己使用的是开发环境。
另外,这两个环境里的内容是独立的,你在开发环境里测试的所有内容不会一起部署到生产环境当中,只有字段、角色等配置会部署过去。
你可以这样理解,你在本地搭建了一个数据库,写好了表,做好了配置,然后测试运行通过——这时候你要到服务器上去配置了,你要拿过去什么呢?你的配置,你的表——不包括你的测试数据,对吧?
本文由 落格博客 原创撰写:落格博客 » CloudKit 优化指南
转载请保留出处和原文链接:https://www.logcg.com/archives/2453.html