大名鼎鼎的 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. <html>
    2. <head>
    3. <title>Log Water</title>
    4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    6. <meta name="apple-mobile-web-app-capable" content="yes">
    7. <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    8. <link rel="apple-touch-icon-precomposed" href="http:/localhost:51884/webclips/images/Log%2520Water/icon.png">
    9. <link rel="apple-touch-startup-image" media="(orientation: landscape)" href="http:/localhost:51884/webclips/images/Log%2520Water/landscape-launch.png"/>
    10. <link rel="apple-touch-startup-image" media="(orientation: portrait)" href="http:/localhost:51884/webclips/images/Log%2520Water/portrait-launch.png"/>
    11. </head>
    12. <body>
    13. <a id="jump" href="workflow://x-callback-url/run-workflow?name=Log%20Water&id=35FD1DED-8322-4477-96B0-2BE4778F9C08&source=homescreen"></a>
    14. <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>
    15. <script type="text/javascript">
    16. if (window.navigator.standalone) {
    17. var e = document.getElementById('jump');
    18. var ev = document.createEvent('MouseEvents');
    19. ev.initEvent('click',true,true,document.defaultView,1,0,0,0,0,false,false,false,false,0,null);
    20. document.body.style.backgroundColor = '#FFFFFF';
    21. setTimeout(function() { e.dispatchEvent(ev); }, 25);
    22. } else {
    23. var icon = document.getElementById('icon');
    24. var frame = document.createElement('iframe');
    25. frame.src = 'http://localhost:51884/webclips/instructions/Log%2520Water';
    26. frame.style.cssText = 'width:100%; height:100%; border:none; margin:0; padding:0;';
    27. document.body.style.cssText = 'margin:0;';
    28. document.body.removeChild(icon);
    29. document.body.appendChild(frame);
    30. }
    31. </script>
    32. </body>
    33. </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%20Water 的内容。Workflow 在本地启了一个 web 服务,返回的网页内容是教你如何把网页添加到主屏幕,类似下面这样:

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

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

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