icloud_icloud照片 - CSDN
精华内容
参与话题
  • GW_iCloud_Bypass

    2020-07-25 23:31:08
    GW_iCloud_Bypass解锁ID
  • iOS iCloud入门

    千次阅读 2018-06-27 14:38:11
    iCloud是Apple公司提供的云端服务,可以非常方便地让用户免费使用云端的5GB空间存储资料,如果用户愿意,还可以花钱升级到更大的云端存储空间,这些云端存储空间可用于备份用户的个人数据(如通讯录、备忘录、邮件、...

    github地址:iCloud入门

    前言

    iCloud是Apple公司提供的云端服务,可以非常方便地让用户免费使用云端的5GB空间存储资料,如果用户愿意,还可以花钱升级到更大的云端存储空间,这些云端存储空间可用于备份用户的个人数据(如通讯录、备忘录、邮件、照片、音乐、视频等),也可以用于备份用户的应用数据。即使用户更换手机,也可以非常方便地将备份在云端的数据重新恢复到新的手机上。
    我之前是不会用iCloud的,现在觉得是真的方便啊。

    正文

    1.iCloud入门

    1.1为应用开启iCloud服务

    我们需要完成如下4件事情:
    1.创建iCloud的容器,要求名字和id是唯一的。 iCloud容器名必须是唯一的,因为这是Cloudkit用来访问数据所使用的全局标识符。
    为了让entitlements起作用,需要在App的证书、标识符与配置文件中ID的部分列出app/bundle id。这意味着标识的证书使用了设置的team id与app id,从中可得到iCloud容器的id。若已经在一个可用的开发者账号中标识了的话Xcode会自动完成这一切。不巧的是,这有时是不同步的,需要更新ID-使用iCloud功能面板修改CloudKit容器ID。否则的话需要修改info.plist文件或.entitlements文件来确保id values与所设置的bundle id一致。
    还有个血与泪的教训啊!创建了之后就删不掉了···希望大家谨慎操作···

    2.创建支持iCloud的Apple ID,并关联上相应的iCloud容器。

    完成后Edit

    关联容器

    3.创建、下载并安装支持iCloud的App对应的provisioning Profile,这个工作需要通过Apple 网站完成。

    4.在Xcode中配置相关设置,成功后启用应用的iCloud服务。


    这样我们就为项目增加了一个entitlements文件。该文件就是该项目的iCloud配置文件。



    经过上面步骤,我们就完成了为应用增加iCloud支持。
    我们双击打开该文件,即可看到如下图。


    1.2设置iCloud的数据

    要进入CloudKit Dashboard操作有两种方式,第一种如上面第一点所说可以直接点CloudKit Dashboard进入,

    另一种就是进入苹果账号后台也有一个相应的菜单;

    点进进入之后:

    再点击容器进入之后:

    选择data进入之后:

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

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

    我跟你们讲哦,写到这里我实在写不下去了,因为WWDC17大会的时候这个界面就修改了,跟网上的博客不一样了,我需要回去恶补一下这个大会内容,再回来写了···

    一天后···

    我回来继续写了,我昨天晚上看了WWDC17大会的内容,嗯,正在安装iOS11 beta9...

    我现在才知道我有多笨,看过的盆友们是否已经在取笑我了呢,你们取笑吧,我就笨怎么着吧,因为笨笨的很可爱!哼!

    通过学习代码操作,我基本了解了这个界面的意思。

    下图是我用代码,在容器里创建了一个RecordType为User的Records,其中属性为name和password,我在public data中存储了两条数据。


    下面我在public中找到User的Record Type然后查询recordName = 1的数据信息。


    如我所愿。

    我尝试在公司账号上修改我的数据,包括增加一个index,都提示我失败,我觉得这个是因为我的iCloud账号是我的Apple ID吧,此处存疑,希望有盆友可以帮我指正。

    1.3代码操作iCloud功能

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

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

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

    //判断手机中的iCloud功能是否开启
    id cloudUrl = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    if (cloudUrl == nil) {
    NSLog(@"iCloud没有开启");
    } else {
    NSLog(@"iCloud开启");
    }

    这个方法接受一个参数, 就是要获取的容器标识。 所谓容器标识, 大多数应用只会用到一个 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:增加一条记录,可以给它一个固定的recordName,就像数据表的主键一样,查找、更新跟删除有用;

    //增加一条记录,可以给它一个固定的recordName,就想数据表的主键一样,查找、更新跟删除有用 可以根据实际情况修改
    -(void)addCloudDataWithPublic:(BOOL)isPublic recordName:(NSString *)recordName 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:recordName];
        //创建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) {
                NSLog(@"保存成功");
            } else {
                NSLog(@"保存失败:%@",error);
            }
        }];
    }
    
    4.假如要保存带有图片的字段,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:

    //增加带图片的提交 图片的保存,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:
    -(void)saveImageDataWithPublic:(BOOL)isPublic recordName:(NSString *)recordName name:(NSString *)name password:(NSString *)password imageName:(NSString *)imageName
    {
        //保存图片 图片的保存,需要用到CKAsset,他的初始化需要一个URL,所以这里,我先把图片数据保存到本地沙盒,生成一个URL,然后再去创建CKAsset:
        UIImage *image=[UIImage imageNamed:imageName];
        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,imageName];
        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:recordName];
        //创建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) {
                NSLog(@"保存成功");
            } else {
                NSLog(@"保存失败:%@",error);
            }
        }];
    }
    
    5.查找单条记录的功能,通过recordName进行

    //查找单条记录
    -(void)searchRecordWithRecordName:(NSString *)recordName withFormPublic:(BOOL)isPublic
    {
        //获得指定的ID
        CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordName];
        
        //获得容器
        CKContainer *container=[CKContainer defaultContainer];
        
        //获得数据的类型 是公有还是私有
        CKDatabase *database;
        //如果是公有就存在公有数据库 私有则存在私有数据库
        if (isPublic) {
            database=container.publicCloudDatabase;
        }
        else
        {
            database=container.privateCloudDatabase;
        }
        //Block需要用self时弱引用self
        //__weak typeof(self)weakSelf = self;
        //查找操作
        [database fetchRecordWithID:noteId completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
            if (!error) {
                NSString *message=[NSString stringWithFormat:@"获得RecordName为%@的数据:%@,%@",recordName,[record objectForKey:@"name"],[record objectForKey:@"password"]];
                NSLog(@"%@",message);
            } else {
                NSLog(@"查找失败:%@",error);
            }
        }];
    }
    
    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) {
            if (!error) {
                NSLog(@"%@",results);
            } else {
                NSLog(@"查找失败:%@",error);
            }
        }];
    }
    


    7.更新一条记录首先要查找出该条记录再对它进行修改如果对应的键值存在进修改其值如果键值不存在则增加新的类型字段
    //更新一条记录 首先要查找出这一条  然后再对它进行修改
    -(void)updateRecordWithFormPublic:(BOOL)isPublic withRecordTypeName:(NSString *)recordTypeName withRecordName:(NSString *)recordName
    {
        //获得指定的ID
        CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordName];
        
        //获得容器
        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:@"changepwd" forKey:@"password"];
                //如果健值不存在 则会增加一个
                [record setObject:@"男" forKey:@"gender"];
                
                [database saveRecord:record completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
                    if (!error) {
                        NSLog(@"修改成功");
                    }
                    else
                    {
                        NSLog(@"修改失败 :%@",error);
                    }
                }];
            }
        }];
    }
    


    8.删除一条记录

    //删除记录
    -(void)deleteRecordWithFormPublic:(BOOL)isPublic withRecordName:(NSString *)recordName
    {
        //获得指定的ID
        CKRecordID *noteId=[[CKRecordID alloc]initWithRecordName:recordName];
        
        //获得容器
        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) {
                NSLog(@"删除成功");
                return;
            }
            NSLog(@"删除失败 %@",error);
        }];
        
    }
    
    调用的方法就不在这里写了。。。

    大家心里还没有点数吗?!

    我添加数据可以在云容器中看到,其实就是一个数据库而已啦。。增删查改这些啦。。。


    我有什么错误欢迎大家指正,感谢!

    参考博客:博客


    展开全文
  • iCloud-3.0

    2020-07-27 23:30:36
    用来改地区,iCloud3.0 大小: 70555976 字节 文件版本: 3.0.2.163 MD5: A8DB662B950B478670D036979F15EED3 SHA1: 012AEB393CC318D4308CEEFED24B22D3BBE4C1AD
  • 事实上,它利用了苹果iCloud的一个安全漏洞,进而可以绕过苹果iCloud的密码尝试次数限制,所以最好是采用双因素身份验证比较好。iDict的完成方法并不杂乱,只是不断测验500个常用暗码猜想iCloud账户暗码。这也意味着...
  • 这篇文章打算倒叙,先讲讲我是怎么优雅地使用 iCloud 服务的吧。 iCloud 是什么? 很简单咯,就像 Google 的 Google Drive,微软的 OneDrive,甚至说百度的百度网盘(这个跟前面提到的还是有差别的…),就是一个...

    很奇怪的标题

    这篇文章打算倒叙,先讲讲我是怎么优雅地使用 iCloud 服务的吧。

    iCloud 是什么?

    很简单咯,就像 Google 的 Google Drive,微软的 OneDrive,甚至说百度的百度网盘(这个跟前面提到的还是有差别的…前面三者更像是真实存在的“云端硬盘”,如果你购买了空间,举个例子,转存了别人分享的资源,这个资源就会复制一份存到你的云盘里,这样做的好处就是,订阅者能更放心的把自己的文件存入,缺点就是订阅的费用一般比较昂贵,仅适合存取一些文档或者图片,不适合视频类的存取;而百度网盘更像是“打标签”,同样一个例子,当你转存了百度网盘的资源,百度会在这个资源上打上你的标签,并不会复制一份存到你的网盘,也可以理解为你的网盘里只存了一个“快捷方式”,或者说“索引”,所以表面上百度网盘有几个TB大小的空间,真正属于你自己的空间并不多,而且嘿嘿嘿,安全性嘛,就不得而知了),就是一个基于网盘的服务
    这些服务以网盘在线存储为核心,衍生出很多有用的功能,例如 Google Photos (它允许用户存入无限张经过适量压缩后的照片)等。
    由于天朝没法体验完整的谷歌服务,而微软爸爸一直又是以一名长者的姿态对待消费者(大概就是很多逻辑很奇怪,然后很多服务也不好用,并且没有手机 OS 作为载体),Apple 以其强大的硬件闭环,构建起了自己的生态圈,这个生态圈就是以 iCloud 为核心。
    举个简单的例子:AirPods 连接 iPhone 后,再和 iPad 或者 Mac 连接的时候,并不需要重新配对,只需要在媒体源那里选择你的耳机就好了,真实体验上有几秒的延迟,不过都还可以接受。

    iCloud 要怎么使用呢?

    分功能介绍

    1.应用 / 游戏数据

    这个很难说,具体的实现方式自己摸索过,大概就是,应用 / 游戏数据按照备份方式分为这几类:

    1. 无任何备份方式
      这个指,该应用 / 游戏仅仅只能在一部手机单机运行,一旦更换设备或者重新下载,数据都会消失。
    2. 通过该应用 / 游戏自己的或者第三方云服务进行备份
      比如 QQ 这样的社交软件,肯定是有消息好友的云端记录的,不过这个应该不属于今天讨论的范畴。
      再举一个例子,游戏账号就是,游戏数据备份在该游戏所在的服务器上,当你更换设备或者重新下载的时候,登陆同一个账号,数据还是会在。
      在这里插入图片描述
    3. 通过 iCloud 手动备份
      这个其实就类似于那个游戏存档吧,定时手动存进 iCloud 里面,再恢复就好了。
    4. iCloud 自动备份
      这个才是 iCloud 的精髓所在。
      怎么说呢,比如音游《跳舞的线(Dancing Line)》,iPhone 和 iPad 端是完全同步的,比如说我在 iPhone 上通关了某一关或者说解锁了某一个皮肤,在 iPad 端也能很快看到,并且这个过程并不需要人为的手动参与。
      还有说很多效率软件,比如 Lightennotability 这种,都是 iCloud 自动备份的。
      我印象很深的一个场景,就是我在 iPad 上面记的笔记,考试前去厕所的时候,用手机可以直接方便的查看复习。

    2.系统数据备份

    当你购买了一部新的 iPhone 或者你把自己的 iPhone 恢复了出厂设置,在初始化的时候, 会问你是设置为新的 iPhone 还是从备份恢复,这个备份就可以是 iCloud 备份(当然也会有人更喜欢用 PC iTunes 备份)。这个备份会把你的手机完完全全地备份下来,包括应用游戏数据、照片等在内的全部数据。就等于你的手机会恢复到和之前完全相同的状态。
    iCloud 云备份
    有一点需要注意的是,如果你在 iCloud 里打开了照片备份,那么在 iCloud 云备份里就不会重复备份你的照片了,你的照片数据就像你的备忘录、日历和钥匙串这写数据一样已经被实时存在云端了。

    3.iCloud Drive

    这个就是一个纯粹的云盘了,可以直接往里面扔进去文件,一些 App 还会调用 iCloud 的接口,直接在云盘里创建文件夹,可以从外部直接访问(比如 Readdle 家的 Document)。对于笔者来说就是存一些上课用的 PDF 文件和 PPTX 文件。
    在这里插入图片描述

    注意

    一个需要注意的就是,要区分 iCloud 和 iCloud Drive,iCloud 是服务,备份在 iCloud 里的应用数据是不可以直接访问的,而调用 iCloud Drive 的应用在云盘里创建的文件夹是可以直接访问的(甚至说在 Windows 环境下也可以通过在线的 iCloud 或者 iCloud 控制面板桌面程序进行访问)

    iCloud 价格?

    为什么我购买了 iCloud 额外的存储空间?

    我用了哪些 iCloud 服务?

    1. iCloud 服务开启了全部的选项,包括空间占用最多的照片
      在这里插入图片描述
    2. 上面有提到,Document 配合 iCloud Drive 来存一些上课用的文档
      在这里插入图片描述

    正文(虽然叫正文但是文章篇幅并不长)

    用列表的形式快速闪回一下

    • 很久之前我的 32g 的手机拿去扩容
    • 九月份我的手机在使用过程中突然无法触控
      • 屏幕逐渐变偏色(不是变暗)
      • 直至完全开不开机
    • 连接 iTunes 没用,无法强制刷机
    • 初步确定是硬件问题
    • 去附近的手机维修店,被告知是硬盘出了问题
    • 修好后手机数据全无
    • 购买 iCloud 额外空间,开启 iCloud 全部服务

    后果

    自己之前的备份习惯是每过一段时间(大概两三个月)手动把照片导进 PC,而上次手机备份恰好是六月份放假的时候,所以我的整个假期的照片数据都没有了。
    那时候突然明白了一些事情,硬件设备是靠不住的,尤其是 Windows 和 Android。
    果断买了 iCloud 额外的空间,把连 iPad 在内的六千多张照片导进了 iCloud 里面,并开启了 iPad 和 iPhone 的云备份。

    数据无价

    展开全文
  • iCloud

    2019-10-05 02:43:53
    iCloud 一旦你的app通过UIDocument进行操作,那么iCloud服务会让你更满意。你只需要两步接入iCloud服务: 注册使用iCloud的资格 在苹果开发者网站,注册你的App ID ,勾选启用iCloud,并生成对应的provisioning ...

    iCloud


    一旦你的app通过UIDocument进行操作,那么iCloud服务会让你更满意。你只需要两步接入iCloud服务:

    注册使用iCloud的资格

    在苹果开发者网站,注册你的App ID ,勾选启用iCloud,并生成对应的provisioning profile文件,下载下来双击:

    300944270035258.png

    然后在项目工程中,勾选使用iCloud服务:

    300948453157509.png

    获取一个兼容iCloud的目录

    在你app启动时,在后台调用 NSFileManager 的 URLForUbiquityContainerIdentifier:(传入nil)方法来获取一个云分享目录的URL。这个目录会在file://localhost/private/var/mobile/Library/Mobile Documents/ 路径下。尽管严格意义上,这个路径并不在你app沙箱的范围内,但是你仍然可以像访问沙箱一样范围这个路径。你app放置在这个路径下的子目录都会自动分享到云端。

    现在我们可以把我们上一篇文章中的People Groups app 变成兼容iCloud服务的。我们在app delegate 中,当我的app启动时,我启动一个后台线程,获取一个云分享目录的URL,然后在主线程中,以属性形式保存起来这个URL:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSFileManager* fm = [NSFileManager new];
        NSURL* ubiq = [fm URLForUbiquityContainerIdentifier:nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.ubiq = ubiq;
        });
    });

    在iOS 6 中,你可以直接在主线程中通过调用 NSFileManager 的 ubiquityIdentityToken ,因为这个方法是立刻返回的。如果返回值为nil,那么iCloud服务不可用,或者这个手机用户没有注册iCloud。如果返回不为nil,那么这个可以标识一个iCloud账户,你可以通过这个标识来判断用户是否登录了不同的账户。

    然后,在任意地方,我可以指定这个URL当作用户的Documents 目录来查找和保存数据:

    NSURL* docsurl = [fm URLForDirectory:NSDocumentDirectory
                                inDomain:NSUserDomainMask
                       appropriateForURL:nil create:NO error:nil];
    NSURL* ubiq =
        [(AppDelegate*)[[UIApplication sharedApplication] delegate] ubiq];
    if (ubiq)
        docsurl = ubiq;

    现在你可以运行你的app,创建一条数据,然后在你的另一个设备上,同样运行这个app,你会发现这个新创建的数据同步到了这台设备上。非常有趣。

    我们还需要去留意是否有新的文档出现在云端了,为了达到这个目的,我会运行一个 NSMetadataQuery 。通常的策略是:实例化一个NSMetadataQuery,配置它的搜索,注册通知,如NSMetadataQueryDidFinishGatheringNotificationNSMetadataQueryDidUpdateNotification,然后开启搜索,我们还要持有这个NSMetadataQuery实例的强引用,以便让它在整个app的生命周期内都在运行。

    另一个需要注意的地方是,我们还要留意本地磁盘上正在打开的文件是否有新的版本从云端下载了。为了达到这个目的,我们注册一个UIDocumentStateChangedNotification 通知。我们可以通过它的documentState 属性来获取当前文档的状态。一个比较麻烦的问题是,当这个documentStateUIDocumentStateInConflict时如何解决冲突。 你可以使用 NSFileVersion 类 ,若要了解更多关于这个类的使用,可以查看 苹果开发者文档:Document-based App Programming Guide里面 的 “Resolving Document Version Conflicts” 章节。

    还有一个需要注意的地方是,一开始,用户关闭了iCloud服务或者对你的app没有启用iCloud服务,但是后来又启用了,这时,你的数据存放在两个不同的地方(云端和Documents 目录)。 你可以调用 NSFileManager 的 setUbiquitous:itemAtURL:destinationURL:error: 方法,来把某个目录转换到云端的无处不在的容器目录。

    其实,相对于存放正式的文件在云端,我们更多时候或者更好的做法是只存放一些键值对数据到云端,类似于一个在线版的NSUserDefaults。 为了达到这个目的,你可以使用 NSUbiquitousKeyValueStore 类,获取一个defaultStore 共享的实例对象,使用的方式跟NSUserDefaults 类似。 这个 NSUbiquitousKeyValueStoreDidChangeExternallyNotification 可以在你的云端数据改变时通知你。通过这个NSUbiquitousKeyValueStore保存的到云端的数据不会记入用户的iCloud使用容量,不过还是应该尽量保持数据简短。

    开源项目推荐

    iCloudDocumentSync

    转载于:https://www.cnblogs.com/YungMing/p/4346752.html

    展开全文
  • iCloud开发入门

    千次阅读 2016-09-10 14:31:10
    CloudKit是苹果公司推出的基于iCloud的远程数据存储服务。它为存储和使用用户的iCloud账户的后端存储服务共享应用数据提供了一种低成本的选择方案。 总体来看,CloudKit主要提供了两个组件: 一个网络中心,用于...

    简介

    CloudKit是苹果公司推出的基于iCloud的远程数据存储服务。它为存储和使用用户的iCloud账户的后端存储服务共享应用数据提供了一种低成本的选择方案。

    总体来看,CloudKit主要提供了两个组件:

    • 一个网络中心,用于管理记录类型和任何公共数据。
    • 一组API,用于在iCloud和设备之间传输数据。

    CloudKitr的安全性是很高的。用户的私有数据受到完全保护,因为开发人员只能访问他们自己的私人数据库而不能查看任何其他用户的私人数据。

    对于只运行于iOS平台的使用大量数据但不需要服务器端大量逻辑的应用程序来说,CloudKit是一个不错的选择。此外,CloudKit也可用于网络和服务器应用程序。

    在这个CloudKit教程中,您将获得使用CloudKit的切身体验,即你需要创建一个名为BabiFüd的餐厅评级应用程序。

    为何选择CloudKit?

    你可能想知道,为什么要选择基于核心数据(Core Data)基础之上的CloudKit,而不是其他BaaS(后端即服务)产品,甚至是基于你自己的服务器?

    原因有三:简单性,高信誉度和低成本。

    简单性

    不像其他的后端解决方案,CloudKit仅需要很少的设置。你不必选择、配置或安装服务器。此外,安全性和伸缩性也都由苹果公司来处理。

    只需在苹果官方网站注册成为iOS开发者项目成员,您就拥有了使用CloudKit的资格。你不必注册额外的服务或创建新帐户。当您在自己的应用程序中启用CloudKit支持时,所有必要的服务器设置都将魔术般自动发生。

    你不需要下载额外的库并对它们进行配置。CloudKit就像任何其他iOS框架一样导入。CloudKit框架本身通过提供一些针对常见操作的便利的API实现了一定程度的简单性。

    这也方便了用户使用。由于在设备设置(甚至是设置结束进入应用程序时)时CloudKit使用的是输入的iCloud凭据,所以没有必要建立复杂的登录屏幕。只要用户登录,他们就可以无缝地开始使用你的应用程序。

    高信誉度

    CloudKit的另一个好处是,通过依靠苹果公司而不是应用程序开发人员,用户可以相信他们的数据的隐私性和安全性。CloudKit能够把您(开发人员)与用户数据隔离开来。

    虽然在调试程序时无法访问可能令人沮丧,但另一个方面这也带来一定好处,因为你不必担心安全或说服用户其数据的安全性。如果一个应用程序用户信任iCloud,那么他们也可以信任作为开发人员的你。

    低成本

    最后,对于任何开发者来说,运行服务的成本也是一个巨大的投资。即使是最便宜的服务器主机也不能为小型、免费或廉价的应用程序提供低成本的解决方案。所以总是会有与运行应用程序相关的成本问题。

    借助于CloudKit,你可以实现针对免费的公共数据的适量的存储和数据传输。在WWDC 2015视频的CloudKit新增功能(https://developer.apple.com/videos/play/wwdc2015/704/)中提供了非常详尽的收费解释。

    上述所有这些优势,使得CloudKit服务成为Mac和iOS应用程序开发中更值得选择的解决方案。

    BabiFüd项目功能介绍

    本教程中提供的BabiFüd示例应用程序使用时下最标准的“速度餐厅”型应用程序风格。不是沿用传统式的基于食品质量、服务速度以及价格等评价标准,而是使用新型的儿童友好性进行评价。这包括设施更换、加高座椅和健康性食品选择等可用性标准。

    应用程序包含四个选项卡:一个附近的餐馆列表;一个附近餐馆地图展示;用户生成注释和功能设置。附近的餐馆列表选项卡是您将在本教程中使用的唯一部分。当然,你现在就可以一瞥运行中的这个示例应用程序。

    开发过程中,我使用了一个模型类来支持这些视图,并封装对CloudKit的调用。其中,CloudKit对象称为记录(Record)。模型中主要的记录类型是一个Establishment,它代表了你的应用程序中不同的餐馆。

    开始

    首先,请下载本教程中的启动项目,地址是https://cdn2.raywenderlich.com/wp-content/uploads/2016/06/BabiFud-Cloudkit-Starter.zip。

    你必须改变你的应用程序的资源标识符和团队类型,然后才能开始编码。为了从苹果公司获得必要的授权,您需要设置团队。拥有一个独特的资源标识符可以使得很多的事更容易操作。

    现在,请使用Xcode打开工程BabiFud.xcodeproj。然后,从「Project Navigator」下选择BabiFud项目,然后选择「BabiFud target」。接下来,选择「General」选项卡,使用一些独特的字符串内容更换资源标识符(Bundle Identifier)。标准的做法是,使用反向域名符号并包括项目名称。然后,选择合适的团队(Team):

    现在,你需要在你的应用程序中设置CloudKit支持,并创建一些容器来保存数据。

    权限和容器

    在向应用程序中添加任何数据之前,你需要一个容器来保存应用程序的记录。容器,其实仅是一个概念上的位置术语,对应于所有应用程序在服务器上的数据。它分为公共和私有数据库两个组。

    要创建一个容器,你首先需要拥有启用您的应用程序的iCloud权限。现在,请从目标编辑器选择【Capabilities】选项卡。然后,把iCloud部分中的开关切换为ON。

    现在,Xcode可能会提示您输入与您的iOS开发者帐户相关联的苹果ID。如果是这样,那么根据要求输入即可。最后,通过勾选【Services】组中的CloudKit复选框来启用CloudKit。

    这将创建一个名为iCloud.<your app’s bundle id>的默认容器,如下图所示:

    克服Xcode中iCloud安装错误

    如果你在创建权限、构建项目或运行应用程序时看到任何警告或错误,并注意到Xcode抱怨容器ID;那么,下面提供一些故障排除提示,供您参考:

    • 如果有任何的警告或错误显示在【iCloud】部分中的【Steps】组中;那么,你可以尝试按下「Fix Issue」按钮。这可能需要反复操作好几次。

    • 应用程序的包ID和iCloud的容器要相匹配并存在于开发人员帐户中,这一点是很重要的。例如,如果包标识为“com.<your domain>.BabiFud”,那么,iCloud的容器名称应是“iCloud.”,再加上资源包ID“iCloud.com.<your domain>.BabiFud”。
    • iCloud的容器名称必须是唯一的,因为这是使用CloudKit访问数据的全球标识符。由于iCloud的容器名称包含了资源ID,所以该资源ID也必须是唯一的(这就是为什么它必须由com.raywendrelich.BabiFud进行修改的原因)。
    • 为了过权限这一关,应用程序/包ID显示于【Certificates】,【Identifiers】和【Profiles】的App ID部分。这意味着,用于签名应用程序的证书必须来自于设置的团队ID并且必须列出应用程序ID;这也就是iCloud的容器id。通常情况下,如果你登录了一个有效的开发帐户,Xcode能够自动地完成这一切。不幸的是,这有时会导致不同步情况。你可以通过一个新的ID刷新来重新开始,并使用iCloud的功能窗格更改CloudKit容器ID以便进行匹配。否则,要解决这个问题,你可能要编辑info.plist文件或BabiFud.entitlements文件,以确保ID值反映你为应用程序设置的包ID。

    CloudKit控制面板简介

    在创建完工程所必需的餐馆数据之后,下一步就是创建一些记录类型来定义您的应用程序所使用的数据。您可以使用CloudKit控制面板来实现这一点。从工程的【Capabilities】窗格中点击【CloudKit Dashboard】,如图所示。

    【注意】你还可以通过在浏览器中打开网址https://icloud.developer.apple.com/dashboard/ 来启动CloudKit控制面板。

    这个控制面板是什么样子呢?请观察一下这个图形:

    注意到,该控制面板由四部分组成:架构(Schema),公共数据(Public Data),私有数据(Private Data)和管理员(Admin)。

    其中,Schema部分代表CloudKit容器中的最高层次对象:记录类型(Record Types),安全角色(Security Roles)及订阅类型(Subscription Types)。在本教程中,你只要关心记录类型即可。

    记录类型(Record Types),是一组定义了单个记录的字段。对于面向对象编程而言,一个记录类型就像一个类。一个记录可以被认为是一个特殊的记录类型的实例。它代表了容器中的结构化数据,就像数据库中的一个典型的行,它封装了一系列键/值对。

    公共数据(PUBLIC DATA)和私有数据(PRIVATE DATA)部分,可以让您在您能够访问的数据库中添加数据或搜索数据。请记住,作为一名开发人员你可以访问所有的公共数据,但只能访问你自己的私有数据。其中,「User Records」存储关于当前iCloud用户的数据,如姓名和电子邮件等。「记录区域」(Record Zone)(这里使用的是默认的区域),用于提供一个逻辑组织到一个私有数据库;实现方法是对记录进行分组。注意,自定义区域支持原子事务,即允许在处理其它操作之前把多个记录同时保存。不过,有关自定义区域的探讨已经超出本教程的范围。

    管理员(ADMIN)部分,能够针对您的团队成员提供不同的权限配置控制。如果你有多个开发团队成员,你可以在这里限制他们编辑数据的能力。当然,这也超出本教程范围。

    添加餐馆记录类型

    现在,请略微想一想你的应用程序的设计吧。你要跟踪的每一家餐馆都对应大量的数据:名称、位置以及各种儿童友好的设施的可用性,等等。记录类型使用字段来定义每个记录包含的各个部分中的数据。

    选择记录类型(Record Types)后,单击详细信息窗格左上方的+图标添加一个新的记录类型,如图所示。

    命名你刚刚创建的新记录类型为「Establishment」。

    你会看到出现一行字段,其中定义了字段名、字段类型和索引等,如下图所示。注意,有一个字段使用了默认名称「StringField」,这是系统自动为您创建的。

    接下来,你要使用新名字「Name」替换「StringField」。字段类型和索引默认已匹配您所需要的第一个字段的定义,但接下来,你需要对于其他一些字段改变字段类型和索引。单击【Add Field…】并根据需要增加新的字段即可。最后,你需要添加以下字段:

    当你添加完所有字段后,你的字段列表应该是这样的:

    点击页面底部【Save】按钮保存您的新的记录类型。

    现在,您已经准备好向您的数据库中添加一些示例记录了。

    选择左侧导航窗格中【PUBLIC DATA】下部的【Default Zone】。该区域将包含您的应用程序的所有公共记录。如果还没有选定,那么请从中心窗格的下拉列表中选择「Establishment」记录类型。然后单击右侧详细信息窗格中的+图标或【New Record】按钮,如下图所示:

    这将创建一个新的空的Establishment记录。

    此时,您已经准备好为您的应用程序输入一些测试数据了。

    需要说明的是,下面的示例数据都是虚构的。这些餐馆数据都位于苹果总部附近,这样它们可以很容易地出现在模拟器上。

    现在,请输入如下表所述的每个记录:

    【注意】每个CoverPhoto元素对应的图像文件都包含在Xcode项目的「Supporting Files\Sample Images」文件夹中。要将图像添加到Establishment记录中,只需将其拖动到CoverPhoto字段中即可。

    一旦保存完所有三个记录,控制面板应该是这样的:

    对于每个记录,输入的值都代表了数据的数据库描述部分。在应用程序中,数据类型是不同的。例如,SeatingType和ChangingTable都是结构类型的。所以,对于SeatingType字段指定的int值可能对应于“high chair”或“booster”这样的座位。对于HealthyOption和KidsMenu这两个字段指定的int值表示布尔类型:其中,0是指没有这一项,1则指有这一项。

    最后,运行应用程序需要你有一个可以用于开发的iCloud帐户。请参考苹果官方文档https://developer.apple.com/library/tvos/documentation/DataManagement/Conceptual/CloudKitQuickStart/EnablingiCloudandConfiguringCloudKit/EnablingiCloudandConfiguringCloudKit.html#//apple_ref/doc/uid/TP40014987-CH2-SW7。

    另外,您还需要在iPhone模拟器中输入与此帐户相关联的icloud凭据。请参考苹果官方文档https://developer.apple.com/library/tvos/documentation/DataManagement/Conceptual/CloudKitQuickStart/CreatingaSchemabySavingRecords/CreatingaSchemabySavingRecords.html#//apple_ref/doc/uid/TP40014987-CH3-SW12。

    现在,切换回Xcode中。从下一节开始,你要把上面创建的数据集成到您的应用程序中!

    查询餐馆记录

    CKQuery对象用于从数据库中选择记录。CKQuery描述了如何找到符合特定条件的特定类型的所有记录。这些条件可以是这样的:所有记录都有一个以N开头的Name字段;所有记录都带有儿童增高座椅;所有记录都要满足在3公里以内。这些类型的表达式都通过NSPredicate对象编码于Cocoa库中。NSPredicate能够评估对象,看它们是否符合指定条件。NSPredicate也用在核心数据(Core Data)中,并且自然地融入CloudKit中,因为谓词通常被定义用于针对某个字段的比较方面。

    事实上,CloudKit仅支持NSPredicate功能的一个子集。这些功能包括:数学比较,字符串和集合操作(例如“字段匹配列表中的项目之一”),还提供了一个特殊的距离函数。函数distanceToLocation:fromLocation:被专门添加到NSPredicate对象定义中,特别支持CloudKit以匹配带有位置字段的记录——此已知位置位于指定半径的范围内。接下来的内容中将详细介绍这种类型谓词的用法。对于其他类型的查询中,CKQuery类参考文档(https://developer.apple.com/library/ios/documentation/CloudKit/Reference/CKQuery_class/)中包含了有关其支持的功能及如何使用的详细描述。

    【注意】CloudKit包括了对CLLocation对象的支持。有一些核心定位框架对象(Core Location Framework)包含了有关地理坐标的信息。这使得开发人员可以很容易创建一个查询来寻找指定地理区域内的餐馆——而不需要我们自己进行所有繁琐的坐标运算。

    接下来,在Xcode中打开文件Model/ Model.swift。该文件中包含了所有您的应用程序进行服务器调用的存根。

    现在,请使用下面的内容来更换fetchEstablishments(_:radiusInMeters:)方法:

    1. func fetchEstablishments(location:CLLocation, radiusInMeters:CLLocationDistance) { 
    2.   // 1 
    3.   let radiusInKilometers = radiusInMeters / 1000.0    
    4.   // 2 
    5.   let locationPredicate = NSPredicate(format: "distanceToLocation:fromLocation:(%K,%@) &lt; %f", "Location", location, radiusInKilometers)    
    6.   // 3 
    7.   let query = CKQuery(recordType: EstablishmentType, predicate: locationPredicate)    
    8.   // 4 
    9.   publicDB.performQuery(query, inZoneWithID: nil) { [unowned self] results, error in 
    10.     if let errorerror = error { 
    11.       dispatch_async(dispatch_get_main_queue()) { 
    12.         self.delegate?.errorUpdating(error) 
    13.         print("Cloud Query Error - Fetch Establishments: \(error)") 
    14.       } 
    15.       return 
    16.     } 
    17.   
    18.     self.items.removeAll(keepCapacity: true) 
    19.     results?.forEach({ (record: CKRecord) in 
    20.       self.items.append(Establishment(record: record, 
    21.         database: self.publicDB)) 
    22.     }) 
    23.   
    24.     dispatch_async(dispatch_get_main_queue()) { 
    25.       self.delegate?.modelUpdated() 
    26.     } 
    27.   } 

    现在,我们按编号来分析一下上面代码的功能:

    1.    CloudKit在其距离谓词中利用公里为计算单位。这一行简单地把radiusInMeters转换为公里。

    2.    根据从当前位置到他们的位置的距离谓词对餐馆进行筛选。这个语句使用从用户的当前位置到指定距离内的位置值来查找所有餐馆。

    3.    使用谓词和记录类型创建CKQuery对象。执行查询时两者都将被使用。

    4.    最后,方法performQuery(_:inZoneWithID:completionHandler :)负责发送查询到iCloud中,并等待任何匹配的结果。通过传递nil作为inZoneWithID参数值,可以针对你所在的默认区域进行查询;也就是说,针对你的公共数据库。如果你既想从公共数据库也想从私有数据库中检索记录,那么,你必须使用一个单独的调用来查询每个数据库。

    你可能想知道:CKDatabase的实例publicDB从何而来?让我们来看看文件Model.swift的顶部的代码吧。

    1. let container: CKContainer 
    2. let publicDB: CKDatabase 
    3. let privateDB: CKDatabase 
    4.   
    5. init() { 
    6.   // 1 
    7.   container = CKContainer.defaultContainer() 
    8.   // 2 
    9.   publicDB = container.publicCloudDatabase 
    10.   // 3 
    11.   privateDB = container.privateCloudDatabase 

    在这里,您定义了您的数据库:

    1.    默认容器是指您在iCloud的功能窗格中指定的那个。

    2.    公共数据库是指在你的应用程序的所有用户中共享的那个。

    3.    私有数据库只包含属于当前登录的用户(在本实例中即指你)的数据。

    总之,此代码将从公共数据库中检索一些地方餐馆,但为了在任何应用程序中看到相应的数据,必须把它关联到一个视图控制器。

    创建需求回调函数

    你可以借助熟悉的委托模式来管理通知的问题。下面这个协议位于Model.swift文件的顶部;当然,你需要在你的视图控制器中实现这个协议:

    1. protocol ModelDelegate { 
    2.   func errorUpdating(error: NSError) 
    3.   func modelUpdated() 
    4. 现在,打开文件MasterViewController.swift,并用以下内容替换modelUpdated()方法原有内容: 
    5. func modelUpdated() { 
    6.   refreshControl?.endRefreshing() 
    7.   tableView.reloadData() 

    当新数据可用时此方法会被调用。在方法tableView(_:cellForRowAtIndexPath:)中实现了所有关于表格视图单元格与CloudKit对象绑定的代码。您可以自行分析学习,恕在此不再赘述。

    接下来,在文件MasterViewController.swift中,请使用如下内容更换errorUpdating(_ :)方法:

    1. func errorUpdating(error: NSError) { 
    2.     let alertController = UIAlertController(title: nil, 
    3.                                             message: error.localizedDescription, 
    4.                                             preferredStyle: .Alert) 
    5.     alertController.addAction(UIAlertAction(title: "Dismiss", 
    6.                                             style: .Default, 
    7.                                             handler: nil)) 
    8.     presentViewController(alertController, animated: true, completion: nil) 

    当查询产生错误时调用此方法。可能由于网络条件较差或特定CloudKit问题(如丢失或不正确的用户凭据或没有记录从查询返回等)发生错误。

    当处理任何一种远程服务器连接时,良好的错误处理是必不可少的。现在,这段代码只显示给用户返回的错误信息。

    但是,目前程序中存在的非常普遍的问题是:一个是用户不能登录到iCloud;另一个是程序还不具有支持用户自动登录icloud的功能。建议您自己修改一下errorUpdating(_:)方法使之至少能够处理这些情况。提示:目前这两类错误都返回1(CKErrorCode)。

    现在,请构建和运行示例项目。目前,你应该看到一个几近完整的餐馆单位名单列表了。

    查询排错

    如果你正在使用iOS模拟器但却发现列表控件中没有填充任何内容,那么,你务必确保从Xcode的菜单项【Debug\Simulate Location\San Francisco, CA, USA】下设置了正确的位置。如果您需要在Xcode中改变这一位置,那么你可以从应用程序的下拉列表中选择其他位置,从而实现强制刷新而不是被动地等待位置触发。

    如果您使用的是iPhone或iPad且启用了定位服务而列表仍未填充,那么说明餐馆没有足够接近你当前的位置。此时,你有两个选择:改变样本数据的坐标以便更接近您的当前位置;或者使用模拟器来运行应用程序。还有第三个选择,但不是非常实用,即你不得不前往库比提诺(Cupertino)并进入苹果公司地区进行试验。

    如果数据未显示正确——或者根本不显示,那么,请使用CloudKit控制面板检查样本数据。确保所有的记录都存在,而且你已经将它们添加到默认区域,而且它们的值都是正确的。如果您需要重新输入数据,那么你可以通过单击回收站图标删除记录(参考下图)。

    调试CloudKit错误有时可能非常棘手。在撰写本文时,CloudKit错误消息中并不包含大量信息。要确定错误的原因,您需要查看与你的特定数据库操作相关连的错误代码。使用数字错误代码,查找相匹配的CKErrorCode枚举值进行分析。在文档中的名称和说明将帮助你缩小问题原因的分析范围。

    【注意】对于可通过CloudKit返回的错误代码列表,请参阅CloudKit框架常量参考(https://developer.apple.com/library/ios/documentation/CloudKit/Reference/CloudKit_constants/#//apple_ref/c/tdef/CKErrorCode)。

    下面是一些常见的错误枚举和相关说明:

    • BadContainer——指定的容器是未知的或未经授权的。
    • NotAuthenticated——当前用户没有通过验证,也没有用户记录可用。如果用户未登录到iCloud时可能出现这种情况。
    • UnknownItem——指定的记录不存在。

    当你得到餐馆的名单列表时,你可能已经注意到了,你可以看到服务餐馆名称及其提供的服务。但没有显示图像!是云端那边出问题了吗?

    当您检索服务餐馆记录时,将自动检索对应的图像。但是,您仍然需要执行必要的步骤来将图像加载到你的应用程序中。这需要借助于云端方法了!

    使用二进制资源

    对于二进制形式的资源数据,如图像,都有相关联的记录。在本文实例中,你的应用程序的资源数据都会显示于MasterViewController表视图的餐馆照片中。

    在本节中,您需要添加逻辑来加载当检索餐馆记录时已下载的资源。

    为此,打开文件Model/Establishment.swift,并用下面的代码更换loadCoverPhoto(_ :)方法:

    1. func loadCoverPhoto(completion:(photo: UIImage!) -&gt; ()) { 
    2.   // 1 
    3.   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { 
    4.     var image: UIImage! 
    5.     // 4 
    6.     defer { 
    7.       completion(photo: image) 
    8.     } 
    9.     // 2 
    10.     guard let asset = self.record["CoverPhoto"] as? CKAsset, 
    11.       path = asset.fileURL.path, 
    12.       imageData = NSData(contentsOfFile: path) else { 
    13.         return 
    14.     } 
    15.     // 3 
    16.     image = UIImage(data: imageData) 
    17.   } 

    此方法从asset有关属性中加载相关图片:

    1.    虽然你下载了资源,但同时您还检索了记录的其余部分数据,因此,你需要使用异步方式加载图像。如你所见,我们把有关代码全部封装在dispatch_async代码块内。

    2.    资源存储在CKRecord中,作为CKAsset的实例,所以需要相应地转换一下。接下来,从资源提供的本地文件URL中加载图像数据。

    3.    使用图像数据来创建UIImage的一个实例。

    4.    执行与检索的图像相关的completion回调函数。请注意,不管执行哪一个return语句,延迟(defer)块都被执行。例如,如果没有图像资源,那么,在返回时永远不会设置image变量的值,当然也就不显示餐厅对应的图像。

    现在,请构建和运行示例项目。你会注意到,由于上面的云端操作,现在餐馆的图像显示出来了!

    目前,在CloudKit资源中还存在两个不足:

    • 资源只能在CloudKit中作为记录属性存在,你不能把它们单独存储。删除记录也会删除所有相关资源。
    • 因为资源与记录数据的其余部分在同一时间下载,所以,获取资源会产生一定的负面性能影响。如果您的应用程序使用了大量的资源,那么,你应该专门存储一下拥有资源的不同类型记录的引用。

    总结

    到现在为止,你应该已经看到了我们的最终项目中所提供的功能。目前,该应用程序已经可以下载餐馆记录,并把它们的详细资料和照片加载到程序的表视图中。

    实际上,您还可以从以下几个方面进一步增强本文中的示例应用:

    允许用户添加自己的照片、笔记、评论和投诉。这将有助于他们避免再次遭遇不愉快的经历。

    允许用户使用地图创建一个新的餐馆记录。你可以把这样的功能添加到Model类中,用于把相应记录保存到公共或私有数据库中。

    添加过滤和搜索支持。工程提供的Model类中已经构建了一个带有一个距离谓词的CKQuery,可以把它修改为一个更复杂的谓词。当然,CloudKit也是支持基于字符串字段的文本搜索的。

    提高应用程序的性能和数据加载体验。你会注意到,本教程中在一切准备就绪时使用了一些工具方法来调用所需的完成处理器函数。另外,CKDatabase的实例也提供了基于NSOperation的方法来更好地控制API执行方式。

    为程序提供缓存和同步支持;这样当应用程序连接到网络时,它能够保持线下响应并保持内容最新。

    使用苹果公司推出的有着强大后端API支持的CloudKit,相信你能够把你的应用程序提升到一个更高的水平!

    展开全文
  • Notability 无法连接 iCloud,怎么办?

    千次阅读 2020-05-20 15:24:41
    使用iCloud自动同步iPad、iPhone和Mac上的Notability记事,亦可找回记事的历史版本。Notability 无法连接 iCloud,怎么办?感兴趣的朋友,下面就和小编一起来看看吧! Notability 无法连接 iCloud,怎么办? 1. 重启...
  • 第二次机会算法

    2020-07-02 16:48:21
    补充:
  • mac iCloud 关闭后 桌面文件不见了

    万次阅读 2018-06-16 20:53:12
    输入/user后回车进入用户文件打开iCloud Drive 
  • iOS 一直弹出登录iCloud的解决方法

    万次阅读 2017-06-29 22:01:57
    一直弹出的时候,你随便输入密码,输三次,它会出现让你重设密码,然后点击重设,会出现网页,然后直接按home键,就可以 ,前提是必须要连上网。
  • macOS下的iCloud的文件夹

    万次阅读 2017-02-18 17:29:39
    macOS的iCloud Drive出现在个人收藏中,如何像普通文件夹那样操作iCloud的文件夹怎么办呢?比如从命令行复制移动文件到iCloud? 原来在macOS中iCloud文件夹的路径如下:~/Library/Mobile\ Documents/com~apple~...
  • https://jingyan.baidu.com/article/295430f1edbca90c7f00506d.html
  • 因为我的桌面文件会直接传到iCloud上, 并且在我退出iCloud时候, 系统让我选择将文件归档在目录下. 找到了桌面文件的解决方法: Finder——>找到左侧的小房子图标(点击)——>找到iCloud Drive (Archive)文件夹...
  • 如果出现这个情况、试着换一下dns、比如114.114.114.114 大概是因为dns解析错误吧、有些网站会出现网络连接错误也是因为这个原因
  • 百度没能解决“连接到icloud是出错”,突然发现是因为禁止了“设置”访问WIFI和蜂窝网络(第三张图所示)。 ​
  • 在macOS、iOS上,使用iCloud能方便地同步各类文件。而在Mac上,我们通过iCloud Drive还能把iCloud当网盘使用。使用方法非常简单:
  • NSString *tempA = @"123"; NSString *tempB = @"456";   1,字符串拼接 ... NSString *newString = [NSString stringWithFormat:@"...int intString = [newString intValu
  • iCloud中的照片如何导出到个人电脑中进行储存? 很多小伙伴都有使用iCloud储存照片的习惯,不过往往iCloud的储存空间并不是那么够用。如果想要将iCloud中的照片导出到个人电脑上进行保存,该如何操作呢...
  • iCloud的pc客户端,从iCloud下载照片到pc
1 2 3 4 5 ... 20
收藏数 13,698
精华内容 5,479
关键字:

icloud