一、iCloud云服务
iCloud是苹果提供的云端服务,用户能够将通讯录、备忘录、邮件、照片、音乐、视频等备份到云服务器并在各个苹果设备间直接进行共享而无需关心数据同步问题。甚至即使你的设备丢失后在一台新的设备上也能够通过Apple ID登录同步。
苹果已经将云端存储功能开放给开发人员。能够存储两类数据:
key-value data
:
分享小量的非关键配置数据到应用的多个实例。使用相似于NSUserDefault
document
:
存储用户文档和应用数据到用户的iCloud账户
进行iCloud开发的准备工作:
- 在开发人员中心上创建AppleID,启用iCloud服务
- 生成相应的配置文件(
Provisioning Profile
)。这里能够使用通配Bundle ID
- 以上2步是针对真机的,调试模拟器能够忽略
- 打开项目的
Capabilities
。找到iCloud服务并开启它 - 在iCloud服务的
Service
中勾选Key-value storae
和iCloud Documents
- 你的项目中就会多出一个
entitlements
文件
- 里面的内容是自己主动生成的,就像这种
- 不管真机还是模拟器,都须要进入手机的设置中登陆iCloud账号
二、Key-Value的iCloud存储
使用和NSUserDefault
几乎相同,都是以键值对的形式存储。
使用实例:
#import "iCloudKeysViewController.h"
@interface iCloudKeysViewController()
@property (strong, nonatomic) NSUbiquitousKeyValueStore *keyStore;
@property (strong, nonatomic) IBOutlet UILabel *textLabel;
@end
@implementation iCloudKeysViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.textLabel.text = @"Ready Go";
//获取iCloud配置首选项
self.keyStore = [NSUbiquitousKeyValueStore defaultStore];
//注冊通知中心,当配置发生改变的时候。发生通知
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(ubiquitousKeyValueStoreDidChange:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:keyStore];
}
/* UI点击,点击改变button */
- (IBAction)changeKey
{
[self.keyStore setString:@"Hello World" forKey:@"MyString"];
[self.keyStore synchronize];
NSLog(@"Save key");
}
/* 监听通知,当配置发生改变的时候会调用 */
- (void)ubiquitousKeyValueStoreDidChange:(NSNotification *)notification
{
NSLog(@"External Change detected");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Change detected"
message:@"Change detected"
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil, nil];
[alert show];
//显示改变的信息
textLabel.text = [keyStore stringForKey:@"MyString"];
}
@end
三、Document的iCloud存储
核心类UIDocument
- 文档存储主要是使用
UIDocument
类来完毕。这个类提供了新建、改动、查询文档、打开文档、删除文档的功能。 UIDocument
对文档的新增、改动、删除、读取全部基于一个云端URL来完毕,对于开发人员而言没有本地和云端之分。这样大大简化了开发过程。
云端URL的获取方式:
- 调用
NSFileManager
的对象方法
-(NSURL *)URLForUbiquityContainerIdentifier:(NSString *)identifier;
- 上面须要传递一个云端存储容器的唯一标示,这个能够去自己主动生成的
entitlements
文件查看Ubiquity Container Identifiers
字段获得,假设传nil
,代表第一个容器
补充知识 :
默认的第一个容器标识是
iCloud.$(CFBundleIdentifier)
,
当中$(CFBundleIdentifier)
代表Bundle ID
,那么依据应用的Bundle ID
就能够得知我的第一个容器的标识是iCloud.com.liuting.icloud.iCloudTest
UIDocument的对象方法:
/* 将指定URL的文档保存到iCloud(能够是新增或者覆盖,通过saveOperation參数设定)*/
- (void)saveToURL:(NSURL *)url
forSaveOperation:(UIDocumentSaveOperation)saveOperation
completionHandler:(void (^)(BOOL success))completionHandler;
/* 保存操作option */
typedef NS_ENUM(NSInteger, UIDocumentSaveOperation) {
UIDocumentSaveForCreating,/* 创建 */
UIDocumentSaveForOverwriting/* 覆盖写入 */
};
/* 打开文档,參数是打开文档成功回调 */
- (void)openWithCompletionHandler:(void (^)(BOOL success))completionHandler;
删除文档使用的是NSFileManager的对象方法:
/* 删除指定URL下的文件 */
- (BOOL)removeItemAtURL:(NSURL *)URL
error:(NSError **)error;
注意事项:
UIDocument
在设计的时候,没有提供统一的存储方式来存储数据。须要我们去继承它,重写2个对象方法自己操作数据
/*
保存文档时调用
@param typeName 文档文件类型
@param outError 错误信息输出
@return 文档数据
*/
-(id)contentsForType:(NSString *)typeName
error:(NSError **)outError;
/*
读取数据时调用
@param contents 文档数据
@param typeName 文档文件类型
@param outError 错误信息输出
@return 读取是否成功
*/
-(BOOL)loadFromContents:(id)contents
ofType:(NSString *)typeName
error:(NSError **)outError;
UIDocument
保存数据的本质:
将A相应类型的数据转化为云端存储的NSData
或者NSFileWrapper
数据UIDocument
读取数据的本质:
将云端下载的NSData
或者NSFileWrapper
数据转化为A相应类型的数据
以下是我自己定义的Document类,继承于UIDocument:
LTDocument.h文件
#import <UIKit/UIKit.h>
@interface LTDocument : UIDocument
@property (strong, nonatomic) NSData *data;/*< 文档数据 */
@end
LTDocument.m文件
#import "LTDocument.h"
@implementation LTDocument
#pragma mark - 重写父类方法
/**
* 保存时调用
* @param typeName 文档文件类型后缀
* @param outError 错误信息输出
* @return 文档数据
*/
- (id)contentsForType:(NSString *)typeName
error:(NSError *__autoreleasing *)outError
{
if (!self.data) {
self.data = [NSData data];
}
return self.data;
}
/**
* 读取数据时调用
* @param contents 文档数据
* @param typeName 文档文件类型后缀
* @param outError 错误信息输出
* @return 读取是否成功
*/
- (BOOL)loadFromContents:(id)contents
ofType:(NSString *)typeName
error:(NSError *__autoreleasing *)outError
{
self.data = [contents copy];
return true;
}
@end
- 假设要载入iCloud中的文档列表,就须要使用还有一个类
NSMetadataQuery
- 通常考虑到网络的原因并不会一次性载入全部数据。而利用
NSMetadataQuery
并指定searchScopes
为NSMetadataQueryUbiquitousDocumentScope
来限制查找iCloud文档数据。 - 使用
NSMetadataQuery
还能够通过谓词限制搜索关键字等信息。并在搜索完毕之后通过通知的形式通知client搜索的情况。
以下是使用演示样例:
1. 属性定义和宏定义:
#import "ViewController.h"
#import "LTDocument.h"
#define kContainerIdentifier @"iCloud.com.liuting.icloud.iCloudTest"
@interface ViewController () <UITableViewDataSource,UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITextField *documentField;/*< 输入框 */
@property (weak, nonatomic) IBOutlet UILabel *documentShowLable;/*< 显示栏 */
@property (weak, nonatomic) IBOutlet UITableView *documentTableView;/* 文档列表 */
/* 文档文件信息,键为文件名称。值为创建日期 */
@property (strong, nonatomic) NSMutableDictionary *files;
@property (strong, nonatomic) NSMetadataQuery *query;/*< 查询文档对象 */
@property (strong, nonatomic) LTDocument *document;/*< 当前选中文档 */
@end
2. 获取云端URL方法:
/**
* 取得云端存储文件的地址
* @param fileName 文件名称。假设文件名称为nil,则又一次创建一个URL
* @return 文件地址
*/
- (NSURL *)getUbiquityFileURL:(NSString *)fileName{
//取得云端URL基地址(參数中传入nil则会默认获取第一个容器),须要一个容器标示
NSFileManager *manager = [NSFileManager defaultManager];
NSURL *url = [manager URLForUbiquityContainerIdentifier:kContainerIdentifier];
//取得Documents文件夹
url = [url URLByAppendingPathComponent:@"Documents"];
//取得终于地址
url = [url URLByAppendingPathComponent:fileName];
return url;
}
3. 查询文档列表方法
/* 从iCloud上载入全部文档信息 */
- (void)loadDocuments
{
if (!self.query) {
self.query = [[NSMetadataQuery alloc] init];
self.query.searchScopes = @[NSMetadataQueryUbiquitousDocumentsScope];
//注意查询状态是通过通知的形式告诉监听对象的
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(metadataQueryFinish:)
name:NSMetadataQueryDidFinishGatheringNotification
object:self.query];//数据获取完毕通知
[center addObserver:self
selector:@selector(metadataQueryFinish:)
name:NSMetadataQueryDidUpdateNotification
object:self.query];//查询更新通知
}
//開始查询
[self.query startQuery];
}
/* 查询更新或者数据获取完毕的通知调用 */
- (void)metadataQueryFinish:(NSNotification *)notification
{
NSLog(@"数据获取成功!");
NSArray *items = self.query.results;//查询结果集
self.files = [NSMutableDictionary dictionary];
//变量结果集。存储文件名称称、创建日期
[items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSMetadataItem *item = obj;
//获取文件名称
NSString *fileName = [item valueForAttribute:NSMetadataItemFSNameKey];
//获取文件创建日期
NSDate *date = [item valueForAttribute:NSMetadataItemFSContentChangeDateKey];
NSDateFormatter *dateformate = [[NSDateFormatter alloc]init];
dateformate.dateFormat = @"YY-MM-dd HH:mm";
NSString *dateString = [dateformate stringFromDate:date];
//保存文件名称和文件创建日期
[self.files setObject:dateString forKey:fileName];
}];
//表格刷新
self.documentShowLable.text = @"";
[self.documentTableView reloadData];
}
4. UI点击事件
#pragma mark - UI点击事件
/* 点击加入文档 */
- (IBAction)addDocument:(id)sender {
//提示信息
if (self.documentField.text.length <= 0) {
NSLog(@"请输入要创建的文档名");
self.documentField.placeholder = @"请输入要创建的文档名";
return;
}
//创建文档URL
NSString *text = self.documentField.text;
NSString *fileName = [NSString stringWithFormat:@"%@.txt",text];
NSURL *url = [self getUbiquityFileURL:fileName];
//创建云端文档对象
LTDocument *document = [[LTDocument alloc] initWithFileURL:url];
//设置文档内容
NSString *dataString = @"hallo World";
document.data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
//保存或创建文档。UIDocumentSaveForCreating是创建文档
[document saveToURL:url
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success)
{
if (success) {
NSLog(@"创建文档成功.");
self.documentField.text = @"";
//从iCloud上载入全部文档信息
[self loadDocuments];
}else{
NSLog(@"创建文档失败.");
}
}];
}
/* 点击改动文档 */
- (IBAction)saveDocument:(UIButton *)sender {
if ([sender.titleLabel.text isEqualToString:@"改动文档"]) {
self.documentField.text = self.documentShowLable.text;
[sender setTitle:@"保存文档" forState:UIControlStateNormal];
} else if([sender.titleLabel.text isEqualToString:@"保存文档"]) {
[sender setTitle:@"改动文档" forState:UIControlStateNormal];
self.documentField.placeholder = @"请输入改动的文档内容";
//要保存的文档内容
NSString *dataText = self.documentField.text;
NSData *data = [dataText dataUsingEncoding:NSUTF8StringEncoding];
self.document.data = data;
//保存或创建文档,UIDocumentSaveForOverwriting是覆盖保存文档
[self.document saveToURL:self.document.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success)
{
NSLog(@"保存成功!"
);
self.documentShowLable.text = self.documentField.text;
self.documentField.text = @"";
}];
}
}
/* 点击删除文档 */
- (IBAction)removeDocument:(id)sender {
//提示信息
if (self.documentField.text.length <= 0) {
self.documentField.placeholder = @"请输入要删除的文档名";
return;
}
//推断要删除的文档是否存在
NSString *text = self.documentField.text;
NSString *fileName = [NSString stringWithFormat:@"%@.txt",text];
NSArray *fileNames = [self.files allKeys];
if (![fileNames containsObject:fileName]) {
NSLog(@"没有要删除的文档");
return;
}
//创建要删除的文档URL
NSURL *url = [self getUbiquityFileURL:fileName];
NSError *error = nil;
//删除文档文件
[[NSFileManager defaultManager] removeItemAtURL:url error:&error];
if (error) {
NSLog(@"删除文档过程中错误发生,错误信息:%@",error.localizedDescription);
return;
}
//从集合中删除
[self.files removeObjectForKey:fileName];
self.documentField.text = @"";
}
5. 视图控制器初始化和列表显示
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.documentTableView.delegate = self;
self.documentTableView.dataSource = self;
/* 从iCloud上载入全部文档信息 */
[self loadDocuments];
}
#pragma mark - UITableView数据源
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return self.files.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identtityKey = @"myTableViewCellIdentityKey1";
UITableViewCell *cell =
[self.documentTableView dequeueReusableCellWithIdentifier:identtityKey];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:identtityKey];
}
//显示文档名和文档创建日期
NSArray *fileNames = self.files.allKeys;
NSString *fileName = fileNames[indexPath.row];
cell.textLabel.text = fileName;
cell.detailTextLabel.text = [self.files valueForKey:fileName];
return cell;
}
#pragma mark - UITableView代理方法
/* 点击文档列表的当中一个文档调用 */
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.documentTableView cellForRowAtIndexPath:indexPath];
//获取文档URL
NSURL *url = [self getUbiquityFileURL:cell.textLabel.text];
//创建文档操作对象
LTDocument *document = [[LTDocument alloc] initWithFileURL:url];
self.document = document;
//打开文档并读取文档内容
[document openWithCompletionHandler:^(BOOL success) {
if(success){
NSLog(@"读取数据成功.");
NSString *dataText = [[NSString alloc] initWithData:document.data
encoding:NSUTF8StringEncoding];
self.documentShowLable.text = dataText;
}else{
NSLog(@"读取数据失败.");
}
}];
}
@end
上面的代码Demo点这里:LearnDemo里面的iCloudDemo
这个代码Demo仅仅有Document的实例代码
假设有什么问题请在下方评论区中提出!
O(∩_∩)O哈。