iOS项目iCloud及CloudKit Dashboard运用

CloudKit是苹果推出的基于iCloud的一个云端数据存储服务.其 主要由下面两部分组成:

一个仪表web页面,用于管理公开数据的记录类型.

一组API接口,用于iCloud和设备之间的数据传递.

 

一:首先在XCode上面打开关于iCloud功能

1:进入对应的项目Targets 中的Capabilities 选项卡,打开关于iCloud功能;如果勾选iCloud Documents或CloudKit会自动生成一个带iCloud.开头的Containers,要配置证书支持;CloudKit Dashboard则可以直接跳转到Web配置关于iCloud的内容;而关于Steps则是配置的步骤是否都成功;

 

 

二:关于证书如何配置支持iCloud功能

1:进入苹果证书管理后台中的Identifiers里有个iCloud Containers菜单

iOS项目iCloud及CloudKit Dashboard运用_第1张图片

 

因为实例中的Bundle Identifier的名字wjy.com.MobileProject;所以在这边创建一个iCloud.wjy.com.MobileProject的ID值;都是前面增加一个iCloud为开头;app的bundle id需要与iCloud容器相对应, iCloud容器名必须是唯一的,因为这是Cloudkit用来访问数据所使用的全局标识符。由于iCloud容器名包含bundle id,因此bundle id也必须是唯一的(这就是为何需要修改com.raywendrelich.BabiFud)。 

为了让entitlements起作用,需要在App的证书、标识符与配置文件中ID的部分列出app/bundle id。这意味着标识的证书使用了设置的team id与app id,从中可得到iCloud容器的id。若已经在一个可用的开发者账号中标识了的话Xcode会自动完成这一切。不巧的是,这有时是不同步的,需要更新ID-使用iCloud功能面板修改CloudKit容器ID。否则的话需要修改info.plist文件或BabiFud.entitlements文件来确保id values与所设置的bundle id一致。

2:创建完上面的ID后,同样进行Identifiers里的App IDs,找到我们当前的App ID然后对它进行编辑

iOS项目iCloud及CloudKit Dashboard运用_第2张图片

打开iCloud的功能选择,并且选择Include CloudKit support,这边这时会有一个警告出现,选择右边的Edit进行编辑

iOS项目iCloud及CloudKit Dashboard运用_第3张图片

这时会有刚才创建好的那个iCloud ID可以选择绑定;选择好以后外面的警告也会消除;然后生成对应的描述文件安装后,XCode上面的步骤报错也会消除;

 

三:设置iCloud的数据

1:要进入CloudKit Dashboard操作有两种方式,第一种如上面第一点所说可以直接点CloudKit Dashboard进入,另一种就是进入苹果账号后台也有一个相应的菜单;

iOS项目iCloud及CloudKit Dashboard运用_第4张图片

2:进入CloudKit Dashboard可以看到如下的页面

 

2.1 SCHEMA :

CloudKit容器的高级类:Record Types, Security Roles, 和Subscription Types,其中主要使用的是Record Types;

一个Record Type用来定义一个单独的记录(可以理解为一个数据模型),相当于存储数据的模板,和数据库的表结构类似;

2.2 PUBLIC DATA 和 PRIVATE DATA

就是你保存数据的地方,开发者可以查看所有的共享数据,但是只能看到自己的私密数据,无法看到用户的私密数据;这里没有显示PRIBATE DATA,其结构和PUBLIC DATA是一样的;

User Records 记录一些当前使用者的信息;

Default Zone :这里可以查看数据的详细信息,也是后面主要使用的;

2.3 ADMIN 主要是管理开发者团队权限的,这里不做过多介绍;

3:切回Record Type选项,点击右边栏的左上角的 "+ ",新增一个模型:

iOS项目iCloud及CloudKit Dashboard运用_第5张图片

输入模型名称: 默认只有一个StringField的属性(这里暂且这么称呼吧),可以点击下面的Add Field... 新增属性列表;

同样可以选择属性的类型,如下图:

iOS项目iCloud及CloudKit Dashboard运用_第6张图片

设置完成后,点击右下角的 Save按钮即可保存!这样,一个模型就建立好了;存储类型的数据:

1. NSData (single bytes)  
2. NSDate (date and time)  
3. NSNumber (both Int and Double)  
4. NSString (or String in Swift)  
5. NSArray (list)  
6. CKReference (used to create relationships between objects)  
7. CLLocation (location)  
8. CKAsset (file)  

