iOS 8 CloudKit上手教程

CloudKit,是蘋果最新推出的基於iCloud的一個雲端數據存儲服務,提供了低成本的雲存儲並能做爲一個後端服務經過用戶們的iCloud帳號分享其應用數據。web

CloudKit主要由兩個部分組成:數據庫

  1. 一個儀表web頁面用於管理公開數據的記錄類型。編程

  2. 一組API接口用於iCloud和設備之間的數據傳遞。swift

CloudKit也具備安全性,爲用戶的私人數據提供了完整的保護。而開發者不只只能接入本身的數據庫,也不容許查看用戶的私有數據。後端

CloudKit適用於那些在服務端計算量不大,卻須要使用大量數據的iOS平臺獨佔應用。xcode

這篇教程將帶領你經過構建一個叫作BabiFüd的找餐廳應用獲取到有關CloudKit開發的實戰經驗。緩存

提示:這篇教程的實例要求你有一個激活的iOS開發者帳戶,不然你將沒有使用iCloud和CloudKit的權限。安全

爲何選擇CloudKit?服務器

在最開始,你也許會好奇爲何你要選擇CloudKit而非Core Data、商業後端服務或者用本身的服務器。網絡

答案有三方面:易操做性、可靠性、成本。

易操做

與其餘的後臺解決方案不一樣,CloudKit的設置至關容易。你無需選擇、配置、安裝服務或者爲縮放比率和安全性之類的問題焦慮。

簡單的註冊成爲iOS開發者你就能夠擁有使用CloudKit的資格,而不須要爲這個附加的功能單獨註冊或者從新申請帳號。做爲啓動CloudKit功能的一部分,全部必要的設置都難以想象的在服務器上自動完成了。

CloudKit的導入方式和其餘的iOS框架同樣不須要額外下載運行庫而且配置它們,框架自己提供了方便的API接口使通常的操做變得極爲簡易。

對於用戶而言,易操做性也是可體現的。因爲CloudKit採用了iCloud的證書認證,一旦用戶安裝以後(或者經過設置應用)就能直接進入,因此創建一個複雜的登陸界面是徹底沒有必要的。只要用戶登陸上去,那就能輕鬆使用你的應用了。

可靠

CloudKit的另外一大優點在於,只要用戶在開發者和蘋果之間更願意相信蘋果,那麼他們就沒必要爲本身私密數據的安全感到擔心,由於CloudKit隔離了用戶數據與和開發者。

雖然對於開發者來講缺少數據會有些失望(好比在調試的時候是很須要數據的),不過對於他們還是有好處的——至少不用再擔憂安全性,也不用去說服用戶信任你。甚至他們絲毫沒有意識到CloudKit和iCloud之間的區別,只要他們信任iCloud就意味着他們信任你。

成本

最後,對於每一個開發者來講運行一個服務須要至關大的耗費。就連是最便宜的主機服務也不會考慮你應用的價格來訂價,並且只要跑上哪怕一個應用,你就得掏錢。而CloudKit容許你免費的使用必定限額的空間來存儲公開數據,若是想了解更多你能夠看這裏

這些優點使得CloudKit能夠簡單無腦的服務於Mac或者iOS應用。

關於BabiFüd

本教程的示例程序是一個叫BabiFüd的應用,是一款最新的定位服務式的典型應用。總之,相較於在定位後側重於餐廳的食物質量、服務效率、價格以及兒童方面的用戶定位,這個應用更關注餐廳設施的變化、座位的溫馨度和健康飲食。

這款應用包含了四個標籤:附近地點的列表(Nearby),顯示附近地點的地圖(Map),用戶生成的記錄(Note)和應用設置(Setting)。你也能夠經過如下的兩張截圖來感覺如下這個應用大概是什麼樣子:

nearby.pngmap.png

這個應用的模型架構是在這些顯示視圖的內部調用了CloudKit,在這裏CloudKit對象稱爲記錄(records),模型中的主要記錄類型稱做Establishment,表明了你的應用裏那些各類各樣的地點。經過這種方式你還能夠在你的數據庫中添加相關的評分和備註信息。

開始    

開始教程以前你須要下載一個CloudKit項目

在你開始編碼以前你能夠更改你應用的Bundle Identifier和Team,你須要設置一個Team以後蘋果纔會給你提供CloudKit相關的功能使用權,而後一個惟一的Bundle Identifier會使得整個過程輕鬆許多。

