iOS开发-iCloud的简单使用(3)-Cloud Kit的使用

好了,看完了key-value,看完了Document的存储,终于到了Cloud kit的使用了。

比起前边的两种,Cloud kit更像是一个数据库。苹果会提供一个dashboard作为云存储的管理端。

Cloud Kit 基础

cloud kit一共有7个基础类,简单了解一下这些基础类接下来的使用就比较容易理解了。

CKContainer

CKContainer类似于应用运行时的沙盒,一个应用只能访问自己的沙盒,同样的,一个应用也只能访问自己的Container。

通过初始化之后就可以使用

1
CKContainer *container = [CKContainer defaultContainer];

CKDatabase

CKDatabase很明显就是数据库,他拥有私有数据库公有数据库两种类型。用户只能访问自己的私有数据库,一些不敏感的数据也可以存储在公有数据库中。

1
2
3
4
//公有数据库
CKDatabase *datebase = container.publicCloudDatabase;
//私有数据库
CKDatabase *datebase = container.privateCloudDatabase;

CKRecord

CKRecord就是数据库中的一条数据记录,他通过key-value的方式来存储和获取数据。目前可以支持如下格式的数据:

  • NSString(swift中的Sting)
  • NSNumber
  • NSData
  • NSDate
  • CLLocation
  • CKReference
  • CKAsset

其中图片的数据类型需要先初始化一个URL,然后把图片保存到本地沙盒,生成URL,再创建CKAsset来存储。

CKRecordZone

CKRecordZone是用来保存Record的。所有的Record都是保存在这里,应用有一个默认的zone,也可以自定义zone

CKRecordIdentifier

CKRecordIdentifierRecord的唯一标示,用来确定Record在数据库中的位置。

CKReference

CKReference是一种引用关系。

CKAsset

CKAsset 为资源文件,比如之前提到的照片就是用这种方式存储的。

CloudKit Dashboard

更新:
这里还有一个WWDC的视频介绍Cloud dashboard
Build Better Apps with CloudKit Dashboard

CloudKit Dashboard 的入口在这里:

logo

你也可以直接访问地址

这个界面好像更新过了,和网上很多教程的样式都不太一样。新的界面长这样:

logo

新的界面包括开发版和产品版(线上版)两种,在开发的时候我们就只针对左边的开发版进行操作就可以了。无论是那个版本,都有五个相同的入口。

Data

Cloud kit的数据管理中心入口。在这里可以进行数据的字段,索引等内容设定。

Logs

查看Cloud kit的服务日志,显示数据库操作,推送通知以及对应环境中的其他活动。

Telemetry

查看对应环境中服务器端性能和数据库利用率的图表。

Public Database Usage

查看公共数据库的使用情况图标,包括活跃用户、请求频率等。

Api Access

管理API令牌和服务器密钥,允许对应环境进行web服务调用。

使用

这次我们只讲简单的操作,所以本篇文章就只使用 Data中的部分功能。

创建RECORD TYPE

首先点击Data , RECORD TYPE , Create New Type创建一个新的RECORD TYPE。

logo

这里注意,我们的RECORD TYPE Name 是Note,这个在之后代码里操作非常重要,所以记得不要写错了。

添加Field

有了表,接下来就是添加字段了。点击Add Field 然后输入字段名,选择类型就可以了。这里可以一次性添加多条,添加完点击保存就可以了。

logo

添加的内容有:

  • title String
  • content String
  • photo Asset

添加Indexs

为了搜索更方便一些,我们添加一些索引。
点击INDEXS,选择我们刚刚添加的Note,然后添加索引,添加的索引类型有QUERYABLE,SEARCHABLE,SORTABLE

logo

添加的内容有:

  • title QUERYABLE
  • title SEARCHABLE
  • title SORTABLE
  • content QUERYABLE
  • content SEARCHABLE
  • content SORTABLE
  • recordName QUERYABLE

添加完就是这样的,对照一下看看有没有添加错啊。

logo

添加数据

接下来,我们就可以给表里添加数据了。

选择RECORD ,确认LOAD RECORDS FROMQUERY FOR RECORDS OF TYPE 都正确了之后,点击Creat New Record…,在右边输入想要插入的数据,添加个几条就可以了。

logo

查询数据

随便添加几条数据之后,还是在这个页面,点击左侧的Query Records,就可以查询到数据了。

logo

到此,Cloud Dashboard的简单使用就介绍到这,接下来我们看看通过代码怎么来操作Cloud kit。
撸起袖子写代码吧。

通过代码操作Cloud Kit

