Workflow 添加到主屏幕是如何实现的

大名鼎鼎的 iOS 平台的 Workflow 是一款将各个 app 打通,实现自动化完成一系列动作的应用,类似于 macOS 上 Automator。如果你还没有试过,强烈推荐你体验一下。这个 app 已经被苹果收购,可以免费下载。

我发现 Workflow 有个保存到主屏幕的功能挺有意思,研究了一下。

当你选择分享某个 workflow 时,UIActivityViewController 中有个「Add to Home Screen」的选项。点击之后会跳转到 Safari,这个时候你会发现跳转的地址是这样的:

1
data:text/html;base64,PGh0bWw+CjxoZWFkPgogIDx0aXRsZT5Mb2cgV2F0ZXI8L3RpdGxlPgogIDxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV...

因为不是做前端的,之前还没有见过这样的地址。查了一下,这种格式叫 Data URI,可以直接将文件内容嵌入到文档中。它的语法是这样的:

1
data:[<mediatype>][;base64],<data>

也就是说 Workflow 把一个网页的内容以 Data URI 的方式直接放到了 Safari 的地址栏里。

那后面的 base64 字符串内容是什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<html>
<head>
<title>Log Water</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="apple-touch-icon-precomposed" href="http:/localhost:51884/webclips/images/Log%2520Water/icon.png">
<link rel="apple-touch-startup-image" media="(orientation: landscape)" href="http:/localhost:51884/webclips/images/Log%2520Water/landscape-launch.png"/>
<link rel="apple-touch-startup-image" media="(orientation: portrait)" href="http:/localhost:51884/webclips/images/Log%2520Water/portrait-launch.png"/>
</head>
<body>
<a id="jump" href="workflow://x-callback-url/run-workflow?name=Log%20Water&id=35FD1DED-8322-4477-96B0-2BE4778F9C08&source=homescreen"></a>
<img id="icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu4AAALuCAYAAADxHZPKAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAhOAAAITgBRZYxYAAAABxpRE9UAAAAAgAAAAAAAAF3AAAAKAAA..." style="margin:auto; position:absolute; width:250px; height:250px; top:0; left:0; bottom:0; right:0;"></img>
<script type="text/javascript">
if (window.navigator.standalone) {
var e = document.getElementById('jump');
var ev = document.createEvent('MouseEvents');
ev.initEvent('click',true,true,document.defaultView,1,0,0,0,0,false,false,false,false,0,null);
document.body.style.backgroundColor = '#FFFFFF';
setTimeout(function() { e.dispatchEvent(ev); }, 25);
} else {
var icon = document.getElementById('icon');
var frame = document.createElement('iframe');
frame.src = 'http://localhost:51884/webclips/instructions/Log%2520Water';
frame.style.cssText = 'width:100%; height:100%; border:none; margin:0; padding:0;';
document.body.style.cssText = 'margin:0;';
document.body.removeChild(icon);
document.body.appendChild(frame);
}
</script>
</body>
</html>

html 的 head 中有一行这样的 meta:

1
<meta name="apple-mobile-web-app-capable" content="yes">

这表示这个网页是一个运行在 standalone 模式的 web app,可以被添加到主屏幕。当用户从主屏幕打开这个页面时,Safari 应该隐藏自身的界面只显示网页内容,看起来像原生的 app 一样。

body 就只有一个 a 标签和一张图片,图片的内容也是使用 Data URI 的方式写在 html 中的。

最后有一段 JS,判断浏览器模式是不是 standalone:

  • 是:就把页面的背景色改成白色,并且模拟点击页面中的 a 标签;
  • 否:将页面内容替换成 http://localhost:51884/webclips/instructions/Log%2520Water 的内容。Workflow 在本地启了一个 web 服务,返回的网页内容是教你如何把网页添加到主屏幕,类似下面这样:

Share Workflow

这样一来整个流程就清晰了:

当用户首次打开这个页面时,会访问 localhost 加载教程页面;而用户把页面添加到主屏幕后,通过主屏幕上的 Icon 打开页面时会运行为 standalone 模式,JS 会直接打开 a 标签中的 URL Scheme 来跳转到 Workflow app。

给鸡排饭加个蛋