像下圖這樣,在Xcode裏面打開BabiFud.xcodeproj。選擇項目導航裏的BabiFud項目,而後選擇目標BabiFud以後在Bundle Identifier裏填入一個你以爲不會重名的名稱,我推薦使用域名反序法,而後加上項目名稱,而後選擇一個合適的Team。

change_team_name1.png

請留意Bundle Identifier和Team。如今你須要爲你的應用設置CloudKit服務,而後建立一個容器來存儲你的應用數據。

權限和容器

在你經過應用添加任何數據以前你須要一個容器來存儲這些數據記錄。所謂的容器其實就是在服務器上爲應用數據假想一塊存儲空間,由共享數據和私密數據組成。建立容器意味着你的應用須要有使用CloudKit的權限。

在目標編輯器裏選擇Capabilities標籤,而後啓用iCloud,以下圖:

capabilities.png

這個時候Xcode可能會提示你登錄你已綁定開發者帳號的蘋果用戶,你只須要照作就是了。最後,在服務選項裏面勾上CloudKit選框。

這樣你就建立了一個名稱形如 iCloud.<你的應用的bundle id="">的默認容器。示例以下圖:

cloudkit_container.png

若是你在上面這些步驟碰見了什麼問題或者錯誤,這裏有一些狀況的解決方案:

1.在啓用iCloud那一步若是提示了錯誤或者警告的話,你能夠試着點擊最Fix Issue按鈕,這樣可能會須要等一段時間。

0_fix_issue.png

2.bundle id和iCloud容器必須一一對應,好比你的bundle identifier寫成了「com.<你的域名>.Babifud」,那你的容器名應該是"iCloud.com.<你的域名>.Babifud"。

3.容器的命名必須獨一無二,由於這是CloudKit用來接入數據的惟一標識符,因此這也意味着bundle id也必須獨一無二。

4.爲了確保擁有正常工做的權限,應用id和bundle id必須列在證書、標示符和配置中心的App ID部分。也就是說你須要設置一個Team id以用來驗證登陸,此外也要列上應用id,還有iCloud 容器的id。

一般來講CloudKit會自動爲你完成全部內容,前提是你登入的是一個可用的開發者帳號。不過有時候並非那麼及時的同步,你還能夠通用一個新的帳號,而後改變CloudKit容器id保證對應。另外,爲此你可能須要修改info.plist文件或者BabiFud.entitlements文件以確保裏面的id也和你設置的bundle id匹配。

關於CloudKit儀表盤

設置了可用權限以後下一步就是配置一下CloudKit,來建立應用數據的記錄類型。以後你即可以開始使用CloudKit的儀表盤,以下圖,點擊CloudKit Dashboard。

cloudkit_dboard_button1.png

提示:你也能夠用網頁登錄你的儀表盤。

你會看到儀表盤出現了,像這樣:

1_basic_image.png

下面是此教程須要的有關於儀表盤的一些概述:

左邊欄的SCHEMA表明CloudKit容器的高級類:Record Types, Security Roles, 和Subscription Types。在教程裏你只會用到record Types。

一個Record Types用來設置定義一個單獨的記錄。在面向對象編程裏,Record Types就至關於某一對象的類模板。一個記錄能夠看作一個Record Type類的實例。這是容器的基本數據結構,卻是很想數據庫裏面的一行數據,包含了一系列鍵和值。

PUBLIC DATA和PRIVATE DATA 就是你添加查找數據的地方,你須要接入數據庫。記住,做爲一個開發者你能夠閱覽全部的共享數據,不過你只能看到你本身的私密數據,User Records記錄了一些當前CloudKit使用者的的信息好比名稱或者郵件。一個Record Zone(這裏是Default Zone)用於給私密數據分組提供數據的邏輯結構。當在進程的其餘操做以前容許大量數據數據同時存儲時Custom zones支持自動處理。不過那不是咱們今天要討論的範圍。

ADMIN欄主要用於管理開發團隊的成員權限,若是你的項目有不少人共同開發,你能夠規劃一下他們各自的權限,這個問題比較艱深並且有些超綱故而在此不談。

添加Establishment類的記錄

選中Record Types,點擊左上方+的圖標而後你就能夠添加一個新的記錄類型並設置細節。以下圖:

2_add_new.png

命名你的新記錄類爲Establishment。

來考慮一下有關應用的設計,每個你想要記錄的Establishment都有不少數據:名稱、地點、或者是兒童友好的選項都是有用的。記錄類就是用於定義記錄中各式各種的數據的。