使用Cloud Kit时,首先要先引入Cloud kit的框架。

1
#import <CloudKit/CloudKit.h>

添加数据

首先创建一个容器

1
2
//获取容器
CKContainer *container = [CKContainer defaultContainer];

然后获取数据库,这里我们使用公有数据库

1
2
//公共数据
CKDatabase *datebase = container.publicCloudDatabase;

接下来创建一条数据,这里的RECORD_TYPE_NAME就是之前创建表的名字Note

1
2
//创建保存数据
CKRecord *noteRecord = [[CKRecord alloc] initWithRecordType:RECORD_TYPE_NAME];

添加数据是使用Key-value的方式。

1
2
[noteRecord setValue:title forKey:@"title"];
[noteRecord setValue:content forKey:@"content"];

这里要特殊注意的是图片的处理,我们先要创建一个URL,然后把图片数据保存到沙盒中,生成一个URL,再创建CKAsset。

1
2
3
4
5
6
7
8
9
10
11
NSDate *dateID = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval timeInterval = [dateID timeIntervalSince1970] * 1000; //*1000表示到毫秒级,这样可以保证不会同时生成两个同样的id
NSString *idString = [NSString stringWithFormat:@"%.0f", timeInterval];
NSString *filePath = [NSString stringWithFormat:@"%@/%@",tempPath,idString];
NSURL *url = [NSURL fileURLWithPath:filePath];
[imageData writeToURL:url atomically:YES];
CKAsset *asset = [[CKAsset alloc]initWithFileURL:url];
[noteRecord setValue:asset forKey:@"photo"];

对了,还有CKAsset转UIImage的方法:

1
2
CKAsset *asset = [record objectForKey:@"photo"];
UIImage *image = [UIImage imageWithContentsOfFile:asset.fileURL.path];

这样数据的内容就处理好了,接下来我们只要把它保存起来就可以咯。

1
2
3
4
5
6
7
8
9
10
11
12
[datebase saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(!error)
{
NSLog(@"保存成功");
}
else
{
NSLog(@"保存失败");
NSLog(@"%@",error.description);
}
}];

整个代码合起来就是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
+ (void)saveCloudKitModelWithTitle:(NSString *)title content:(NSString *)content photoImage:(UIImage *)image
{
//获取容器
CKContainer *container = [CKContainer defaultContainer];
//公共数据
CKDatabase *datebase = container.publicCloudDatabase;
// //私有数据
// CKDatabase *datebase = container.privateCloudDatabase;
// //创建主键
// CKRecordID *noteID = [[CKRecordID alloc] initWithRecordName:@"NoteID"];
//创建保存数据
CKRecord *noteRecord = [[CKRecord alloc] initWithRecordType:RECORD_TYPE_NAME];
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData == nil)
{
imageData = UIImageJPEGRepresentation(image, 0.6);
}
NSString *tempPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/imagesTemp"];
NSFileManager *manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:tempPath]) {
[manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSDate *dateID = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval timeInterval = [dateID timeIntervalSince1970] * 1000; //*1000表示到毫秒级,这样可以保证不会同时生成两个同样的id
NSString *idString = [NSString stringWithFormat:@"%.0f", timeInterval];
NSString *filePath = [NSString stringWithFormat:@"%@/%@",tempPath,idString];
NSURL *url = [NSURL fileURLWithPath:filePath];
[imageData writeToURL:url atomically:YES];
CKAsset *asset = [[CKAsset alloc]initWithFileURL:url];
[noteRecord setValue:title forKey:@"title"];
[noteRecord setValue:content forKey:@"content"];
[noteRecord setValue:asset forKey:@"photo"];
[datebase saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(!error)
{
NSLog(@"保存成功");
}
else
{
NSLog(@"保存失败");
NSLog(@"%@",error.description);
}
}];
}

查询数据

查询所有数据

查询数据同样,我们也要先获取容器和数据库。

1
2
3
//获取位置
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;

之后我们添加一个查询的条件,cloud kit和core data一样,都是使用NSPredicate
而且还要用到CKQuery这个类。

1
2
3
//添加查询条件
NSPredicate *predicate = [NSPredicate predicateWithValue:YES];
CKQuery *query = [[CKQuery alloc] initWithRecordType:RECORD_TYPE_NAME predicate:predicate];

准备好了,接下来就可以调用查询方法了,获取到的results就是获取到数据的数组了,这里我用了通知的方法把查询到的数据传出去。

