最近給落格輸入法加入了一個叫做“對數雲”的東西,其實不難,比使用 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 很好理解,就是所有人;Creator 也不難,就是文件的創建者;這個 Authenticated 就有點暈了——它是指所有登錄了 iCloud 賬號的人。
這裡對應的, World 就是沒有登錄 iCloud 賬號的人。
所以,如果你在一條記錄裡加入了比如“下載量”這樣的計數器,那麼你就應該給“身份驗證”加入寫的權限,當然了,這樣有點不安全,不過對數雲這裡意在公開分享,所以就還好了,如果你有更嚴格的安全需求,那麼你應該單獨去設計一下角色問題。如果不給寫的權限(默認沒有),那麼其他用戶的下載計數就將不能更新。
對於 CloudKit 來說,如果你的用戶沒有登錄 iCloud ,那也是可用的,只是沒有了寫的權限,所以這些人的下載不會被記錄數字了——因為沒有權限更新。
CloudKit 的生產環境
初次使用 CloudKit 的開發者可能會頭疼,這個藍色的開發環境和綠色的生產環境到底區別在哪裡? ——最明顯的區別:你用 Xcode 直接編譯運行的版本都會默認連接到開發版本里,而你上傳到 iTunes Connect 裡的程序則默認連接到生產環境。
值得注意的是,即使是 TestFlight ,也使用的是生產環境,只有你自己使用的是開發環境。
另外,這兩個環境裡的內容是獨立的,你在開發環境裡測試的所有內容不會一起部署到生產環境當中,只有字段、角色等配置會部署過去。
你可以這樣理解,你在本地搭建了一個數據庫,寫好了表,做好了配置,然後測試運行通過——這時候你要到服務器上去配置了,你要拿過去什麼呢?你的配置,你的表——不包括你的測試數據,對吧?
本文由 落格博客 原創撰寫:落格博客 » CloudKit 優化指南
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/2453.html