當你開始定義Name, Attribute Type,和Index的時候你會看到以下這行,這個時候一個Attribute命名模板已經自動建立。

3_add_attr.png

你能夠修改這個預設的Attribute Name爲你想要的,這裏由於要作的是示例中的應用,所以你須要添加下面的Attribute,點擊Add Attribute...添加新行。

table_ck_attributes.png

當你完成以後你會獲得以下這樣一個屬性表單。

4_all_atts.png

點擊頁面底部的Save以保存你的新紀錄類型。

你如今能夠添加一些Establishment樣本到你數據庫裏面了。

選擇左邊導航裏的Default Zone欄這裏主要存儲了你應用上的共享數據,而後在出如今中間的下拉列表裏選擇Establishment記錄類,最後點擊右邊+的圖標。

5_new_record.png

這樣你就建立了一個全新的空Establishment記錄。

6_new_record.png

這時你能夠添加一些測試數據了。

下面這個測試樣例的數據是徹底虛構的,位置數據已經被設定在蘋果總部附近以便於在虛擬機上測試。

填寫數據以下表:

table_ck_attributes.png

提示:每個 CoverPhoto屬性裏的圖片都存儲在項目的Supporting Files\Sample Images文件夾裏面,你只要拖動這些圖片到 CoverPhoto欄裏面它們就在記錄保存的時候會自動上傳。

三個測試數據錄入完畢以後會像下面這樣;

7_completed_record.png7_completed_record.png

對於每條數據,這裏的值只是表明它在數據庫中的樣子,在應用的那一端會徹底不同,好比SeatingType和ChangingTable是枚舉類型,在這裏的這個整數表明的是枚舉的數的值。對於HealthyOption和KidsMenu來講這裏的值表明了布爾型數據:0表明這家餐廳不知足此項,1則是知足。

讓咱們在退回Xcode。是時候把這些數據整合進你的應用了。

查詢Establishment數據

CKQuery對象被用於從數據庫裏查詢記錄。一個CKQuery描述瞭如何查詢一些特定類型或是特定條件的記錄。這些條件能夠是諸如「記錄的Name是M開頭的」、「有軟墊座位的記錄」、「方圓三千米之內的記錄」。這一類型在Cocoa裏的表達方式則是使用NSPredicate對象,NSPredicate也會斷定各對象是否知足規則。在Core Data裏適用的不少斷定很一樣適用於CloudKit。由於斷定條件一般會被定義爲對某一屬性進行比較。

CloudKit僅支持可用的NSPredicate方法中的一部分子集。包括一些數學比較,字符串和集合運算,以及新增的特定距離函數。distanceToLocation:FromLocation方法:NSPredicate爲CloudKit添加的一個根據已知座標計算和半徑匹配範圍內記錄的方法。下面的這類斷定都略過了細節,對於其它的查詢CKQuery類引用了一個關於使用的方法和如何使用的清單。

提示:CloudKit包括了支持CLLocation類,其中有Core Location框架下包括地理位置的一些對象。這使得在某一地理範圍內查詢Establishment類變得簡單,你不須要寫那些痛苦的數學公式。

打開Model\Model.swift文件,包含了服務端的全部調用。

用下面這一段替換掉fetchEstablishments(location:, radiusInMeters:)方法

 1 func fetchEstablishments(location:CLLocation,
 2                            radiusInMeters:CLLocationDistance) {
 3 // CloudKit在它自帶的距離斷定中使用的單位是千米,這裏把radiusInMeters轉換成千米
 4 let radiusInKilometers = radiusInMeters / 1000.0 
 5   // 這一斷定Establishment類的條件是它們到當前距離的千米數,這個方法會根據用戶當前位置以及設定範圍獲得範圍內全部的Establishment和它們的位置信息
 6   let locationPredicate = NSPredicate(format: "distanceToLocation:fromLocation:(%K,%@) < %f",
 7     "Location",
 8     location,
 9     radiusInKilometers) 
10   // CKQuery 對象的建立須要一個record類型和一個斷定條件做爲參數,它們將用於查詢
11   let query = CKQuery(recordType: EstablishmentType,
12                        predicate:  locationPredicate) 
13   // performQuery(_:, inZoneWithID:, completionHandler:)方法會把你的查詢發送到iCloud,返回結果。當傳遞的inZoneWithID爲nil的狀況下你只會在default zone也就是共享數據中進行查詢,若是你但願連着一塊兒查詢私人數據的話,則須要進行一個單獨的調用。
14   publicDB.performQuery(query, inZoneWithID: nil) { 
15     results, error in
16     if error != nil {
17       dispatch_async(dispatch_get_main_queue()) {
18         self.delegate?.errorUpdating(error)
19         return
20       }
21     } else {
22       self.items.removeAll(keepCapacity: true)
23       for record in results{
24         let establishment = Establishment(record: record as CKRecord, database: self.publicDB)
25         self.items.append(establishment)
26       }
27       dispatch_async(dispatch_get_main_queue()) {
28         self.delegate?.modelUpdated()
29         return
30       }
31     }
32   }
33 }

 

