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];