iCloud服務數組
iCloud 是蘋果提供的雲端服務,用戶能夠將通信錄、備忘錄、郵件、照片、音樂、視頻等備份到雲服務器並在各個蘋果設備間直接進行共享而無需關心數據同步問題,甚至 即便你的設備丟失後在一臺新的設備上也能夠經過Apple ID登陸同步。固然這些內容都是iOS內置的功能,那麼對於開放者如何利用iCloud呢?蘋果已經將雲端存儲功能開放給開發者,利用iCloud開發者 能夠存儲兩類數據:用戶文檔和應用數據、應用配置項。前者主要用於一些用戶文檔、文件的存儲,後者更相似於平常開放中的偏好設置,只是這些配置信息會同步 到雲端。服務器
要進行iCloud開發一樣須要一些準備工做(下面的準備工做主要是針對真機的,模擬器省略Provisioning Profile配置過程):網絡
一、 2步驟仍然是建立App ID啓用iCloud服務、生成對應的配置(Provisioning Profile),這個過程當中Bundle ID可使用通配符(Data Protection、iCloud、Inter-App Audio、Passbook服務在建立App ID時其中的Bundle ID是可使用通配ID的)。app
3.在Xcode中建立項目(假設項目名稱爲「kctest」)並在項目的 Capabilities中找到iCloud並打開。這裏須要注意的就是因爲在此應用中要演示文檔存儲和首選項存儲,所以在Service中勾選 「Key-value storae」和「iCloud Documents」:ide
在項目中會自動生成一個」kctest.entitlements」配置文件,這個文檔配置了文檔存儲容器標識、鍵值對存儲容器標識等信息。測試
4.不管是真機仍是模擬器都必須在iOS「設置」中找到iCloud設置登陸帳戶,注意這個帳戶沒必要是沙盒測試用戶。ui
A.首先看一下如何進行文檔存儲。文檔存儲主要是使用UIDocument類來完成,這個類提供了新建、修改(其實在API中是覆蓋操做)、查詢文檔、打開文檔、刪除文檔的功能。this
UIDocument 對文檔的新增、修改、刪除、讀取所有基於一個雲端URL來完成(事實上在開發過程當中新增、修改只是一步簡單的保存操做),對於開發者而言沒有本地和雲端之 分,這樣大大簡化了開發過程。這個URL能夠經過NSFileManager的URLForUbiquityContainerIdentifier:方 法獲取,identifier是雲端存儲容器的惟一標識,若是傳入nil則表明第一個容器(事實上這個容器能夠經過前面生成的 「kctest.entiements」中的Ubiquity Container Identifiers來獲取。如上圖能夠看到這是一個數組,能夠配置多個容器,例如咱們的第一個容器標識是 「iCloud.$(CFBundleIdentifier)」,其中$(CFBundleIdentifier)是Bundle ID,那麼根據應用的Bundle ID就能夠得知第一個容器的標識是「iCloud.com.cmjstudio.kctest」。)。下面是經常使用的文檔操做方法:atom
-(void)saveToURL:forSaveOperation:completionHandler::將指定URL的文檔保存到iCloud(能夠是新增或者覆蓋,經過saveOperation參數設定)。url
-(void)openWithCompletionHandler::打開當前文檔。
注意:刪除一個iCloud文檔是使用NSFileManager的removeItemAtURL:error:方法來完成的。
由 於實際開發過程當中數據的存儲和讀取狀況是複雜的,所以UIDocument在設計時並無提供統一的存儲方式來保存數據,而是但願開發者本身繼承 UIDocument類並重寫-(id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError和-(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError方法來根據不一樣的文檔類型本身來操做數據(contents)。這兩個方法分別在保存文檔 (-(void)saveToURL:forSaveOperation:completionHandler:)和打開文檔 (-(void)openWithCompletionHandler:)時調用。一般在子類中會定義一個屬性A來存儲文檔數據,當保存文檔時,會經過 -(id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError方法將A轉化爲NSData或者NSFileWrapper(UIDocument保存數據的本質就是保存轉化獲得的NSData或 者NSFileWrapper);當打開文檔時,會經過-(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError方法將雲端下載的NSData或者NSFileWrapper數據轉化爲A對應類型的數據。爲了方便演示下面簡單定義一個繼承自 UIDocument的KCDocument類,在其中定義一個data屬性存儲數據:
1 // 2 // KCDocument.m 3 // kctest 4 // 5 // Created by Kenshin Cui on 14/4/5. 6 // Copyright (c) 2015年 cmjstudio. All rights reserved. 7 // 8 #import "KCDocument.h" 9 @interface KCDocument() 10 @end 11 @implementation KCDocument 12 #pragma mark - 重寫父類方法 13 /** 14 * 保存時調用 15 * 16 * @param typeName 17 * @param outError 18 * 19 * @return 20 */ 21 -(id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError{ 22 if (self.data) { 23 return [self.data copy]; 24 } 25 return [NSData data]; 26 } 27 /** 28 * 讀取數據時調用 29 * 30 * @param contents 31 * @param typeName 32 * @param outError 33 * 34 * @return 35 */ 36 -(BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError{ 37 self.data=[contents copy]; 38 return true; 39 } 40 @end
如 果要加載iCloud中的文檔列表就須要使用另外一個類NSMetadataQuery,一般考慮到網絡的緣由並不會一次性加載全部數據,而利用 NSMetadataQuery並指定searchScopes爲NSMetadataQueryUbiquitousDocumentScope來限制 查找iCloud文檔數據。使用NSMetadataQuery還能夠經過謂詞限制搜索關鍵字等信息,並在搜索完成以後經過通知的形式通知客戶端搜索的情 況。
你們都知道微軟的OneNote雲筆記本軟件,經過它能夠實現多種不一樣設置間的筆記同步,這裏就簡單實現一個基於iCloud服務的 筆 記軟件。在下面的程序中實現筆記的新增、修改、保存、讀取等操做。程序界面大體以下,點擊界面右上方增長按鈕增長一個筆記,點擊某個筆記能夠查看並編輯。
在主視圖控制器首先查詢全部iCloud保存的文檔並在查詢通知中遍歷查詢結果保存文檔名稱和建立日期到UITableView展現;其次當用戶點擊了增長按鈕會調用KCDocument完成文檔添加並導航到文檔詳情界面編輯文檔內容。
1 // 2 // KCMainTableViewController.m 3 // kctest 4 // 5 // Created by Kenshin Cui on 14/4/5. 6 // Copyright (c) 2015年 cmjstudio. All rights reserved. 7 // 8 #import "KCMainTableViewController.h" 9 #import "KCDocument.h" 10 #import "KCDetailViewController.h" 11 #define kContainerIdentifier @"iCloud.com.cmjstudio.kctest" // 容器id,能夠從生產的entitiements文件中查看Ubiquity Container Identifiers(注意其中 的$(CFBundleIdentifier)替換爲BundleID) 12 @interface KCMainTableViewController () 13 @property (strong,nonatomic) KCDocument *document;//當前選中的管理對象 14 @property (strong,nonatomic) NSMutableDictionary *files; //現有文件名、建立日期集合 15 @property (strong,nonatomic) NSMetadataQuery *dataQuery;//數據查詢對象,用於查詢iCloud文檔 16 @end 17 @implementation KCMainTableViewController 18 #pragma mark - 控制器視圖方法 19 - (void)viewDidLoad { 20 [super viewDidLoad]; 21 22 [self loadDocuments]; 23 } 24 #pragma mark - UI事件 25 //新建文檔 26 - (IBAction)addDocumentClick:(UIBarButtonItem *)sender { 27 UIAlertController *promptController=[UIAlertController alertControllerWithTitle:@"KCTest" message:@"請輸入筆記名稱" preferredStyle:UIAlertControllerStyleAlert]; 28 [promptController addTextFieldWithConfigurationHandler:^(UITextField *textField) { 29 textField.placeholder=@"筆記名稱"; 30 }]; 31 UIAlertAction *okAction=[UIAlertAction actionWithTitle:@"肯定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 32 UITextField *textField= promptController.textFields[0]; 33 [self addDocument:textField.text]; 34 }]; 35 [promptController addAction:okAction]; 36 UIAlertAction *cancelAction=[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 37 38 }]; 39 [promptController addAction:cancelAction]; 40 [self presentViewController:promptController animated:YES completion:nil]; 41 } 42 #pragma mark - 導航 43 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 44 if ([segue.identifier isEqualToString:@"noteDetail"]) { 45 KCDetailViewController *detailController= segue.destinationViewController; 46 detailController.document=self.document; 47 } 48 } 49 #pragma mark - 屬性 50 -(NSMetadataQuery *)dataQuery{ 51 if (!_dataQuery) { 52 //建立一個iCloud查詢對象 53 _dataQuery=[[NSMetadataQuery alloc]init]; 54 _dataQuery.searchScopes=@[NSMetadataQueryUbiquitousDocumentsScope]; 55 //注意查詢狀態是經過通知的形式告訴監聽對象的 56 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(metadataQueryFinish:) name:NSMetadataQueryDidFinishGatheringNotification object:_dataQuery];//數據獲取完成通知 57 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(metadataQueryFinish:) name:NSMetadataQueryDidUpdateNotification object:_dataQuery];//查詢更新通知 58 } 59 return _dataQuery; 60 } 61 #pragma mark - 私有方法 62 /** 63 * 取得雲端存儲文件的地址 64 * 65 * @param fileName 文件名,若是文件名爲nil則從新建立一個url 66 * 67 * @return 文件地址 68 */ 69 -(NSURL *)getUbiquityFileURL:(NSString *)fileName{ 70 //取得雲端URL基地址(參數中傳入nil則會默認獲取第一個容器) 71 NSURL *url= [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:kContainerIdentifier]; 72 //取得Documents目錄 73 url=[url URLByAppendingPathComponent:@"Documents"]; 74 //取得最終地址 75 url=[url URLByAppendingPathComponent:fileName]; 76 return url; 77 } 78 /** 79 * 添加文檔到iCloud 80 * 81 * @param fileName 文件名稱(不包括後綴) 82 */ 83 -(void)addDocument:(NSString *)fileName{ 84 //取得保存URL 85 fileName=[NSString stringWithFormat:@"%@.txt",fileName]; 86 NSURL *url=[self getUbiquityFileURL:fileName]; 87 88 /** 89 建立雲端文檔操做對象 90 */ 91 KCDocument *document= [[KCDocument alloc]initWithFileURL:url];