iCloud开发: key-value Storage,CloudKit,iCloud Documents
iCloud开发
- 使用iCloud的开发的前提是要有开发者账号,个人或企业均可。
iCloud三种类型的存储方式
| 类型 | 说明 |
|---|---|
| key-value storage | 键值对的存储服务,用于一些简单的数据存储 |
| iCloud Documents | 文档存储服务,用于将文件保存到iCloud中 |
| CloudKit | 云端数据库服务 |
项目配置
- 这些是基本配置,三种方式都需要这些配置。
1、iCloud 官网配置
创建支持iCloud的Apple ID,并关联上相应的iCloud容器。


输入Identifier即可创建完毕

2、本地Xcode配置
- 添加CloudKit框架到项目
- Xcode配置信息: 选择项目->targets->Capabilities->iCloud->打开开关

- 1、勾选自己要开启的Services
- 2、选择对应的Containers,可以使用默认,也可以指定固定的
- 3、观察steps是否全部success
- 4、修改entitlements

注意事项
- demo是iOS和macos数据同步,所以配置稍微复杂点
- 如果只是iOS设备间进行同步,不用修改Containers,使用默认即可,第四步不用修改
- 其中
iCloud Key-Value Store默认是用team id和bundle identifier做标识,因为mac和ios的bundle identifier不一致,所以要手动指定为统一的
一、key-value storage
- 一般用于同步少量数据或者进行一些配置性质的数据同步,使用简单。
- 使用
NSUbiquitousKeyValueStore对象进行数据读写
1、获取默认store
// 获取默认的store,这就是在xxx.entitlements里配置的`iCloud Key-Value Store`
self.keyValueStore = [NSUbiquitousKeyValueStore defaultStore];
2、写入数据
NSLog(@"写入iCloud数据:%zd",self.number);
[self.keyValueStore setLongLong:self.number forKey:@"number"];
// 同步数据,避免冲突
[self.keyValueStore synchronize];
3、读取数据
// 在获取到store后,读取iCloud数据
self.number = [self.keyValueStore longLongForKey:@"number"];
4、监听数据改变(多台设备)
- 需要实时知道一些配置的变更,特别是在你有多台设备时(如同时拥有iPhone和iPad)
// 添加监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataChanged:) name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:nil];
- (void)dataChanged:(NSNotification *)noti{
// 监听到keyvalue值改变就会触发这个通知
NSLog(@"keyvalue改变了:%@",noti);
if ([noti.userInfo[NSUbiquitousKeyValueStoreChangedKeysKey] containsObject:@"number"]) {
self.number = [noti.object longLongForKey:@"number"];
NSLog(@"keyvalue改变了:%zd",self.number);
self.myLabel.stringValue = [NSString stringWithFormat:@"%zd",self.number];
}
}
- iPhone上改变,mac上监听
二、CloudKit
iCloud官网配置
1、选择某个容器

2、新建Record,类似表名称
- 需要打开recordName的索引,这样客户端才能查询数据

3、新建Field,类似表字段

- Filed 类型

4、进入Data,创建记录
- 创建记录后才能在客户端进行增删查改



