转载

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则是配置的步骤是否都成功;

iOS项目iCloud及CloudKit Dashboard运用

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

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

iOS项目iCloud及CloudKit Dashboard运用

因为实例中的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然后对它进行编辑

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

iOS项目iCloud及CloudKit Dashboard运用

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

三:设置iCloud的数据

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

iOS项目iCloud及CloudKit Dashboard运用

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

iOS项目iCloud及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运用

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

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

iOS项目iCloud及CloudKit Dashboard运用

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

NSData (single bytes)  
NSDate (date and time)  
NSNumber (both Int and Double)  
NSString (or String in Swift)  
NSArray (list)  
CKReference (used to create relationships between objects)  
CLLocation (location)  
CKAsset (file)

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

iOS项目iCloud及CloudKit Dashboard运用

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

iOS项目iCloud及CloudKit Dashboard运用

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

四:代码操作iCloud功能

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

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* _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

正文到此结束
Loading...