1
2
3
4
5
6
7
8
9
//开始查询
[database performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
NSLog(@"%@",results);
//把数据做成字典通知出去
NSDictionary *userinfoDic = [NSDictionary dictionaryWithObject:results forKey:@"key"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"CloudDataQueryFinished" object:nil userInfo:userinfoDic];
}];

整个代码合起来就是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//查询数据
+ (void)queryCloudKitData
{
//获取位置
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;
//添加查询条件
NSPredicate *predicate = [NSPredicate predicateWithValue:YES];
CKQuery *query = [[CKQuery alloc] initWithRecordType:RECORD_TYPE_NAME predicate:predicate];
//开始查询
[database performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
NSLog(@"%@",results);
//把数据做成字典通知出去
NSDictionary *userinfoDic = [NSDictionary dictionaryWithObject:results forKey:@"key"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"CloudDataQueryFinished" object:nil userInfo:userinfoDic];
}];
}

查询单个数据

同样也是获取容器和数据库。

1
2
3
//获取位置
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;

然后我们需要获取查询的数据的RecordID。

1
2
3
4
5
6
7
8
9
10
[database fetchRecordWithID:recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",record);
//把数据做成字典通知出去
NSDictionary *userinfoDic = [NSDictionary dictionaryWithObject:record forKey:@"key"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"CloudDataSingleQueryFinished" object:nil userInfo:userinfoDic];
});
}];

合起来代码就是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//查询单条数据
+ (void)querySingleRecordWithRecordID:(CKRecordID *)recordID
{
//获取容器
CKContainer *container = [CKContainer defaultContainer];
//获取公有数据库
CKDatabase *database = container.publicCloudDatabase;
[database fetchRecordWithID:recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@",record);
//把数据做成字典通知出去
NSDictionary *userinfoDic = [NSDictionary dictionaryWithObject:record forKey:@"key"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"CloudDataSingleQueryFinished" object:nil userInfo:userinfoDic];
});
}];
}

删除数据

删除数据同样,我们也要先获取容器和数据库。

1
2
3
//获取位置
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;

然后,在删除数据时,要获取到你要删除的数据的RecordID,之后直接执行删除方法即可

1
2
3
4
5
6
7
8
9
10
11
12
[database deleteRecordWithID:recordID completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
if(error)
{
NSLog(@"删除失败");
}
else
{
NSLog(@"删除成功");
}
}];

合起来代码就是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//删除数据
+ (void)removeCloudKitDataWithRecordID:(CKRecordID *)recordID
{
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;
[database deleteRecordWithID:recordID completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
if(error)
{
NSLog(@"删除失败");
}
else
{
NSLog(@"删除成功");
}
}];
}

修改数据

还是那句话,先获取容器

1
2
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *database = container.publicCloudDatabase;

其实修改数据就是重新把数据写入。在写入的时候如果选择之前没有的key,就相当于新增了一个字段。然后执行保存操作就可以了。

直接上全部的代码好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//修改数据
+ (void)changeCloudKitWithTitle:(NSString *)title content:(NSString *)content photoImage:(UIImage *)image RecordID:(CKRecordID *)recordID
{
//获取容器
CKContainer *container = [CKContainer defaultContainer];
//获取公有数据库
CKDatabase *database = container.publicCloudDatabase;
[database fetchRecordWithID:recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
NSData *imageData = UIImagePNGRepresentation(image);
if (imageData == nil)
{
imageData = UIImageJPEGRepresentation(image, 0.6);
}
NSString *tempPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/imagesTemp"];
NSFileManager *manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:tempPath]) {
[manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSDate *dateID = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval timeInterval = [dateID timeIntervalSince1970] * 1000; //*1000表示到毫秒级,这样可以保证不会同时生成两个同样的id
NSString *idString = [NSString stringWithFormat:@"%.0f", timeInterval];
NSString *filePath = [NSString stringWithFormat:@"%@/%@",tempPath,idString];
NSURL *url = [NSURL fileURLWithPath:filePath];
[imageData writeToURL:url atomically:YES];
CKAsset *asset = [[CKAsset alloc]initWithFileURL:url];
[record setObject:title forKey:@"title"];
[record setObject:content forKey:@"content"];
[record setValue:asset forKey:@"photo"];
[database saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if(error)
{
NSLog(@"修改失败 %@",error.description);
}
else
{
NSLog(@"修改成功");
}
}];
}];
}

Demo

先放一下demo的地址

demo简单的制作了一个简单的cloud kit存储功能,一些交互没有完善,但是基本的增删改查功能都已经实现了。

logo

以上就是Cloud Kit的简单使用。此文章仅供个人学习使用,如有不当,希望大佬指出。