iOS学习笔记32-iCloud入门
一、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点击,点击改变按钮 */
- (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
还可以通过谓词限制搜索关键字等信息,并在搜索完成之后通过通知的形式通知客户端搜索的情况。
下面是使用示例:
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
iOS学习笔记32-iCloud入门的更多相关文章
- iOS学习笔记-地图MapKit入门
代码地址如下:http://www.demodashi.com/demo/11682.html 这篇文章还是翻译自raywenderlich,用Objective-C改写了代码.没有逐字翻译,如有错漏 ...
- iOS学习笔记11-多线程入门
一.iOS多线程 iOS多线程开发有三种方式: NSThread NSOperation GCD iOS在每个进程启动后都会创建一个主线程,更新UI要在主线程上,所以也称为UI线程,是其他线程的父线程 ...
- IOS学习笔记48--一些常见的IOS知识点+面试题
IOS学习笔记48--一些常见的IOS知识点+面试题 1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...
- iOS学习笔记06—Category和Extension
iOS学习笔记06—Category和Extension 一.概述 类别是一种为现有的类添加新方法的方式. 利用Objective-C的动态运行时分配机制,Category提供了一种比继承(inher ...
- iOS学习笔记——AutoLayout的约束
iOS学习笔记——AutoLayout约束 之前在开发iOS app时一直以为苹果的布局是绝对布局,在IB中拖拉控件运行或者直接使用代码去调整控件都会发上一些不尽人意的结果,后来发现iOS在引入了Au ...
- IOS学习笔记25—HTTP操作之ASIHTTPRequest
IOS学习笔记25—HTTP操作之ASIHTTPRequest 分类: iOS2012-08-12 10:04 7734人阅读 评论(3) 收藏 举报 iosios5网络wrapper框架新浪微博 A ...
- IOS学习笔记之关键词@dynamic
IOS学习笔记之关键词@dynamic @dynamic这个关键词,通常是用不到的. 它与@synthesize的区别在于: 使用@synthesize编译器会确实的产生getter和setter方法 ...
- iOS学习笔记-精华整理
iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...
- iOS学习笔记10-UIView动画
上次学习了iOS学习笔记09-核心动画CoreAnimation,这次继续学习动画,上次使用的CoreAnimation很多人感觉使用起来很繁琐,有没有更加方便的动画效果实现呢?答案是有的,那就是UI ...
- iOS学习笔记总结整理
来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...
随机推荐
- Modelsim与Simulink协同仿真
当使用硬件描述语言(HDL)完成电路设计时,往往需要编写Testbench对所设计的电路进行仿真验证,测试设计电路的功能是否与预期的目标相符.而编写Testbench难度之大,这时可以借助交互式图形化 ...
- Oracle CRS/GI 进程介绍
在10g和11.1,Oracle的集群称为CRS(Oracle Cluster Ready Service), 在11.2,Oracle的集群称为GI(Grid Infrastructure). 对于 ...
- Mac OS X El Capitan系统完整性保护System Integrity Protection (SIP)
http://blog.csdn.net/yulimin/article/details/49992031 引言:前段时间经历了XCode编译器代码被注入的事件后,这次 Mac OS X El Cap ...
- java中IO流之字节字符流的总结概述
概念 这么庞大的体系里面,常用的就那么几个,我们把它们抽取出来,如下图: Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都 ...
- 《剑指offer》51:数组中的逆序对
题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000 ...
- javaweb基础(6)_servlet配置参数
一.ServletConfig讲解 1.1.配置Servlet初始化参数 在Servlet的配置文件web.xml中,可以使用一个或多个<init-param>标签为servlet配置一些 ...
- lua拷贝二进制文件的方法
使用lua拷贝二进制文件相比文本文件复杂一点,方法如下 function copyFunc(targetPath,sourcePath) local rf = io.open(sourcePath,& ...
- 【转】如何在VC下检测当前存在的串口及串口热拔插
当我们在用VS进行串口编程时,在打开串口前,经常想知道当前PC上存在多少个串口,哪些串口可用?哪些串口已经打开了,最好是在一个Combo Box中列表系统当前所有可用的串口以供选择,然而如何获取系统当 ...
- Django2.0里urls.py里配置的改变
从Django2.0开始,urls.py配置方法有很大改变. 1.把url函数换成path 2.不在使用^.$作为路由 3.其他地方以后再进一步研究 下面看一个列子: from django.cont ...
- python爬虫: 豆瓣电影top250数据分析
转载博客 https://segmentfault.com/a/1190000005920679 根据自己的环境修改并配置mysql数据库 系统:Mac OS X 10.11 python 2.7 m ...