CloudKit是蘋果推出的基於iCloud的一個雲端數據存儲服務.其 主要由下面兩部分組成:
一個儀表web頁面,用於管理公開數據的記錄類型.
一組API接口,用於iCloud和設備之間的數據傳遞.
一:首先在XCode上面打開關於iCloud功能
1:進入對應的項目Targets 中的Capabilities 選項卡,打開關於iCloud功能;如果勾選iCloud Documents或CloudKit會自動生成一個帶iCloud.開頭的Containers,要配置證書支持;CloudKit Dashboard則可以直接跳轉到Web配置關於iCloud的內容;而關於Steps則是配置的步驟是否都成功;
二:關於證書如何配置支持iCloud功能
1:進入蘋果證書管理後台中的Identifiers裡有個iCloud Containers菜單
因為實例中的Bundle Identifier的名字wjy.com.MobileProject;所以在這邊創建一個iCloud.wjy.com.MobileProject的ID值;都是前面增加一個iCloud為開頭;app的bundle id需要與iCloud容器相對應,?iCloud容器名必須是唯一的,因為這是Cloudkit用來訪問數據所使用的全局標識符。由於iCloud容器名包含bundle id,因此bundle id也必須是唯一的(這就是為何需要修改com.raywendrelich.BabiFud)。
為了讓entitlements起作用,需要在App的證書、標識符與配置文件中ID的部分列出app/bundle id。這意味著標識的證書使用了設置的team id與app id,從中可得到iCloud容器的id。若已經在一個可用的開發者賬號中標識了的話Xcode會自動完成這一切。不巧的是,這有時是不同步的,需要更新ID-使用iCloud功能面板修改CloudKit容器ID。否則的話需要修改info.plist文件或BabiFud.entitlements文件來確保id values與所設置的bundle id一致。
2:創建完上面的ID後,同樣進行Identifiers裡的App IDs,找到我們當前的App ID然後對它進行編輯
打開iCloud的功能選擇,並且選擇Include CloudKit support,這邊這時會有一個警告出現,選擇右邊的Edit進行編輯
這時會有剛才創建好的那個iCloud ID可以選擇綁定;選擇好以後外面的警告也會消除;然後生成對應的描述文件安裝後,XCode上面的步驟報錯也會消除;
三:設置iCloud的數據
1:要進入CloudKit Dashboard操作有兩種方式,第一種如上面第一點所說可以直接點CloudKit Dashboard進入,另一種就是進入蘋果賬號後台也有一個相應的菜單;
2:進入CloudKit Dashboard可以看到如下的頁面
2.1 SCHEMA :
CloudKit容器的高級類:Record Types, Security Roles, 和Subscription Types,其中主要使用的是Record Types;
一個Record Type用來定義一個單獨的記錄(可以理解為一個數據模型),相當於存儲數據的模板,和數據庫的表結構類似;
2.2 PUBLIC DATA 和 PRIVATE DATA
就是你保存數據的地方,開發者可以查看所有的共享數據,但是只能看到自己的私密數據,無法看到用戶的私密數據;這裡沒有顯示PRIBATE DATA,其結構和PUBLIC DATA是一樣的;
User Records 記錄一些當前使用者的信息;
Default Zone :這裡可以查看數據的詳細信息,也是後面主要使用的;
2.3 ADMIN 主要是管理開發者團隊權限的,這裡不做過多介紹;
3:切回Record Type選項,點擊右邊欄的左上角的 "+ ",新增一個模型:
輸入模型名稱: 默認只有一個StringField的屬性(這裡暫且這麼稱呼吧),可以點擊下面的Add Field... 新增屬性列表;
同樣可以選擇屬性的類型,如下圖:
設置完成後,點擊右下角的 Save按鈕即可保存!這樣,一個模型就建立好了;存儲類型的數據:
NSData (single bytes) NSDate (date and time) NSNumber (both Int and Double) NSString (or String in Swift) NSArray (list) CKReference (used to create relationships between objects) CLLocation (location) CKAsset (file)
5:回到Default Zone,中間藍色區域的右上角的名稱旁有個倒三角,這裡可以選擇使用(查詢)哪個模型下的數據:
6:如果要存是圖片字段記得選Asset類型
注意:1 Public Record ,1 Private Record,意思是:使用模型Note的有一條公共數據,一條隱私數據,由於我登陸的iCloud賬號和開發者賬號不一樣,相當於是用戶賬號,所以這裡是看不到那個隱私數據的
四:代碼操作iCloud功能
1:首先引入CloudKit.framework系統框架,並引入命名空間#import
2:首先是判斷手機中的iCloud功能是否開啟,如下面如果有值則表示已經開啟的iCloud功能;
id cloudUrl=[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
這個方法接受一個參數, 就是要獲取的容器標識。 所謂容器標識, 大多數應用只會用到一個 iCloud 容器,所以我們這裡傳入 nil, 就代表默認獲取第一個可用的容器。這個方法內部會查找當前應用擁有的 iCloud 容器, 如果找到就會返回這個容器的 URL, 證明當前應用的 iCloud 容器可用。 如果找不到,就會返回 nil, 證明當前應用的 iCloud 不可用。
注意: iCloud 容器和你 App 文件沙盒, 在 iOS 文件系統中其實是分別存放在兩個不同的地方的:
iCloud 文件路徑格式 file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~xxx~aaa/Documents
App 沙盒文件路徑格式 file:///var/mobile/Containers/Data/Application/3B4376B3-89B5-3342-8057-3450D4224518/Documents/
由此可見, 這也是為什麼 iCloud 和 Sandbox 文件路徑訪問需要兩套不同的方式的原因了。
3:增加一條記錄,可以給它一個固定的recordID,就像數據表的主鍵一樣,查找、更新跟刪除有用;
-(void)addCloudDataWithPublic:(BOOL)isPublic recordID:(NSString *)recordID name:(NSString *)name password:(NSString *)password { //CloudKit給應用程序分配部分空間,用於存儲數據,首先要獲取這個存儲空間,這裡我們直接獲取了默認的存儲器(可以自定義存儲器): CKContainer *container=[CKContainer defaultContainer]; CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; //公共數據 } else { database=container.privateCloudDatabase;//隱藏數據 } //創建主鍵ID 這個ID可以到時查找有用到 CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID]; //創建CKRecord 保存數據 CKRecord *noteRecord = [[CKRecord alloc]initWithRecordType:@"User" recordID:noteId]; //設置數據 [noteRecord setObject:name forKey:@"name"]; [noteRecord setObject:password forKey:@"password"]; //保存操作 [database saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { if (!error) { [self showAlertMessage:@"保存成功"]; } }]; }
4:假如要保存帶有圖片的字段,需要用到CKAsset,他的初始化需要一個URL,所以這裡,我先把圖片數據保存到本地沙盒,生成一個URL,然後再去創建CKAsset:
//增加帶圖片的提交 圖片的保存,需要用到CKAsset,他的初始化需要一個URL,所以這裡,我先把圖片數據保存到本地沙盒,生成一個URL,然後再去創建CKAsset: -(void)saveImageDataWithPublic:(BOOL)isPublic recordID:(NSString *)recordID name:(NSString *)name password:(NSString *)password { //保存圖片 圖片的保存,需要用到CKAsset,他的初始化需要一個URL,所以這裡,我先把圖片數據保存到本地沙盒,生成一個URL,然後再去創建CKAsset: UIImage *image=[UIImage imageNamed:@"icloudImage"]; NSData *imageData = UIImagePNGRepresentation(image); if (imageData == nil) { imageData = UIImageJPEGRepresentation(image, 0.6); } NSString *tempPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/imagesTemp"]; NSFileManager *manager = [NSFileManager defaultManager]; if (![manager fileExistsAtPath:tempPath]) { [manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:nil]; } NSString *filePath = [NSString stringWithFormat:@"%@/%@",tempPath,@"iCloudImage"]; NSURL *url = [NSURL fileURLWithPath:filePath]; [imageData writeToURL:url atomically:YES]; CKAsset *asset = [[CKAsset alloc]initWithFileURL:url]; //與iCloud進行交互 CKContainer *container=[CKContainer defaultContainer]; CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; //公共數據 } else { database=container.privateCloudDatabase;//隱藏數據 } //創建主鍵ID 這個ID可以到時查找有用到 CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID]; //創建CKRecord 保存數據 CKRecord *noteRecord = [[CKRecord alloc]initWithRecordType:@"User" recordID:noteId]; //設置數據 [noteRecord setObject:name forKey:@"name"]; [noteRecord setObject:password forKey:@"password"]; [noteRecord setObject:asset forKey:@"userImage"]; //保存操作 [database saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { if (!error) { [self showAlertMessage:@"保存成功"]; } }]; }
5:查找單條記錄的功能,通過recordID進行
//查找單條記錄 -(void)searchRecordWithRecordID:(NSString *)recordID withFormPublic:(BOOL)isPublic { //獲得指定的ID CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID]; //獲得容器 CKContainer *container=[CKContainer defaultContainer]; //獲得數據的類型 是公有還是私有 CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; } else { database=container.privateCloudDatabase; } __weak typeof(self)weakSelf = self; //查找操作 [database fetchRecordWithID:noteId completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { NSString *message=[NSString stringWithFormat:@"獲得RecordID為%@ 的數據:%@,%@",recordID,[record objectForKey:@"name"],[record objectForKey:@"password"]]; [weakSelf showAlertMessage:message]; }]; }
6:查詢多條記錄的功能,用到的謂詞進行
//查找多條記錄(可以用謂詞進行) -(void)searchRecordWithFormPublic:(BOOL)isPublic withRecordTypeName:(NSString *)recordTypeName { CKContainer *container=[CKContainer defaultContainer]; //獲得數據的類型 是公有還是私有 CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; } else { database=container.privateCloudDatabase; } NSPredicate *predicate=[NSPredicate predicateWithValue:YES]; CKQuery *query=[[CKQuery alloc]initWithRecordType:recordTypeName predicate:predicate]; __weak typeof(self)weakSelf = self; [database performQuery:query inZoneWithID:nil completionHandler:^(NSArray* _Nullable results, NSError * _Nullable error) { [weakSelf showAlertMessage:[NSString stringWithFormat:@"%@",results]]; }]; }
7:更新一條記錄首先要查找出該條記錄,再對它進行修改,如果對應的鍵值存在進修改其值,如果鍵值不存在則增加新的類型字段;
//更新一條記錄 首先要查找出這一條 然後再對它進行修改 -(void)updateRecordWithFormPublic:(BOOL)isPublic withRecordTypeName:(NSString *)recordTypeName withRecordID:(NSString *)recordID { //獲得指定的ID CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID]; //獲得容器 CKContainer *container=[CKContainer defaultContainer]; //獲得數據的類型 是公有還是私有 CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; } else { database=container.privateCloudDatabase; } __weak typeof(self)weakSelf = self; //查找操作 [database fetchRecordWithID:noteId completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { if (!error) { //對原有的健值進行修改 [record setObject:@"aa123456789" forKey:@"password"]; //如果健值不存在 則會增加一個 [record setObject:@"男" forKey:@"gender"]; [database saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) { if (!error) { [weakSelf showAlertMessage:@"修改保存成功"]; } else { [weakSelf showAlertMessage:[NSString stringWithFormat:@"出錯誤 :%@",error]]; } }]; } }]; }
8:刪除一條記錄
//刪除記錄 -(void)deleteRecordWithFormPublic:(BOOL)isPublic withRecordID:(NSString *)recordID { //獲得指定的ID CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID]; //獲得容器 CKContainer *container=[CKContainer defaultContainer]; //獲得數據的類型 是公有還是私有 CKDatabase *database; if (isPublic) { database=container.publicCloudDatabase; } else { database=container.privateCloudDatabase; } __weak typeof(self)weakSelf = self; [database deleteRecordWithID:noteId completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) { if (!error) { [weakSelf showAlertMessage:@"刪除成功"]; return; } [weakSelf showAlertMessage:[NSString stringWithFormat:@"刪除失敗 %@",error]]; }]; }
五:操作實例
編寫一個關於在同一台手機上免登錄的實例,主要原理是通過獲取手機設備唯一碼,然後通過iCloud保存到雲端,在刪除APP後再安裝後就可以直接從雲端獲得用戶跟密碼;會不斷完善此實例,如果感興趣可以點星並關注,會接著編寫關於iCloud關於文件的同步功能;注意證書可以換成自個的,步驟如上:
源代碼地址:https://github.com/wujunyang/iCloudProject