一、什么是cloudKit
移動開發中,網絡請求數據是日常中用到的,我們習慣把一些用戶改動的數據存在服務器,以便下次請求使用。或者開發者方通過服務器將編輯的數據發送給用戶。但是這一切都建立在我們的APP擁有自己的服務器之上。如果沒有服務器的情況下我們的某些開發就變得很難進行,比如,公司沒有服務器,確需要你做出一個用戶系統,這顯然令人頭痛,因為這幾乎是一個不可能完成的任務(有人說,扯淡吧,公司會沒有服務器? 呵呵,我公司就沒有啊)。然而蘋果提供了一些便利,雖然不能完全替代后端,但是在一定程度上可以解決數據的雲端存儲需求,沒錯就是本文要講的:CloudKit。每個開發者在使用CloudKit之后,蘋果會為其提供一個不小的dashboard作為雲端數據的存儲地,CloudKit 提供了一套API用於在dashboard中存取數據。簡直太方便了啊有米有。
關於CloudKit的詳細說明,我建議大家看看這里。
二、如何使用cloudKit
使用之前,我們先了解下關於CloudKit的一些基本數據類型。
CloudKit 基礎對象類型
CloudKit 的基礎對象類型有 7 種。這些對象類型可能和你在其他編程領域了解的類似對象類型稍有差別。
-
CKContainer
: Containers 就像應用運行的沙盒一樣,一個應用只能訪問自己沙盒中的內容而不能訪問其他應用的。Containers 就是最外層容器,每個應用有且僅有一個屬於自己的 container。(事實上,經過開發者授權配置 CloudKit Dashboard 之后,一個應用也可以訪問其他應用的 container。) -
CKDatabase
: Database 即數據庫,私有數據庫用來存儲敏感信息,比如說用戶的性別年齡等,用戶只能訪問自己的私有數據庫。應用也有一個公開的數據庫來存儲公共信息,例如你在構建一個根據地理位置簽到的應用,那么地理位置信息就應該存儲在公共數據庫里以便所有用戶都能訪問到。 -
CKRecord
: 即數據庫中的一條數據記錄。CloudKit 使用 record 通過 k/v 結構來存儲結構化數據。關於鍵值存儲,目前值的架構支持 NSString、NSNumber、NSData、NSDate、CLLocation,和 CKReference、CKAsset,以及存儲以上數據類型的數組。 -
CKRecordZone
: Record 不是以零散的方式存在於 database 之中的,它們位於 record zones 里。每個應用都有一個 default record zone,你也可以有自定義的 record zone。 -
CKRecordIdentifier
: 是一條 record 的唯一標識,用於確定該 record 在數據庫中的唯一位置。 -
CKReference
: Reference 很像 RDBMS 中的引用關系。還是以地理位置簽到應用為例,每個地理位置可以包含很多用戶在該位置的簽到,那么位置與簽到之間就形成了這樣一種包含式的從屬關系。 -
CKAsset
: 即資源文件,例如二進制文件。還是以簽到應用為例,用戶簽到時可能還包含一張照片,那么這張照片就會以 asset 形式存儲起來。
本文就一個簡單的例子說說使用cloudKit。假如我們需要做一個用戶信息的存儲系統。
使用CloudKit 的環境配置
在使用CloudKit之前,我們需要先在Xcode 中做一些配置。 (本文基於Xcode8)
- 在target -> capabilities 中找到 iColod選項。
- 打開iCloud的控制開關。
- 在展開的框中做如下設置
其中在containers中的 “ cloudKit dashBoard ”按鈕可以直接進入到我們在icloud上的存儲空間。 這里要確保APP中使用的開發者賬號是實際存在的 。進入網頁之后登錄開發者賬號登錄。
登錄成功會出現這個畫面:
這里我已經存了一部分數據。 存進去之后還是很直觀的,但是看不到存儲的屬性對應的內容,我也還在研究中。
使用CloudHit 保存數據
如果我們需要保存一個包含了用戶的 賬戶、密碼、用戶名字、電話號碼、郵箱、用戶頭像等數據的對象。 我們創建一個用戶的屬性類 UserInfoModel。
#import <Foundation/Foundation.h> @interface UserInfoModel : NSObject /** 賬戶名稱 */ @property (nonatomic,strong)NSString *userAccout; /** 用戶名字 */ @property (nonatomic,strong) NSString *userName; /** 用戶頭像 */ @property (nonatomic,strong) UIImage *userAvtar; /** 用戶手機號碼 */ @property (nonatomic,strong) NSString *userPhoneNum; /** 用戶郵箱號 */ @property (nonatomic,strong) NSString *userEmail; /** 用戶密碼 */ @property (nonatomic,strong) NSString *userPassword; @end
在保存數據之前,我們為了之后方便查詢,可以將某一項作為唯一標示(這要求標示不會經常被修改),比如在這里我們選擇賬戶名,當然你也可以選擇其他任意的字符,關鍵是之后的查找要有依據。 接下來就開始進行保存了。
- (void)saveWithModel:(UserInfoModel*)userInfoModel{ //因為賬戶名不變的 以帳戶名做微ID最好不過了 CKRecordID *postrecordID = [[CKRecordID alloc]initWithRecordName:userInfoModel.userAccout]; CKRecord *postRecrod = [[CKRecord alloc] initWithRecordType:userInfoModel.userAccout recordID:postrecordID]; //將用戶類的屬性和屬性值打包成一個字典 其中屬性對應key 屬性值對應Value 因為屬性中有一欄是圖片類,CloudKit不支持直接對圖片進行保存,但是可以轉換成NSdata,這洋就可以進行保存了. 這里說明一下 cloudKit的提交 只接受NSString、NSNumber、NSData、CLLocation,和 CKReference、CKAsset 等直接的存儲, 其它的需要 NSMutableDictionary *dic = [[NSMutableDictionary alloc]init]; NSMutableArray *propArr = [self getAllProp:[userInfoModel class]]; //這里使用getAllProp 在下面貼出 for (NSString *prop in propArr) { if([[userInfoModel valueForKey:prop] isKindOfClass:[UIImage class]]){ // 圖片特殊情況另外處理 如果實別的不符合存儲的也同樣需要處理 UIImage *image = [userInfoModel valueForKey:prop]; postRecrod[prop] = [NSData dataWithData:UIImagePNGRepresentation(image)]; //record可以像字典一樣進行數據的收納 }else{ postRecrod[prop] = [userInfoModel valueForKey:prop]; } } //用戶信息 提交到 雲 [[[CKContainer defaultContainer] privateCloudDatabase] saveRecord:postRecrod completionHandler:^(CKRecord *savedPlace, NSError *error) { if(savedPlace){ DLog(@"%@",savedPlace); //成功 打印存儲的內容 }else{ DLog(@"%@",error); //失敗 打印錯誤 } }]; }
- (NSMutableArray *)getAllProp:(Class)cls{ // 獲取當前類的所有屬性 unsigned int count;// 記錄屬性個數 objc_property_t *properties = class_copyPropertyList(cls, &count); // 遍歷 NSMutableArray *mArray = [NSMutableArray array]; for (int i = 0; i < count; i++) { // objc_property_t 屬性類型 objc_property_t property = properties[i]; // 獲取屬性的名稱 C語言字符串 const char *cName = property_getName(property); // 轉換為Objective C 字符串 NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding]; [mArray addObject:name]; } return mArray; }
使用CloudHit 查詢數據
- (void )cloudGetUserInfoWithUseraccout:(NSString *)userAccout Succeed:(void(^)(UserInfoModel* ))succeed failed:(void(^)(NSError *))failed{ UserInfoModel *model = [[UserInfoModel alloc]init]; if(userAccout){ CKRecordID *postrecordID = [[CKRecordID alloc]initWithRecordName:userAccout]; [[[CKContainer defaultContainer] privateCloudDatabase] fetchRecordWithID:postrecordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { // handle errors here if(error){ if(failed){ failed(error); } }else{ //說明查詢成功 if(succeed){ //已經獲取到了存入的數據, 並經過轉換存入了字典 dic 將字典中的鍵值對賦給一個類對應的屬性 同理因為其中有一個圖片 所以需要做一個NSdata的轉換 NSMutableArray *mArray = [self getAllProp:[model class]]; for (NSString *prop in mArray) { //這里如果不是在后台人為添加的數據 不會出現沒有對應的屬性的情況 但是為了保險起見。在UserInfoModel 重寫 setVale:forUndefinedKey方法 id info = [record valueForKey:prop]; if(![info isKindOfClass:[NSData class]]){ // [model setValue:[dic valueForKey:prop] forKey:prop]; [model setValue:record[prop] forKey:prop]; }else{ UIImage *image = [UIImage imageWithData:info]; [model setValue:image forKey:prop]; } } succeed(model); //回調獲取到的模型 } } } ]; } }
使用CloudHit 刪除數據
- (void)cloudDeleteModelWithModel:(UserInfoModel *)userInfoModel{ //之前也說了在存儲的時候, 我們操縱cloud上的數據都是通過record ID來實現的, 所以record ID的名字應該是一個不經常改變的屬性, 這里用的就是用戶的賬戶名 CKRecordID *postrecordID = [[CKRecordID alloc]initWithRecordName:userInfoModel.userAccout]; [[[CKContainer defaultContainer] privateCloudDatabase] deleteRecordWithID:postrecordID completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) { if(!error){ //刪除成功 }else{ DLog(@"刪除失敗%@",error); //打印錯誤 } }]; }
以上就是cloudKit的簡單使用。通過這個,當需要存儲一些比較簡單的數據還是可以滿足的。
PS:我也剛開始用,發現了一個小問題, 單次存儲的數據不能太大(大小沒有測試,大概是2M左右吧),不然會提示失敗。 所以有的數據過大,可能需要經過拆分,或者圖片過大需要經過壓縮才能進行存儲。