5:回到Default Zone,中间蓝色区域的右上角的名称旁有个倒三角,这里可以选择使用(查询)哪个模型下的数据:

iOS项目iCloud及CloudKit Dashboard运用_第7张图片

 

6:如果要存是图片字段记得选Asset类型

 

注意:1 Public Record ,1 Private Record,意思是:使用模型Note的有一条公共数据,一条隐私数据,由于我登陆的iCloud账号和开发者账号不一样,相当于是用户账号,所以这里是看不到那个隐私数据的

 

四:代码操作iCloud功能

1:首先引入CloudKit.framework系统框架,并引入命名空间#import <CloudKit/CloudKit.h> 就可以进行操作

2:首先是判断手机中的iCloud功能是否开启,如下面如果有值则表示已经开启的iCloud功能;

id cloudUrl=[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

这个方法接受一个参数, 就是要获取的容器标识。 所谓容器标识, 大多数应用只会用到一个 iCloud 容器,所以我们这里传入 nil, 就代表默认获取第一个可用的容器。这个方法内部会查找当前应用拥有的 iCloud 容器, 如果找到就会返回这个容器的 URL, 证明当前应用的 iCloud 容器可用。 如果找不到,就会返回 nil, 证明当前应用的 iCloud 不可用。

注意: iCloud 容器和你 App 文件沙盒, 在 iOS 文件系统中其实是分别存放在两个不同的地方的:

iCloud 文件路径格式 file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~xxx~aaa/Documents

App 沙盒文件路径格式 file:///var/mobile/Containers/Data/Application/3B4376B3-89B5-3342-8057-3450D4224518/Documents/

由此可见, 这也是为什么 iCloud 和 Sandbox 文件路径访问需要两套不同的方式的原因了。

3:增加一条记录,可以给它一个固定的recordID,就像数据表的主键一样,查找、更新跟删除有用;

-(void)addCloudDataWithPublic:(BOOL)isPublic recordID:(NSString *)recordID name:(NSString *)name password:(NSString *)password
{
    //CloudKit给应用程序分配部分空间,用于存储数据,首先要获取这个存储空间,这里我们直接获取了默认的存储器(可以自定义存储器):
    CKContainer *container=[CKContainer defaultContainer];
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase; //公共数据
    }
    else
    {
        database=container.privateCloudDatabase;//隐藏数据
    }
    
    //创建主键ID  这个ID可以到时查找有用到
    CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID];
    //创建CKRecord 保存数据
    CKRecord *noteRecord = [[CKRecord alloc]initWithRecordType:@"User" recordID:noteId];
    
    //设置数据
    [noteRecord setObject:name forKey:@"name"];
    [noteRecord setObject:password forKey:@"password"];
    
    //保存操作
    [database saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
        if (!error) {
            [self showAlertMessage:@"保存成功"];
        }
    }];
}

4:假如要保存带有图片的字段,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:

//增加带图片的提交 图片的保存,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:
-(void)saveImageDataWithPublic:(BOOL)isPublic recordID:(NSString *)recordID name:(NSString *)name password:(NSString *)password
{
    //保存图片 图片的保存,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:
    UIImage *image=[UIImage imageNamed:@"icloudImage"];
    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];
    }
    
    NSString *filePath = [NSString stringWithFormat:@"%@/%@",tempPath,@"iCloudImage"];
    NSURL *url = [NSURL fileURLWithPath:filePath];
    [imageData writeToURL:url atomically:YES];
    
    CKAsset *asset = [[CKAsset alloc]initWithFileURL:url];
    
    //与iCloud进行交互
    CKContainer *container=[CKContainer defaultContainer];
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase; //公共数据
    }
    else
    {
        database=container.privateCloudDatabase;//隐藏数据
    }
    
    //创建主键ID  这个ID可以到时查找有用到
    CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID];
    //创建CKRecord 保存数据
    CKRecord *noteRecord = [[CKRecord alloc]initWithRecordType:@"User" recordID:noteId];
    
    //设置数据
    [noteRecord setObject:name forKey:@"name"];
    [noteRecord setObject:password forKey:@"password"];
    [noteRecord setObject:asset forKey:@"userImage"];
    
    //保存操作
    [database saveRecord:noteRecord completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
        if (!error) {
            [self showAlertMessage:@"保存成功"];
        }
    }];
}

