苹果在WWDC 2015中介绍了 CloudKit JS,它允许开发者创建一个网页界面来连接已经使用了 CloudKit 的应用的相同的数据容器。 CloudKit 原来的一个显著的限制,就是数据只允许 iOS 和 OS X 应用来使用。希望取消了这个限制后,能够带来更多的开发者,在创建他们的应用时使用 CloudKit。
在这篇文章中,我们将通过创建一个简单的记事本应用,允许用户把重要的事情记录在云端数据库中,来演示 CloudKit JS 的特性。
CloudKit JS
CloudKit JS 支持以下浏览器
- Safari
- FirFox
- Chrome
- IE
- Edge
有趣的是,它还支持 node, 意味着你可以通过自己中间层来获取请求,并展示结果给自己的API接口。
创建一个 CloudKit JS 应用
为了示范 CloudKit JS 的能力,我创建了一个简单的应用,允许你存储笔记到 CloudKit。
让我们来演示一下这个应用是如何创建的。 当我们要创建一个 CloudKit 应用时,无论它是 iOS 的还是 JS 的,第一步是打开 iClound的 developer dashboard。在这里设置应用的细节,创建 record type, 建立安全规则,填写数据等等。你可以在这个页面了解更多关于 dashboard 的内容https://icloud.developer.apple.com/dashboard。
创建一个叫做 CloudNotes的应用,把设置都默认。
创建了应用后,我们需要具体指明它的 record type. 这个应用仅存储简单的笔记,有一个标题和内容。选择左栏 Schema 下的 Record Types, 你可以看到,User 的记录类型已经存在了,这是默认生成的。
点击添加按钮“+” ,创建一个新的 record type,命名为 CloudNote. 这个 record 用来存储我们的数据。
现在需要给 record 添加字段了,添加 title 和 content 这两个字段,都设置为 String 类型。这是目前我们仅需要设置的数据结构。
接下来,我们添加一条记录,以便在网页上可以展示和检索到东西。在左栏的菜单中,选择 Public Data 下的 “Default Zone”。所有我们要在这个应用中使用的数据都是公共的。在真正的项目中,你可能希望把用户的数据存在单独的私有数据中,但为了简洁,这个示例中我们没有添加安全和授权方面的规则。
点击添加按钮,会出现添加的页面,你填写完毕后点击保存按钮,数据就会保存在CloudKit 中了。
现在我们的CloudKit中已经有了一些数据,让我们写段JS代码展示一下他吧。
JS 应用的架构
我们这个应用仅有一个页面(index.html),包含了外链的 JavaScript 文件。用来请求和存储 CloudKit 数据。为了帮助展示数据,我们引入了Knockout JS。它可以用来简化数据的绑定和展示,确保当数据源变化是,UI可以自动被刷新。同时我们还引入了 bootstrap ,省去我们自己去写 css 样式。
下面是所有的外链引用。
<html>
<head>
<title>iOS9 Day by Day - CloudKit Web Services Example</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script type="text/javascript" src="cloudNotes.js"></script>
<script src="https://cdn.apple-cloudkit.com/ck/1/cloudkit.js" async></script>
</head>
让我们来看一下 CloudNotes.js,看他是如何从CloudKit获取数据的。
在请求数据之前,我们首先要等待CloudKit API的加载。我们添加下面的代码到window 的eventListener中。它可以监听到 cloudkitloaded事件。
window.addEventListener('cloudkitloaded', function() {
当CloudKit对象加载成功后,你需要设置他的identifier,environment 和 API token。
CloudKit.configure({
containers: [{
containerIdentifier: 'iCloud.com.shinobicontrols.CloudNotes',
apiToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
environment: 'development'
}]
});
现在我们会到 CloudKit的 Dashboard 来生成一个 API token。在左栏选择Admin下的 API Tokens,然后点击添加,给API token取一个名字,然后把对应的值复制到上面的代码中。
在我的代码中,我引用了 Knockout JS 来绑定 model 到 HTML 上。 我创建了一个 CloudNotesViewModel,负责管理页面。它包括一个数组,里面包含了所有的笔记数据。同时包括了保存新笔记,查询笔记,以及在有无权限的情况下的两种展示效果。
在视图模型能够调用这些方法之前,首先我们需要创建CloudKit的权限验证。
container.setUpAuth().then(function(userInfo) {
// Either a sign-in or a sign-out button will be added to the DOM here.
if(userInfo) {
self.gotoAuthenticatedState(userInfo);
} else {
self.gotoUnauthenticatedState();
}
self.fetchRecords(); // Records are public so we can fetch them regardless.
});
当验证解决后,它依据用户是否登录的状态,在Dom中添加一个登录或退出的按钮。你需要在页面中创建一个id为 "apple-sign-in-button"的div。这个container.setUpAuth 方法可以在登录以后,自动修改 div。
查询记录
下面是在 CloudKit 中查询 “ColudNote” 的代码。
self.fetchRecords = function() {
var query = { recordType: 'CloudNote' };
// Execute the query.
return publicDB.performQuery(query).then(function (response) {
if(response.hasErrors) {
console.error(response.errors[0]);
return;
}
var records = response.records;
var numberOfRecords = records.length;
if (numberOfRecords === 0) {
console.error('No matching items');
return;
}
self.notes(records);
});
};
你可以看到我们根据recordtype建立了一个简单的查询,然后在公用数据库中进行了遍历查询。当然,你也可以在私有数据库中进行查询,只是在本例子中,我们只使用了公用数据库。
当我们查询到笔记的结果后,我们把它存储在slef.notes中,它受 knockout 检测。意味着会根据数据来重新渲染页面。这样查询到的笔记结果,就会显示在页面当中。
<div data-bind="foreach: notes">
<div class="panel panel-default">
<div class="panel-body">
<h4><span data-bind="text: fields.title.value"></span></h4>
<p><span data-bind="text: fields.content.value"></span></p>
</div>
</div>
</div>
模版会遍历所有的 notes 对象,打印 title 和 content 到 panel 上。
当用户登录后,在 panel 上看到 “Add New Note”。 所以 saveNewNote 方法需要实现把数据存储到 CloudKit 的功能。
if (self.newNoteTitle().length > 0 && self.newNoteContent().length > 0) {
self.saveButtonEnabled(false);
var record = {
recordType: "CloudNote",
fields: {
title: {
value: self.newNoteTitle()
},
content: {
value: self.newNoteContent()
}
}
};
在方法的前半段,我们首先做基本的数据验证工作。并且依据表单的内容新建一个记录。
创建了新的记录后,我们把它存入 CloudKit。
publicDB.saveRecord(record).then(
function(response) {
if (response.hasErrors) {
console.error(response.errors[0]);
self.saveButtonEnabled(true);
return;
}
var createdRecord = response.records[0];
self.notes.push(createdRecord);
self.newNoteTitle("");
self.newNoteContent("");
self.saveButtonEnabled(true);
}
);
publicDB.saveRecord(record) 会把新创建的记录保存到公共数据库中,同时返回一个响应结果。然后记录会放入之前我们创建好的数组,无须再去查询一遍,同时表单会被清空,保存按钮也再次变为可点击状态。
iOS 应用
我们的示例是为了演示数据通过 CloudKit 如何在 iOS 和 web 应用之间共享,所以,还需要一个 iOS 客户端的应用。
创建这个应用,我们选择 Xcode 的默认模版 master detail application.
为了能够让 iCloud 工作,我们需要在设置中打开 capablities. 开启 iCloud 服务, Xcode 会自动的链接开发者中心,并为你准备好需要的权限项。
设置如下图。
现在已经准备好了,可以在应用中使用 CloudKit了。这里我们不详细介绍如何组织界面了,编写代码了,感兴趣的可以去看 comprehensive explanation of how to use CloudKit on iOS in iOS8-day-by-day。下面是应用的样子,标题限制在 viewController 的 title 上,内容显示在屏幕中央。
总结
希望本文能够让你对 CloudKit JS 的 API 有所了解,看它是如何的简单易用。我非常高兴苹果公司推出这样的基于 web 的 api。但我仍持部分保留意见,我想,我个人不会在项目中使用它的。
我很多第三方的云服务,比苹果的原生SDK,拥有更好的文档,更多的特性。而且,我始终无法在模拟器中使用 CloudKit,如果这个 bug 不及时解决,会对开发者带来很大困扰。
使用 CloudKit 的需求,是确定存在的,我推荐开发者都去尝试一下。想想,如果你的数据需要在另一个平台共享的场景。
延伸阅读
想要了解更多关于 CloudKit JS 和 web 服务的内容,请查看WWDC 2015的 session 710 CloudKit JS and Web Services. 另外,你可以在 Github 中,查看本文的示例代码。
这是一个系列文章,查看更多请移步目录页