编码开始
- 以上配置完毕可尝试在客户端进行增删查改。
- 初始化容器对象
// 初始化容器对象
self.container = [CKContainer containerWithIdentifier:ContainerID];
1、查询数据
- 判断iCloud账户状态
accountStatusWithCompletionHandler: - 获取私有数据库对象
weakSelf.container.privateCloudDatabase - 查询数据
performQuery: inZoneWithID:completionHandler:
if(self.container){
// 访问私有数据库
__weak typeof(self) weakSelf = self;
[weakSelf.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
// 只有登录iCloud才能读取
if (accountStatus == CKAccountStatusAvailable) {
// 获取私有数据库实例
CKDatabase *db = weakSelf.container.privateCloudDatabase;
CKQuery *query = [[CKQuery alloc] initWithRecordType:RecordType predicate:[NSPredicate predicateWithValue:YES]];
// 查询数据
[db performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
if(!error){
weakSelf.preOrders = [NSMutableArray arrayWithArray:results];
NSLog(@"%@",results);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.tableView reloadData];
});
}else{
NSLog(@"Error:%@",error);
}
}];
}else {
NSLog(@"登录iCloud错误");
}
}];
}else {
NSLog(@"连接iCloud错误");
}
- 返回CKRecord类型的数据,可以通过
objectForKey方法直接读取
<CKRecord: 0x101813050; recordID=FF125857-926B-4DC5-B972-9E6A6502B5A5:(_defaultZone:__defaultOwner__), recordChangeTag=jzjms8b2, values={\n amount = 9144;\n time = \"2021-08-20 09:33:45 +0000\";\n}, recordType=Water>
NSDate *time = [ck objectForKey:@"time"];
NSInteger count = [ck objectForKey:@"amount"];
2、新增数据
判断iCloud账户状态
accountStatusWithCompletionHandler:获取私有数据库对象
weakSelf.container.privateCloudDatabase创建Record
CKRecord *record = [[CKRecord alloc] initWithRecordType:RecordType];保存数据
saveRecord: completionHandler:if(self.container){
// 1 访问私有数据库
__weak typeof(self) weakSelf = self;
[self.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
// 1.1 只有登录iCloud才能读取
if (accountStatus == CKAccountStatusAvailable) {
// 1.2 获取私有数据库实例
CKDatabase *db = weakSelf.container.privateCloudDatabase;
// 新增数据
CKRecord *record = [[CKRecord alloc] initWithRecordType:RecordType]; record[@"time"] = [NSDate date];
record[@"amount"] = @(arc4random()%10000); // 1.3 保存数据
[db saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(error) {
NSLog(@"%@", error);
} else {
NSLog(@"Saved successfully:%@",record);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf queryAction:nil];
});
}
}];
}else {
NSLog(@"登录iCloud错误");
}
}];
}
3、删除数据
- 判断iCloud账户状态
accountStatusWithCompletionHandler: - 获取私有数据库对象
weakSelf.container.privateCloudDatabase - 查询Record是否存在
fetchRecordWithID: completionHandler: - 删除数据
deleteRecordWithID: completionHandler:
if(self.container){
// 访问私有数据库
__weak typeof(self) weakSelf = self;
[weakSelf.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
// 只有登录iCloud才能读取
if (accountStatus == CKAccountStatusAvailable) {
// 获取私有数据库实例
CKDatabase *db = weakSelf.container.privateCloudDatabase;
[db fetchRecordWithID:record.recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(error) {
NSLog(@"%@", error);
} else {
NSLog(@"查询成功:%@",record);
[db deleteRecordWithID:record.recordID completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
if(error) {
NSLog(@"%@", error);
} else {
NSLog(@"删除成功:%@",record);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.preOrders removeObjectAtIndex:indexPath.row];
[weakSelf.tableView reloadData];
});
}
}];
}
}];
}else {
NSLog(@"登录iCloud错误");
}
}];
}else {
NSLog(@"连接iCloud错误");
}
4、修改数据
- 判断iCloud账户状态
accountStatusWithCompletionHandler: - 获取私有数据库对象
weakSelf.container.privateCloudDatabase - 查询Record是否存在
fetchRecordWithID: completionHandler: - 保存数据
saveRecord: completionHandler:
if(self.container){
// 访问私有数据库
__weak typeof(self) weakSelf = self;
NSInteger count = [weakSelf.countTextField.text integerValue];
[weakSelf.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError * _Nullable error) {
// 只有登录iCloud才能读取
if (accountStatus == CKAccountStatusAvailable) {
// 获取私有数据库实例
CKDatabase *db = weakSelf.container.privateCloudDatabase;
[db fetchRecordWithID:weakSelf.record.recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(error) {
NSLog(@"%@", error);
} else {
NSLog(@"查询成功:%@",record);
record[@"amount"] = @(count);
[db saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(error) {
NSLog(@"%@", error);
} else {
NSLog(@"更改成功:%@",record);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"refreshUI" object:nil];
[weakSelf.navigationController popViewControllerAnimated:YES];
});
}
}];
}
}];
}else {
NSLog(@"登录iCloud错误");
}
}];
}else {
NSLog(@"连接iCloud错误");
}
5、监听iCloud账户状态
// 账户信息状态改变了会触发这个信息,可以尝试刷新数据
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataChanged:) name:CKAccountChangedNotification object:nil];
- (void)dataChanged:(NSNotification *)noti{
NSLog(@"账户信息状态改变了:%@",noti);
[self queryAction:nil];
}
CloudKit 知识扫盲
- CKContainer 容器,或者沙盒,每个应用只能访问自己的容器。
- CKDatabase 顾名思义,数据库了,包含私有数据库和公有数据库,用户只能访问自己的私有数据库,一些不敏感的数据也可以存储在公有数据库中。
- CKRecord 数据记录,keyvalue形式存储的,存储一些基本类型(NSString,NSNumber,NSData,NSDate,CLLocation,CKAsset,CKReference等)
- CKRecordZone 类似分区,是用来保存Record的。所有的Record都是保存在这里,应用有一个默认的zone,也可以自定义zone。
- CKAsset 文件存储记录
- CKQuery 数据库查询对象,指定查询条件进行数据查询
三、iCloud Documents
1、Xcode配置
- 允许你把一份文档上传到iCloud中,然后其他设备再同步app上传的文档。