5:查找单条记录的功能,通过recordID进行

//查找单条记录
-(void)searchRecordWithRecordID:(NSString *)recordID withFormPublic:(BOOL)isPublic
{
    //获得指定的ID
    CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID];
    
    //获得容器
    CKContainer *container=[CKContainer defaultContainer];
    
    //获得数据的类型 是公有还是私有
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase;
    }
    else
    {
        database=container.privateCloudDatabase;
    }
    
    __weak typeof(self)weakSelf = self;
    //查找操作
    [database fetchRecordWithID:noteId completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
        NSString *message=[NSString stringWithFormat:@"获得RecordID为%@ 的数据:%@,%@",recordID,[record objectForKey:@"name"],[record objectForKey:@"password"]];
        [weakSelf showAlertMessage:message];
    }];
}

6:查询多条记录的功能,用到的谓词进行

//查找多条记录(可以用谓词进行)
-(void)searchRecordWithFormPublic:(BOOL)isPublic withRecordTypeName:(NSString *)recordTypeName
{
    CKContainer *container=[CKContainer defaultContainer];
    //获得数据的类型 是公有还是私有
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase;
    }
    else
    {
        database=container.privateCloudDatabase;
    }
    
    NSPredicate *predicate=[NSPredicate predicateWithValue:YES];
    CKQuery *query=[[CKQuery alloc]initWithRecordType:recordTypeName predicate:predicate];
    
    __weak typeof(self)weakSelf = self;
    [database performQuery:query inZoneWithID:nil completionHandler:^(NSArray<CKRecord *> * _Nullable results, NSError * _Nullable error) {
        [weakSelf showAlertMessage:[NSString stringWithFormat:@"%@",results]];
    }];
}

7:更新一条记录首先要查找出该条记录,再对它进行修改,如果对应的键值存在进修改其值,如果键值不存在则增加新的类型字段;

//更新一条记录 首先要查找出这一条  然后再对它进行修改
-(void)updateRecordWithFormPublic:(BOOL)isPublic withRecordTypeName:(NSString *)recordTypeName withRecordID:(NSString *)recordID
{
    //获得指定的ID
    CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID];
    
    //获得容器
    CKContainer *container=[CKContainer defaultContainer];
    
    //获得数据的类型 是公有还是私有
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase;
    }
    else
    {
        database=container.privateCloudDatabase;
    }
    
    __weak typeof(self)weakSelf = self;
    //查找操作
    [database fetchRecordWithID:noteId completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
        if (!error) {
            
            //对原有的健值进行修改
            [record setObject:@"aa123456789" forKey:@"password"];
            //如果健值不存在 则会增加一个
            [record setObject:@"" forKey:@"gender"];
            
            [database saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
                if (!error) {
                    [weakSelf showAlertMessage:@"修改保存成功"];
                }
                else
                {
                    [weakSelf showAlertMessage:[NSString stringWithFormat:@"出错误 :%@",error]];
                }
            }];
            
        }
    }];
}

8:删除一条记录

//删除记录
-(void)deleteRecordWithFormPublic:(BOOL)isPublic withRecordID:(NSString *)recordID
{
    //获得指定的ID
    CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordID];

    //获得容器
    CKContainer *container=[CKContainer defaultContainer];
    
    //获得数据的类型 是公有还是私有
    CKDatabase *database;
    if (isPublic) {
        database=container.publicCloudDatabase;
    }
    else
    {
        database=container.privateCloudDatabase;
    }
    
    __weak typeof(self)weakSelf = self;
    [database deleteRecordWithID:noteId completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
        if (!error) {
            [weakSelf showAlertMessage:@"删除成功"];
            return;
        }
        [weakSelf showAlertMessage:[NSString stringWithFormat:@"删除失败 %@",error]];
    }];
    
}

 五:操作实例

编写一个关于在同一台手机上免登录的实例,主要原理是通过获取手机设备唯一码,然后通过iCloud保存到云端,在删除APP后再安装后就可以直接从云端获得用户跟密码;会不断完善此实例,如果感兴趣可以点星并关注,会接着编写关于iCloud关于文件的同步功能;注意证书可以换成自个的,步骤如上:

源代码地址:https://github.com/wujunyang/iCloudProject

你可能感兴趣的:(iOS项目iCloud及CloudKit Dashboard运用)