早在2012年,Apple随iOS 5一起推出了iCloud。与此同时,该公司宣布开发人员将可以通过许多API访问iCloud。 首先,开发人员有三种选择:
这些API并不是完美的。 主要缺点是缺乏透明度。 尤其是核心数据集成,甚至在最有经验的开发人员中也引起了沮丧和困惑 。 当出现问题时,开发人员不知道是谁或谁是罪魁祸首。 这可能是他们的代码或Apple的问题。
CloudKit
在2014年WWDC上,Apple推出了CloudKit ,这是一个全新的框架,可以直接与Apple的iCloud服务器进行交互。 该框架可与许多PaaS (平台即服务)解决方案相提并论,例如Firebase。 与Firebase一样,Apple提供了灵活的API和仪表板,使开发人员可以窥探存储在Apple iCloud服务器上的数据。
我最喜欢CloudKit的是Apple对框架的承诺。 据该公司称, iCloud Drive和iCloud Photo Library建立在CloudKit之上。 这表明CloudKit框架及其基础架构是可靠且可靠的。
作为开发人员,这种信任和承诺的标志很重要。 过去,Apple偶尔会发布一些API,这些API受到bug或缺少关键功能的困扰,仅仅是因为该公司没有吃自己的狗粮。 对于CloudKit,情况并非如此。 这是有希望的。
您应该使用CloudKit吗?
键值存储和文档存储有其用途,Apple强调CloudKit不会替代或弃用现有的iCloud API。 核心数据也是如此。 例如,CloudKit不提供本地存储。 这意味着,如果运行在没有网络连接的设备上的应用程序仅依赖CloudKit,则几乎没有用。
苹果还强调,使用CloudKit时,错误处理至关重要。 例如,如果保存操作失败,并且没有通知用户,那么她甚至可能不知道自己的数据没有保存,并且丢失了。
CloudKit是用于在云中存储结构化和非结构化数据的出色解决方案。 如果您需要一种解决方案来访问多个设备上的数据,那么当然可以考虑使用CloudKit。
在2015年的WWDC上,苹果做了很少的开发人员所期望或期望的事情。 它宣布了CloudKit的Web服务 。 这意味着CloudKit几乎可以在任何平台上使用,包括Android和Windows Phone。
苹果的定价也很有竞争力 。 CloudKit入门是免费的,并且对于大多数应用程序仍然免费。 同样,如果您计划将数据存储在云中,那么CloudKit当然值得考虑。
CloudKit概念
挣扎于核心数据的开发人员通常不熟悉框架的构建块。 如果您不花时间学习和理解核心数据堆栈,那么您将不可避免地遇到问题。 CloudKit也是如此。
在开始研究使用CloudKit的示例应用程序之前,我想花几分钟向您介绍CloudKit框架和基础架构的许多关键概念。 让我们从容器,数据库和沙箱开始。
隐私与遏制
苹果非常清楚地表明隐私是CloudKit的重要方面。 首先要知道的是,每个应用程序在iCloud中都有自己的容器。 这个概念与iOS应用程序各自具有自己的沙箱的方式非常相似。 但是,可以将容器与其他应用程序共享,只要这些应用程序与同一开发者帐户相关联即可。 您可以想象,这为开发人员带来了许多有趣的可能性。
CloudKit容器包含多个数据库。 每个容器都有一个公共数据库,可用于存储应用程序的每个用户均可访问的数据。 除了公共数据库之外,容器还为应用程序的每个用户包含一个私有数据库。 用户的专用数据库用于存储特定于该特定用户的数据。 数据隔离和封装是CloudKit和iCloud基础架构的关键组件。
即使应用程序的容器可以容纳许多数据库,从开发人员的角度来看,容器也只能容纳两个数据库:当前已登录其iCloud帐户的用户的公共数据库和私有数据库 。 截至2017年,Apple引入了第三个数据库,即共享数据库 ,使应用程序能够共享在另一个用户的私有数据库上共享的记录子集,从而邀请它们为那些公开的记录做出贡献。
稍后我将详细介绍iCloud帐户。
记录和记录区
应用程序容器的数据库存储记录。 这与传统数据库没有太大区别。 乍一看,CloudKit数据库中存储的记录似乎不过是键-值对字典的包装器。 他们可能看起来像赞美的字典,但这只是故事的一部分。
每条记录还具有记录类型和许多元数据字段。 记录的元数据可跟踪创建记录的时间,创建记录的用户,记录的最后更新时间以及记录的更新人。
CKRecord
类代表这样的记录,它是一个非常强大的类。 您可以在记录中存储的值不限于属性列表类型。 您可以在记录中存储字符串,数字,日期和Blob数据,但是CKRecord
类还将位置数据CLLocation
视为第一类数据类型。
您甚至可以在记录中存储受支持的数据类型的数组。 换句话说,对于CKRecord
实例,字符串或数字的数组没有问题。
记录按记录区组织。 记录区将相关记录分组。 公共和私有数据库都有一个默认的记录区,但是可以根据需要创建自定义记录区。 记录区是一个高级主题,在本系列中我们将不作详细讨论。
人际关系
记录之间的关系由CKReference
类的实例管理。 让我们看一个示例,以更好地理解关系如何精确地工作。 我们将在本系列中创建的应用程序将管理许多购物清单。 每个列表中可以包含零个或多个项目。 这意味着每个项目都需要对其所属列表的引用。
重要的是要了解该项目保留了对列表的引用。 尽管可以为列表的项目创建CKReference
实例的数组,但将外键与项目而不是列表保持在一起更为方便(建议)。 这也是苹果的建议。
CloudKit管理关系的方式是相当基本的,但是它确实提供了一个选项,可以在删除父记录时自动删除记录的子项。 在本系列的稍后部分,我们将仔细研究关系。
资产
我还想提到CKAsset
类。 虽然可以在记录中存储数据块,但应将非结构化数据(例如图像,音频和视频)存储为CKAsset
实例。 CKAsset
实例始终与记录关联,并且它对应于磁盘上的文件。 在本系列中,我们将不使用CKAsset
类。
认证方式
我相信您同意CloudKit看起来很吸引人。 但是,有一个我们尚未讨论的重要细节:身份验证。 用户通过其iCloud帐户对自己进行身份验证。 未登录iCloud帐户的用户无法将数据写入iCloud。
尽管对于任何iCloud API都是如此,但请记住,在这种情况下,仅依赖CloudKit的应用程序将无法正常运行。 如果开发人员允许,用户只能做的事情就是访问公共数据库中的数据。
读取数据
未登录其iCloud帐户的用户仍可以从公共数据库读取数据。 不用说,私有数据库不可访问,因为iCloud不知道谁在使用该应用程序。
读写
登录后,用户可以读写公共数据库和私有数据库。 我已经提到苹果非常重视隐私。 结果,存储在私有数据库中的记录只能由用户访问。 甚至您(开发人员)也看不到用户存储在其私有数据库中的数据。 这是Apple管理应用程序后端的缺点,但这对用户来说是绝对的胜利。
购物清单
我们将要构建的应用程序将管理您的购物清单。 每个购物清单将有一个名称和零个或多个项目。 构建购物清单应用程序后,您应该在自己的项目中使用CloudKit框架感到很舒服。
先决条件
在本教程中,我将使用Xcode 9和Swift 4 。 如果您使用的是旧版本的Xcode,请记住您正在使用其他版本的Swift编程语言。 这意味着您将需要更新项目的源代码以满足编译器的要求。 所做的更改大部分都是次要的,但务必要意识到这一点。
如果您不熟悉iOS开发或Swift语言,请务必检查一下。
项目设置
现在该开始编写一些代码了。 启动Xcode并基于Single View Application模板创建一个新项目。
给您的项目起一个名字和一个组织标识符 。 生成的包标识符将用于创建应用程序默认容器的标识符。 由于标识符共享一个全局名称空间,因此它们在各个开发者帐户之间必须是唯一的。 因此,遵循Apple的建议并使用反向域名表示法非常重要。
启用iCloud
下一步是启用iCloud和CloudKit。 在左侧的项目浏览器中选择项目,然后从目标列表中选择应用程序的目标。 打开常规选项卡,然后将团队设置为正确的团队。 为避免在下一步发生任何问题,请验证您的开发者帐户是否具有创建App ID所需的权限。
接下来,打开顶部的“ 功能”选项卡,然后将iCloud的开关设置为打开。 Xcode将需要一些时间来代表您创建一个App ID 。 还将在App ID中添加必要的权利。 如果这不起作用,请确保团队设置正确,并且您具有创建应用程序ID所需的权限。
启用CloudKit就像选中标记为CloudKit的复选框一样简单。 默认情况下,您的应用程序将为您的应用程序使用默认容器。 启用CloudKit时会自动为您创建此容器。
如果您的应用程序需要访问其他容器或多个容器,请选中标有“ 指定自定义容器”的复选框,并检查您的应用程序需要访问的容器。
您可能已经注意到Xcode已自动将目标链接到CloudKit框架。 这意味着您已经准备好开始在应用程序中使用CloudKit。
弄湿你的脚
在本系列的下一个教程中,我们将添加添加,编辑和删除购物清单的功能。 但是,为了完成本教程,我想向您展示如何与CloudKit API交互,以使您的头脑更加湿润。 我们要做的就是获取当前登录用户的记录。
打开ViewController.swift并在顶部添加导入语句以导入CloudKit框架。
import UIKit
import CloudKit
要获取用户记录,我们首先需要获取记录的标识符。 让我们看看它是如何工作的。 我创建了一个辅助方法fetchUserRecordID
,以包含用于获取用户记录标识符的逻辑。 我们在视图控制器的viewDidLoad
方法中调用此方法。
override func viewDidLoad() {
super.viewDidLoad()
// Fetch User Record ID
fetchUserRecordID()
}
fetchUserRecordID
的实现比viewDidLoad
有趣。 我们首先通过调用CKContainer
类上的defaultContainer
来获取对应用程序默认容器的引用。 然后,在defaultContainer
上调用fetchUserRecordIDWithCompletionHandler(_:)
。 此方法接受闭包作为其唯一参数。
private func fetchUserRecordID() {
// Fetch Default Container
let defaultContainer = CKContainer.default()
// Fetch User Record
defaultContainer.fetchUserRecordID { (recordID, error) -> Void in
if let responseError = error {
print(responseError)
} else if let userRecordID = recordID {
DispatchQueue.main.sync {
self.fetchUserRecord(recordID: userRecordID)
}
}
}
}
该闭包接受两个参数:一个可选的CKRecordID
实例和一个可选的NSError
实例。 如果error
为nil
,我们将安全地拆开recordID
。
需要强调的是,闭包将在后台线程上调用。 这意味着从CloudKit调用的闭包内部更新应用程序的用户界面时,需要格外小心。 例如,在fetchUserRecordID
,我在主线程上显式调用fetchUserRecord(_:)
。
在fetchUserRecord(_:)
,我们通过告诉CloudKit我们感兴趣的记录来获取用户记录。请注意,我们在privateDatabase
对象( defaultContainer
对象的属性)上调用了fetchRecordWithID(_:completionHandler:)
。
该方法接受CKRecordID
实例和完成处理程序。 后者接受可选的CKRecord
实例和NSError
实例。 如果我们成功获取了用户记录,则将其打印到Xcode的控制台。
private func fetchUserRecord(recordID: CKRecordID) {
// Fetch Default Container
let defaultContainer = CKContainer.default()
// Fetch Private Database
let privateDatabase = defaultContainer.privateCloudDatabase
// Fetch User Record
privateDatabase.fetch(withRecordID: recordID) { (record, error) -> Void in
if let responseError = error {
print(responseError)
} else if let userRecord = record {
print(userRecord)
}
}
}
在Xcode中运行该应用程序时,您将在控制台中看到类似于以下内容的内容:
这应该使您对CloudKit框架有所了解。 其现代的API直观且易于使用。 在下一个教程中,我们将更深入地研究CloudKit API的可能性。
您可以在GitHub上克隆完整的示例项目(标签#introduction
)。
结论
您现在应该对CloudKit框架的基本知识有适当的了解。 本系列的其余部分将重点介绍构建购物清单应用程序。 在下一个教程中,我们将首先添加添加,编辑和删除购物清单的功能。