2、自定义UIDocument
- 首先继承UIDocument,实现自己的方法,做好NSData数据的转换
#import "MyDocument.h"
@implementation MyDocument
- (instancetype)initWithFileURL:(NSURL *)url image:(UIImage *)image
{
if (self = [super initWithFileURL:url])
{
_myImage = image;
}
return self;
}
// 写入数据前
- (nullable id)contentsForType:(NSString *)typeName error:(NSError **)outError{
// 只能返回NSData 或者 NSFileWrapper ,所以这里要转换图片
return UIImageJPEGRepresentation(_myImage, 0.7);
}
// 读取数据后
- (BOOL)loadFromContents:(id)contents ofType:(nullable NSString *)typeName error:(NSError **)outError {
if ([contents isKindOfClass:[NSData class]]) {
// 如果是NSData,还要转换成图片
_myImage = [UIImage imageWithData:contents];
}
return YES;
}
@end
3、保存图片
- 文件名可以随机生成
- 查询时进行模糊查询即可全部查出来
- 保存方法
saveToURL: forSaveOperation: completionHandler:
- (void)saveWithImage:(UIImage *)image{
if(self.baseURL){
// UIImage *image = [UIImage imageNamed:@"1"];
self.localImageView.image = image;
NSURL *bgURL = [self.baseURL URLByAppendingPathComponent:@"100JZPlg6M1DQht3xU.png"];
MyDocument *bgImg = [[MyDocument alloc] initWithFileURL:bgURL image:image];
[bgImg saveToURL:bgURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success) {
if (success)
{
NSLog(@"同步成功!");
}
else
{
NSLog(@"同步失败, 可以记录到本地等待下一次重新同步");
}
}];
}else{
NSLog(@"连接iCloud错误");
}
}
4、下载图片
- (IBAction)downloadClick {
// 进行文档同步
if(self.baseURL){
__weak typeof(self) weakSelf = self;
__block NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
// 查询数据范围
query.searchScopes = @[NSMetadataQueryUbiquitousDataScope];
// 查询条件 NSMetadataItemFSNameKey 按照文件名搜索
// query.predicate = [NSPredicate predicateWithFormat:@"%K == '100JZPlg6M1DQht3xU.png'", NSMetadataItemFSNameKey];
// 模糊查询使用 *
query.predicate = [NSPredicate predicateWithFormat:@"%K like '*JZPlg6M1DQht3xU.png'", NSMetadataItemFSNameKey];
// 监听查询结果
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"Note:%@",note);
// query.results 查询结果数组,如果模糊匹配可能有多个
if (query.results.count > 0)
{
NSURL *fileURL = [(NSMetadataItem *)query.results.firstObject valueForAttribute:NSMetadataItemURLKey];
//加载背景图片
MyDocument *bgImage = [[MyDocument alloc] initWithFileURL:fileURL image:nil];
[bgImage openWithCompletionHandler:^(BOOL success) {
if (success)
{
NSLog(@"下载成功!");
weakSelf.backgroungImageView.image = bgImage.myImage;
}else{
NSLog(@"下载失败");
}
}];
}
// 查询完毕,关闭
[query stopQuery];
}];
// 开启查询
[query startQuery];
}
}
5、Mac版代码稍微有点区别
- 自定义NSDocument
#import "MyDocument.h"
@implementation MyDocument
// 读取数据后
- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError {
NSImage *im = [[NSImage alloc] initWithContentsOfURL:url];
if (im) {
self.myImage = im;
return YES;
}
return NO;
}
// 写入数据前
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
// 只能返回NSData 或者 NSFileWrapper ,所以这里要转换图片
NSData *data = self.myImage.TIFFRepresentation;
return data;
}
- (instancetype)initWithFileURL:(NSURL *)url image:(NSImage *)image
{
if (self = [super initWithContentsOfURL:url ofType:@"png" error:nil])
{
_myImage = image;
}
return self;
}
@end
- 保存图片
- (void)saveWithImage:(NSImage *)image{
if(self.baseURL){
self.localImageView.image = image;
NSURL *bgURL = [self.baseURL URLByAppendingPathComponent:@"100JZPlg6M1DQht3xU.png"];
MyDocument *bgImg = [[MyDocument alloc] initWithFileURL:bgURL image:image];
[bgImg saveToURL:bgURL ofType:@"png" forSaveOperation:NSSaveOperation completionHandler:^(NSError * _Nullable errorOrNil) {
if (!errorOrNil)
{
NSLog(@"同步成功!");
}
else
{
NSLog(@"同步失败, 可以记录到本地等待下一次重新同步:%@",errorOrNil);
}
}];
}else{
NSLog(@"连接iCloud错误");
}
}
- 下载图片
- (IBAction)downloadClick:(NSButton *)btn {
// 进行文档同步
if(self.baseURL){
__weak typeof(self) weakSelf = self;
__block NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
// 查询数据范围
query.searchScopes = @[NSMetadataQueryUbiquitousDataScope];
// 查询条件 NSMetadataItemFSNameKey 按照文件名搜索
// query.predicate = [NSPredicate predicateWithFormat:@"%K == '100JZPlg6M1DQht3xU.png'", NSMetadataItemFSNameKey];
// 模糊查询使用 *
query.predicate = [NSPredicate predicateWithFormat:@"%K like '*JZPlg6M1DQht3xU.png'", NSMetadataItemFSNameKey];
// 监听查询结果
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"Note:%@",note);
// query.results 查询结果数组,如果模糊匹配可能有多个
if (query.results.count > 0)
{
NSURL *fileURL = [(NSMetadataItem *)query.results.firstObject valueForAttribute:NSMetadataItemURLKey];
//加载背景图片
MyDocument *bgImage = [[MyDocument alloc] initWithFileURL:fileURL image:nil];
if([bgImage readFromURL:fileURL ofType:@"png" error:nil]){
NSLog(@"下载成功!");
weakSelf.localImageView.image = bgImage.myImage;
}
else{
NSLog(@"下载失败");
}
}
// 查询完毕,关闭
[query stopQuery];
}];
// 开启查询
[query startQuery];
}
}
6、监听数据改变
- 监听通知
NSMetadataQueryDidFinishGatheringNotification - 查询里面的
[query stopQuery];需要注释掉
// 监听数据改变
[center addObserverForName:NSMetadataQueryDidFinishGatheringNotification object:query queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
if (query.results.count > 0)
{
NSURL *fileURL = [(NSMetadataItem *)query.results.firstObject valueForAttribute:NSMetadataItemURLKey];
//加载背景图片
MyDocument *bgImage = [[MyDocument alloc] initWithFileURL:fileURL image:nil];
if([bgImage readFromURL:fileURL ofType:@"png" error:nil]){
NSLog(@"下载成功!");
weakSelf.localImageView.image = bgImage.myImage;
}
else{
NSLog(@"下载失败");
}
}
}];
iCloud开发: key-value Storage,CloudKit,iCloud Documents的更多相关文章
- iOS开发——数据持久化Swift篇&iCloud云存储
iCloud云存储 import UIKit class ViewController: UIViewController { override func viewDidLoad() { super. ...
- 真正解决方案:phpMyAdmin #1089 - Incorrect prefix key; the storage engine doesn't support unique prefix key
先直接给解决方案: 点击A_I后,不要输入大小,直接点击执行!!! 分析 当你在使用phpMyAdmin 创建数据库表的时候,一般我们需要设置一个主键,然后让其自增长,但是有时候当你设置完成后,你可能 ...
- 错误:java.io.FileNotFoundException: /storage/emulated/0/Documents/eclipse-inst-win64.exe
在Android服务的最佳实例中:https://www.cnblogs.com/hh8888-log/p/10300972.html,在最后运行的时候,点击Start Download按钮总是会报一 ...
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
--系统应用与系统服务 iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用 ...
- iOS--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook等系统服务开发汇总
iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...
- iOS开发系列通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开
--系统应用与系统服务 iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用 ...
- iOS开发系列——内购、GameCenter、iCloud、Passbook系统服务开发汇总
社交 Social 现在很多应用都内置“社交分享”功能,可以将看到的新闻.博客.广告等内容分享到微博.微信.QQ.空间等,其实从iOS6.0开始苹果官方就内置了Social.framework专门来实 ...
- iOS项目iCloud及CloudKit Dashboard运用
CloudKit是苹果推出的基于iCloud的一个云端数据存储服务.其 主要由下面两部分组成: 一个仪表web页面,用于管理公开数据的记录类型. 一组API接口,用于iCloud和设备之间的数据传递. ...
- iOS开发——高级技术&iCloud服务
iCloud服务 iCloud 是苹果提供的云端服务,用户可以将通讯录.备忘录.邮件.照片.音乐.视频等备份到云服务器并在各个苹果设备间直接进行共享而无需关心数据同步问题,甚至 即使你的设备丢失后在一 ...
随机推荐
- BeanUtils JavaBean 工具包使用
感谢原文作者:小老弟 原文链接:https://www.cnblogs.com/syncmr/p/10523576.html 目录 简介 BeanUtils类 使用示例 ConvertUtils 功能 ...
- mysql连接出错:ERROR 1040 (HY000): Too many connections
1.查看mysql的最大连接数:show variables like '%max_connections%'; 2. 查看服务器响应的最大连接数: 3. 设置最大连接数: set GLOBAL m ...
- 一键部署mysql 无修改直接cp 执行 100% 有效
一键部署mysql 无修改直接cp 执行 100% 有效 将安装包拖至/opt目录下,编一个脚本文件,然后source执行脚本,等脚本执行完成, 即可使用mysql -u root -p点击 ...
- SQLServer误删ReportServerTempDB导致打不开其他库——解决方案
无意间删除了ReportServerTempDB库和ReportServer 导致其他库连接不上 错误:报表服务器无法打开与报表服务器数据库的连接.所有请求和处理都要求与数据库建立连接. 解决方案: ...
- 同事提出个我从未想过的问题,为什么Kubernetes要"多此一举"推出静态Pod概念?
同事提出个我从未想过的问题,为什么Kubernetes要"多此一举"推出静态Pod概念? 我们知道k8s中Pod可以说是一个合格的容器小管家,Pod 被设计成支持多个容器可以一起进 ...
- python解释器的安装以及解释器多版本共存
版本介绍 python创始人:gukido(龟叔) python1.X 原始版本,几乎不用 python2.x 最高版本2.7(2020年之后不再维护) python3.x 最高版本3.9(一般用3. ...
- Solution -「USACO 2020.12 P」Spaceship
\(\mathcal{Description}\) Link. Bessie 在一张含 \(n\) 个结点的有向图上遍历,站在某个结点上时,她必须按下自己手中 \(m\) 个按钮中处于激活状态 ...
- CentOS7编译安装升级openssh8.7p1
因生成环境服务器安全扫描出的漏洞问题,只能升级最新的openssh,适用于centos6和centos7的升级使用. 一.编译前工作 openssl版本要求1.0.1以上,zlib版本要求1.1.4以 ...
- 【Python自动化Excel】pandas操作Excel的“分分合合”
话说Excel数据表,分久必合.合久必分.Excel数据表的"分"与"合"是日常办公中常见的操作.手动操作并不困难,但数据量大了之后,重复性操作往往会令人崩溃. ...
- 命令行下Git调用IDEA的diff功能
命令行下git diff, 有人欢喜有人厌, 本文以IDEA diff为例, 介绍如何更换Git的diff工具. IDEA diff IDEA作为一个图形化工具, 其实也提供了极少一部分命令行接口, ...