全部的都作完以後你也許會好奇CKDatabase的實例publicDB是從哪兒來的。那麼看下Model類最開始的代碼:

 1 let container : CKContainer
 2 let publicDB : CKDatabase
 3 let privateDB : CKDatabase
 4  
 5 init() {
 6   // defaultContainer()表明的是你在iCloud功能欄裏制定的那個容器
 7   container = CKContainer.defaultContainer() 
 8   // publicCloudDatabase則是你應用上的全部用戶共享的數據
 9   publicDB = container.publicCloudDatabase 
10   // privateCloudDatabase僅僅是你我的的私密數據
11   privateDB = container.privateCloudDatabase 
12 }

 

這些代碼會從共享數據裏找出幾個當地的Establishment數據來,不過咱們還須要在應用裏面用一個視圖控制器來顯示點東西。

設置必要回調函數

你可能會注意到通知中心使用的是咱們很是熟悉的委託模式。下面是Model.swift文件最開始的協議部分代碼,你能夠在你的視圖控制器中實現它。

1 protocol ModelDelegate {
2 
3   func errorUpdating(error: NSError)
4 
5   func modelUpdated()
6 
7 }

 

打開MasterViewController.swift文件並用下面代碼替換掉 modelUpdated()方法:

1 func modelUpdated() {
2 
3   refreshControl?.endRefreshing() 
4 
5   tableView.reloadData() 
6 
7 }

 

這裏一般是在新數據可用時執行。在tableView(_: cellForRowAtIndexPath:)中,有關CloudKit對象的全部table view cell的喚醒都被照顧到了,你能夠本身隨便看一看。

如今來用下面的代碼替換掉errorUpdating(error:):

1 func errorUpdating(error: NSError) {
2   let message = error.localizedDescription 
3   let alert = UIAlertView(title: "Error Loading Establishments",
4    message: message, delegate: nil, cancelButtonTitle: "OK")
5   alert.show()
6 }

 

不管查詢結果產生任何錯誤都會調用此方法。這些錯誤發生的緣由多是網絡狀況較差,也多是像用戶憑證丟失或出錯這樣的CloudKit特有問題,還多是由於你所要找的這條記錄根本不存在。

提示:在對待任何遠程服務的時候一個好的異常處理是必不可少的,而在這裏你僅僅是給用戶彈出了一個錯誤提示而已。

在模擬器上運行一下,你會看到以下這樣一個關於附近的Establishment列表:

places_no_assets.png

你能夠看到Establishment的名稱以及它提供的相關服務,不過你卻沒有看到任何相關的圖片顯示,這是爲何?

當你獲取你的Establishment記錄時候你也會自動的去獲取圖片,不過,想要在你的App中顯示圖片的話你還須要作一些必要事項(這在下面會提到,先來看一看一些出錯處理)。

常見錯誤處理

若是你獲取的列表顯示並不正確,請確保你在Debug\Location\Apple中所設定的位置信息準確無誤。若是你更改了位置信息,下拉列表進行強制刷新,而不要等待位置觸發器。

若是你是在啓用了定位的iPhone或者iPad上調試,並且列表顯示仍不正確,那僅僅是由於你這些Establishment的位置離你的當前位置還不夠近。這裏有兩種解決方法:要麼把示例數據的位置信息修改到你附近,要麼是用模擬器來調試。其實這裏還有一個實用性卓越的第三種解決方案——你能夠跑到Cupertino去而後在蘋果的草坪上散散步。

若是示例數據並未顯示適當,或者根本不顯示。用CloudKit儀表盤檢查一下示例數據,首先確認你是否將它們都添加進了Default Zone之中,而後看一看它們的值是否正確。若是你想從新寫入數據的話你能夠像下圖這樣刪除掉原來的數據:

8_delete.png

有時候調試CloudKit,報錯會至關狡猾。好比在寫入的時候,CloudKit的報錯並不會包含太多信息,若是你想斷定錯誤緣由的話你須要看一下錯誤代碼已經你具體要作的數據庫操做是什麼樣的。使用數值化的錯誤代碼,而後再跟CKErrorCode對比來檢錯。文檔中的標題和描述會對錯誤範圍的縮小有所幫助,下面是一些例子:

提示:對於一些簡單的錯誤,你能夠在蘋果的官方文檔中有關CloudKit的章節裏CKErrorCode枚舉類型中找到。

如下是常見的錯誤類型,以及處理建議:

.BadContainer 和 .MissingEntitlement

檢查一下在iCloud的Entitlements欄目中指定了與CKContainer對象匹配的容器,而且它存在於你的CloudKit儀表盤中。

.NotAuthenticated 和 .PermissionFailure

確保你在Settings.app中輸入了正確的iCloud用戶憑證,並確保iCloud可用。

.UnknownItem

檢查CloudKit儀表盤中的記錄類型名稱與RecordType字串相匹配。

使用二進制資源

資源就是二進制數據,好比在你的記錄中使用的圖片。在這個例子中,你的應用資源就是那些將要展現在你附近的列表視圖中的Establishment照片。

在這一節你將添加有關資源加載的邏輯,首先這些資源須要在你重獲Establishment記錄的時候已經下載好了。

打開Model\Establishment.swift文件而且使用下面代碼替換掉loadCoverPhoto(completion:)方法:

 1 func loadCoverPhoto(completion:(photo: UIImage!) -> ()) { 
 2   //雖然資源數據是在你獲取記錄的剩餘內容時一塊兒下載的,可是你並不想在同時顯示這些圖片。因此這裏把全部的代碼都包裹到了dispatch_async塊中。
 3   dispatch_async(
 4    dispatch_get_global_queue(
 5     DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)){
 6     var image: UIImage!
 7     //資源在CKRecord中做爲CKAsset的一個實例來保存,以方便準確的轉換
 8     let coverPhoto = self.record.objectForKey("CoverPhoto") as CKAsset! 
 9     if let asset = coverPhoto {
10       //使用資源提供的本地文件URL來加載圖片
11       if let url = asset.fileURL { 
12         let imageData = NSData(contentsOfFile: url.path!)
13         //使用資源數據來構建一個UIImage實例
14         image = UIImage(data: imageData) 
15       }
16     }
17     //使用獲取的image來運行completion回調函數
18     completion(photo: image) 
19   }
20 }

 

構建並運行,Establishment圖片應該能夠顯示了:

with_assets.png

下面是有關CloudKit資源我已經給出的兩點:

1.資源數據在CloudKit上只能以一個屬性的形式存在於記錄中,你不能單獨的存儲它們,刪除一條記錄也會刪除掉相關的全部資源數據。

2.由於資源數據的獲取是與記錄剩餘內容的獲取在同時進行的,因此也會在性能上帶來必定的負面影響,若是你的App會用到大量的資源,你應該將資源單獨存儲爲另外一種形式的記錄。

更多

如今這個App能夠在表格視圖中下載到有關Establishment記錄,並加載細節信息和圖片了。你能夠在最下方下載到一個完整的工程文件,而後對其進行以下擴展:

  1. 容許用戶上傳圖片,記錄,評論或者投訴。分享一些糟糕的經歷會對用戶有所幫助。

  2. 讓用戶使用地圖來添加新的Establishment記錄,這一功能添加到你的Model類後可做爲一個關於在共享數據庫或者私人數據庫中存儲記錄的示例。

  3. 添加過濾盒搜索,Model類可用距離斷定構建一個CKQuery,不過斷定能夠改進得更爲複雜。CloudKit也支持文本查找和特徵字串。

  4. 改善App和數據加載的性能。這個教程只是在一切準備就緒以後使用了便捷可用的方法來調用一些現成的操做。CKDatabase的實例基於NSOperation以提供了比起API執行更多得多的操做方法。在操做中(好比操做剛剛完成)你就能夠接受數據而非同時進行。

  5. 使用緩存和同步以保證App可以離線使用並在聯網以後就能當即更新到最新的內容。

相關文章
相關標籤/搜索