一个 Mac App 是如何诞生的:Browser Deputy
某日在 Vercel 上看 Anybox 网站的访问统计,其中有个从未见过的网站,自然而然的,我想了解关于 Anybox 的评论。这是一个频繁更新的个人博客,首页的三篇文章里都未提及 Anybox。归档列表也比较长,而且篇幅不短,肉眼搜索无果,只好使用谷歌的站内搜索。
我使用的浏览器是 Chrome,具体操作步骤:
可以看到,这个操作即使可以完全使用快捷键完成,它仍然需要敲击多次键盘,特别是在删除 URL 路径时。简而言之,它费手指。
从上面的操作我们可以看到,未知部分仅有关键词。我们可以从当前标签页获得网站,然后拼凑出网址并在浏览器中打开。
这种需求就是为什么我们使用启动器 App:通过每个启动器都支持的自定义动作实现特有的需求。
比如如果我要在 Raycast 内实现这样的一个扩展,大概步骤会是这样:
runAppleScript
函数获取浏览器的当前标签页的 URL 和使用 `open` 函数打开链接以我编写过多个 Raycast 扩展的经验预计,实现这个扩展用时不会超过一小时。
当然,如果我花了这一个小时去写这个扩展,也就不会有这篇文章了。
我的问题很容易解决,但是不会写程序的其他人呢?
这几年我写的几个应用,从最早的 Seamless,到 OK JSON,再到 Anybox,这些应用的最初想法都来源于我自身的需求。我相信,作为一个普通人,我的需求也不特殊,总会有部分人也有着同样需求。
不过,我也承认,这些都是小众需求。我的几个应用都是收费应用,现在面临着一样的问题,如何让他们被更多人知道。所以某种程度上,这个应用也是一种 #BuildInPublic 的尝试。
按照目前的想法,这个应用就是一个可自定义搜索引擎的应用,不同之处就是提供了当前标签页信息。它的交互应该是这样的:
Raycast 可以添加 Quicklink,Alfred 可以添加 Web Search,LaunchBar 也可以添加 Action。我前几年用过一个叫 Haste 的应用,它可以通过全局快捷键呼出自定义搜索引擎面板。这些都说明,自定义搜索引擎的可替代品太多了,如 Alfred 和 LaunchBar 可谓将效率优化到极致,而新晋应用 Raycast 则通过优秀的设计降低工具门槛,效率上也可通过配置达到前辈水平。
如果我们做一些简单的商业调查,会发现上市已五年的 Haste 应用,在中国区的 Mac App Store 仅有区区 20 个评分。换言之,这个自定义搜索引擎应用,它的商业前景是很渺茫的。
所以,得加功能。
既然这个应用已经需要通过 Apple Script 和浏览器交互了,那就想想这上面能做的事情还有什么吧。
除搜索外,复制当前标签页的 URL 也是一个常用功能,还可以支持 Markdown 形式复制。另外还可以支持搜索书签、历史记录、标签页等。
大概一年前,我就有想过写此系列文章,当时设想的应用是搜索菜单项应用,交互恰好和这个搜索引擎应用一致,所以不妨添加此功能。
「搜索菜单项」这个功能,我虽然不用,因为常见的操作我通过快捷键完成,但它其实很常见。
Raycast 有内置扩展 Search Menu Items;Alfred 有 Menu Bar Search Workflow;Typora 的开发者也有一款名为 Paletro 的应用;知名的 Mac 工具应用开发厂商 Many Tricks 旗下有一款偏向鼠标操作的 Menuwhere;还有来自独立开发者 Roey Biran 的 Finbar。举的这些例子说明这个功能是有市场和认知度的,并不会无谓地复杂化应用。
总结目前设想的功能:
在开发一个应用时,设计总会和最终产品有区别,这其中可能是因为技术问题,也可能是因为投入思考的时间更多,有更好的设计。但在写代码前就通过文档或原型定义产品,总归是更好的。我们的大脑内存有限,写文档或者画原型的过程,不仅是思考过程,也是存储过程,除了让产品的逻辑更清晰外,还能让我们记下思维结果,不至于遗漏或重复。
俗话说得好,工欲善其事,必先立其名。
应用名称确定好后,其实也就确定了这个应用的边界。
但对我而言,在写代码前就需要想好名字的原因是,在苹果的应用开发工具 Xcode 内新建一个应用时,需要输入一个唯一的应用包名(Bundle Identifier)。惯例是使用反向 DNS 格式(Reverse-DNS Format)。比如苹果的官网域名是 apple.com,苹果浏览器 Safari 的包名则是 com.apple.Safari
。应用包名在应用上线前都是可以更改的,但一旦开始写代码了,代码内难免出现应用名称,这个也需要一同修改。为了减少麻烦,我一般会在新建项目前确立好应用名称。
这个应用和浏览器相关甚重,所以名字中需体现这一点,我想到了这些名字:
可以看到,我的创意实在有限,这些名字基本上就是「浏览器助手」不同翻译。
里面我最喜欢的是「Browser Sidekick」。
如果你用牛津高阶词典查一下「sidekick」释义,你会发现如此例句:Batman and his young sidekick Robin。
当下我就觉得是它了,就是这个意思,这个应用就是你的罗宾!但该死的是,这个名字已经被一个浏览器抢走了,这意味着我需要直接放弃「sidekick」这个词,连带划去「Web Sidekick」。
剩下的名字里,「Browser Assistant」听起来太资本主义,让人想起上班;「Browser Helper」让人想起早些年的 IE 浏览器流氓插件。「Web」这个词虽然比较短,但不如「Browser」准确,所以名字基本可以确定为「Browser something」,其中 something 为「sidekick」的同义词。
打开 macOS 的词典应用,里面有本《American English Thesaurus》词典,虽然没有「sidekick」的同义词,但可查阅「helper」可得到:
assistant, coworker, fellow worker, workmate, teammate, helpmate, helpmeet, associate, aider, aide, colleague, supporter, partner, collaborator, abetter; subordinate, deputy, auxiliary, second, second in command, number two, right-hand man, right-hand woman, wingman, attendant, junior, acolyte; accessory, accomplice, henchman; sidekick.
结果可谓理想也不理想,合理选择并不多。「aide」听起来太政治了,于是只剩下「deputy」,那就叫「Browser Deputy」吧。
名字确定后,也可以注册域名了。
对于 iOS 应用而言,网站和域名的重要性并不如 macOS 应用。因为大部分人只会在 App Store 内搜索并下载 iOS 应用。而 Mac App Store 远远不如 App Store 活跃,搜索引擎给其带来的流量仍是很重要的。而且因为应用有搜索菜单项这个需要去除沙箱才能实现的功能,所以无法上架 Mac App Store,更需要做好网站,提高下载转化率。
但另外一方面,一个 app 后缀的域名,一年需要 15 美元;虽然独立域名看起来美观一些,但同一个开发者之间的应用在域名上看不出关联,不能互相推广;而且域名多了之后,相关的网站项目也多,维护和续费也麻烦,接入 Paddle 的支付 SDK 时,新域名都要审核。权衡之下,决定不注册新域名,直接使用开发者域名 anybox.ltd。
此时应用的功能和名称都已定义完毕,按照企业工作流程,下一步是将需求文档交由相关人士,包括客户端开发、后端开发、设计师等,然后客户端开发在收到设计师的 UI 设计图后,方可开始具体的 UI 和交互开发。
Browser Deputy 是个纯客户端应用,不需要和和服务器通讯,所以不需要后端开发;而我对设计软件的使用实在不甚熟悉,所以也无法做出高保真设计图,而且应用界面就是一个应用中常见的命令面板,所以我选择直接编写代码,不画原型也不做设计。
不过在写代码前,我一般还是会参考下相似应用是如何设计的。
macOS 自带的「聚焦」搜索(Spotlight)。默认情况下只显示搜索框,输入内容后再展开结果。搜索框左侧是放大镜图标,字体的字号较大,右侧是当前选中的结果类型的图标。每个结果项只有一行,左侧是结果类型的图标,然后是内容,右侧可能有二级结果页的提示箭头。还可能有显示结果类型的章节标题(section header)。
Xcode 的 Open Quickly 毫无疑问是我使用次数最多的命令面板。在写代码时,可通过⇧ + ⌘ + O
唤起,输入大致的文件名称,按下回车,直接在 Xcode 编辑器打开当前选中的代码文件。它默认情况下和 Spotlight 一样,只有一个搜索框。每个结果项有两行文字,与左侧的文件类型图标垂直居中。标题行的内容是文件名称,详情行的内容是文件路径。当前选中行会使用 macOS 的主题色作为背景颜色,搜索的关键词使用更大的字重高亮。
相比之下,Raycast 的设计就比较复杂了。虽然是单行设计,但结果项从左往右依次是类型图标、标题、次标题、类型说明。此外也有章节标题,底部还有个固定的工具栏,提供更多操作的入口和说明。
上述三个是比较有代表性的,其他可供参考对象还有 Alfred、LaunchBar、Paletro、Arc 浏览器的 Command Bar 等。
让我们再次列举 Browser Deputy 设想的功能:
我的开发习惯是,功能实现由简至难。这里面的六个功能里,二、三、四可划分为同一类功能,即内置命令。实现它们只需通过 Apple Script 获取当前浏览器的标签页,而这些我早已在 Anybox 中实现,所要做的事情只是复制粘贴。然后是搜索浏览器菜单项,搜索书签和历史记录,最后需要最多时间的,可能是自定义搜索引擎,因为涉及的界面较多。
把这些功能分类后,可以得到这些在代码内定义的功能类别:
其中原定的搜索标签页,在调查后决定放弃,因为菜单项中已包含标签页,而且我怀疑此功能的实用性。上述的功能顺序也是我们实现的顺序。
经过思考后,决定采用单行设计,因为内置命令如拷贝URL,并无第二行内容可展示。结果项左侧为类型图标,然后是动作名称。底部加入工具栏,一则提供当前应用信息,二是提供设置入口。用户可随时进入设置页面,而无需通过菜单栏图标或者快捷键 ⌘ + ,
。
在做过多款 macOS 应用,接触了许多用户后,我有些体会,即使你做的是效率或者工具应用,也不能假设用户会知道一些如 ⌘ + ,
可打开设置的惯例。应用的所有功能,应该是对用户可见的。可见并不是指把所有功能按钮都放在显眼的工具上,而是要能被用户发现。比如应用内可关闭的帮助提示和帮助指南、应用外的文档网站等。
实现内置命令后,需要实现菜单项。在通过 Accessibility API 获取应用的菜单项时,我才意识到菜单是树状结构,比如 Chrome 的 Developer Tools 菜单项,它位于 View → Developer 内。这个层级信息是有用的,应该展示给用户。但如今的单行设计内,我们没有更多的空间展示这个信息。所以决定将结果项改为两行内容。
这版设计的主要不同之处是把结果项从一行改为两行,然后在右侧添加了菜单项的快捷键标签,此外也更改了窗口的背景颜色,增大了对比度。
对于书签,第二行内容自然是 URL。而对于内置命令,第二行内容是拷贝结果,在 UI 上保持一致,也符合逻辑。
实现过程遇到了一些问题,比如不同浏览器对于书签的储存方式不一样,但大体上可分为三个阵营:Chromium、Safari 和 Firefox。但最复杂的是 Safari。即便这个应用已能直接访问文件系统,但若想访问 Safari 的书签文件,仍需用户手动授权。为此我们得增加一个授权按钮和提示说明,不过这是最后的问题了。
工具栏的中间还有很大空间,可展示更多信息,当然,此处信息仅用于 DEBUG。
应用到这个程度了,有些设置也可以先出台了,比如激活 Browser Deputy 的快捷键、内置拷贝动作的快捷键。
通过快捷键复制当前标签页的 URL 是非常方便的,但主流的浏览器均无提供,我第一次看到此快捷键,也是在 Arc 浏览器。
本来计划实现搜索书签功能后,继续做历史记录的,但研究后发现,历史记录可能是最麻烦的。Browser Deputy 支持大部分常见浏览器,包括 Chrome 以及其他基于 Chromium 项目的浏览器(如 Edge、Brave、Vivaldi、Arc、Opera )、Safari 以及 Firefox。除 Safari 把书签存放在一个 plist 文件外,其余浏览器都把书签存放在一个 JSON 文件中,只是格式或许有差异。对于获取书签,我们只需解析对应的书签文件即可。而历史记录较为复杂:这些浏览器都把历史记录放在 SQLite 数据库中,有些还把内容存放在不同的数据表里。于是我决定先做自定义搜索引擎。
咋一看这表单还挺复杂的,因为除了设置名称,我们还支持设置图标和图标颜色,同时还添加了一些帮助信息以及模版参数按钮,便于用户使用。我考虑过去掉图标和图标颜色设置,让表单变得简单一些,但这些体现个性的设置,如果我是用户的话,我会希望有。而且考虑到用户不会频繁添加或更新搜索引擎,所以决定保留这些设置。
设置页面全部使用 SwiftUI 实现,使用了许多 macOS 13 才支持的特性,而设计则参考了 macOS 13 的设置应用。所以 Browser Deputy 只支持最新版本的 macOS,即 macOS Ventura。
Mac 用户的升级热情不如 iPhone 用户,一般用户也期待应用至少支持最近的一两个版本。但考虑到因适配需额外增加的开发时间,以及支持系统版本数量的收益是逐渐递减的,因为用旧系统的人会越来越少,而且 macOS 14 还有几个月也要发布了,再加上我手上也只有 macOS 13,所以最终决定只支持 macOS 13。
这其实是个很轻松的决定:在应用新建项目前,我就决定只支持 macOS 13,因为我在 OK JSON 中使用了 SwiftUI 的 GroupedFormStyle
,那时就发现使用 SwiftUI 的 Form 来实现设置页面极其方便。所以我决定在未来的项目里充分利用 SwiftUI。虽然中途也遇到了 SwiftUI 的一些问题,但通过组件化,我可以很轻松地复用这些组件:听起来稀松平常的,但「复用 UI 组件」在 macOS 的传统 UI 开发框架 AppKit 内并不容易。
添加自定义搜索引擎功能后,最初的设想,直接使用 Google 站内搜索的功能也可以使用了。主窗口的设计也做了一些调整,主要为使用系统主题色高亮当前选中项的背景。
在开始啃最后一个难题前,我决定先折腾折腾。原来的面板窗口的背景是纯白的,虽然对比度没有问题,但我想加上类似 Spotlight 的毛玻璃效果,即窗口背景会随着背后的内容而发生变化。效果如下面的截图,「Google」标志和窗口背景的融合。
此外,应用首次启动时,应该显示的快速指南也先做了。而且到最后阶段了,本地化也可以一并做了,于是应用能以简体中文显示。
在花费了一些功夫后,搜索历史记录功能也完成了。
作为一款搜索书签应用,我在开发过程也不免拿它和 Anybox 比较,看看二者的体验差距如何。Anybox 的快速查找在体验仍有许多优势,比如高亮显示搜索关键词、左侧的网站图标、按上次打开顺序排列的结果、右侧的打开快捷键、可直接粘贴 URL 等功能。
右侧的快捷键 ⌘ + 1–8 打开前八项,Browser Deputy 此处已经放置自定义命令的关键词和菜单项的快捷键,所以会优先显示这些更重要的;左侧在网站提供图标时,自动显示网站图标,这个功能还可应用到自定义搜索引擎;记住上次打开的项目,搜索时按上次打开时间排序;此外也支持通过 ⌘ + ⏎ 直接粘贴书签或者历史记录的 URL。
通过这些细节变动,算是把体验差距拉小了。
自定义脚本是计划外的功能。在计划的功能实现后,我发现了自己的一个需求无法通过这个应用完成:使用其他浏览器打开当前标签页。
比如我在 Anybox 中为 Netflix 设置的偏好浏览器为 Safari,但 Netflix 网站最近不知道上线了什么破烂玩意儿,使用 Safari 打开会出现如下界面,所以我只好切换到 Chrome。
我的第一反应是通过支持自定义的 Apple Script 脚本实现这功能,但这可能需要更多的时间,不妨放在未来的版本里。所以我单独为这需求,在应用内实现了一个 URL Scheme:bd://open?app=com.google.Chrome&url={url}
通过 URL Scheme 能很快地完成预设功能,但用户的需求是无法预测的,这也是为什么苹果在三十年前就支持 Apple Script,在这几年大力发展 Apple Script 的替代功能捷径。
所以在实现了上述的 URL Scheme,我开始尝试实现自定义 Apple Script 脚本。所幸的是,得益于 SwiftUI,自定义脚本的很多组件都复用于搜索引擎,实际工作量远低于预估工作量。
目前我其实也没用上这个自定义脚本功能,但它大大地提高了 Browser Deputy 的扩展性。而扩展性是好 Mac 应用的标志之一。
一个效率工具,让用户少敲一下键盘也是效率的提升,所以我添加了「自动执行关键词命令」选项。我想象过添加类似 LaunchBar 的执行方式:x 秒后键盘无操作,即执行当前选中的命令。但细思之下,觉得这对应用的搜索匹配有很高要求,我暂时无法实现这样级别的搜索引擎。另外也对用户的心智有一定压迫,用户需要及时决定下一步操作。再者,这样的交互也确实不常见,绝大多数网站和应用的命令面板的命令还是主动执行的。
另外底部栏内仅显示应用图标和名称及设置入口,但应用图标也可在搜索输入框的左侧显示,设置可通过快捷键 ⌘ + ,
进入。用户熟悉应用后,底部栏就是在浪费空间,因此我提供了隐藏底部栏的选项。
此外,右侧的关键词和快捷键标签内,也实现了对齐。虽然此处可通过使用等宽字体实现对齐,但苹果的等宽字体 SF Mono 的快捷键符号的显示效果不如系统字体 San Francisco,所以我额外费了些功夫使这些符号和字母对齐。
到了这个时候,应用功能已完成,更多的事情在应用之外,包括应用图标、应用网站和最后的上线测试。
使用设计软件糊一个图标可能会很快,但如果你对你的设计能力没有信心,那最好还是别在图标上糊弄。从我的经验上来看,图标对转化率的影响颇大,包括下载转化率和付费转化率。如果你在应用已上线后更换图标,还可能有一星评论等着你。
我为 Anybox 更换过两次图标,第一次更换时收到了十多个用户在不同渠道给我的负面反馈以及两个一星评论。
食物营养跟踪应用 FoodNoms 最近通过更科学的 A/B Test 证实了我的经验:How FoodNoms' New App Icon Boosted Download Conversion Rate by 10%。
但自己实在不会,怎么办?
自己做不来的事,就找别人做。
国外有一批应用图标设计师,可谓包揽了大部分小众应用的图标设计,包括 Matthew Skiles、Gavin Nelson、Michael Flarup、Parakeet 等。有一些耳熟能详的应用的图标正是出自他们之手。我和上面一些设计师接触并合作过,收费标准不一,有些按小时计费,有些按件计费,但 500 美元是底线,2000 美元不罕见。
我也在 Upwork 上找过设计师,虽然价格要实惠很多,但那次合作的体验非常糟糕:那个设计师每次细小变动都要找你确认,最终导致我需要在线上同步配合他,相当于我也在工作了。最终出来的作品你也无法挑剔:每一个改动你都同意了的!我不知道这种体验在 Upwork 上是否常见,但我从此以后就放弃在上面寻找设计方案,因为我觉得我在上面找的是体力劳动者,而不是创意工作者。
对于我这种自己做不来,又舍不得花钱,还懒的人而言,能有其他选择吗?
其实很多人也应该知道答案了:应用图标我是用 Midjourney 画的,简单至极。
我试用过 Stable Diffusion,所以大致了解此类的 AI 画图工具的用法。付费订阅了十美元的基础计划后,我就直接在 Discord 上输入我的第一个 Midjourney prompt:
A flat-design style macOS app icon. The background is light-colored and main subject is a slanted steering wheel for pirate ships.
在换了一轮又一轮的关键词后,始终找不到心里想要的那个海盗船船舵。转念一想,用放大镜图标也未尝不可。于是下一类的 prompt 开始:
macOS app icon for a browser helper app. Abstract, flat, subtle gradients. The main subject is a magnifying glass.
Midjourney 一直在给我这类的风光图,我不明白。后来改关键词后,才知道原来是因为「browser」这个词。
最后通过「macOS app icon for a search app. Flat and simple. A slanted magnifying glass.」得到了一个合适的图标:
我选择了右上作为 Browser Deputy 的图标。
网站一直是我最不愿意做的事情,好在我几个月前上线了新版的 OK JSON,所以 Browser Deputy 的网站可在这基础上更改。虽然看起来是删删改改的事情,但实际也耗时两天。现在已可访问:浏览器效率工具 - Browser Deputy。
上线测试需要重置本地的配置项,以模拟用户首次打开时的状态,发现潜在的问题。比如窗口的初始大小和位置可能和想象中不一样、初始引导是否正确地弹出、内购是否正常运作等。
我在两年前的文章《从不懂 iOS 开发到 App Store 上架,开发过程和经验全分享 》分享过上线后推广的内容。如果说有什么变化的话,那就是我觉得努力做好产品这一点应该是在营销之上的。就算你愿意付费打广告,起码也得有好的产品,让人能把台本写好。App Store 给优秀产品的免费推广,对于独立开发者而言,应该优先考虑。当然 App Store 的编辑有审美偏好,不一定和你的产品一致,但即便如此,App Store 的搜索仍是最值得关注的,而不是在互联网内撒网。
话虽如此,Browser Deputy 因为无法在沙箱下工作,无缘 Mac App Store,失去了最简单最轻松甚至是最多的流量来源。
所以我只能在一些网站上发布应用,如 Product Hunt、V2EX、Reddit 等。这里面有中文网站和英文网站,所以应用等介绍文章需准备两种语言的,应用截图也是如此。
Browser Deputy 现已上线,售价 $11.99 美元,可免费试用 14 天。
即日起至 8 月 13 日,可通过 SSPAI 折扣码,在官网或应用内购买授权时,享受六折优惠,实付人民币 50 元出头,即可永久享受免费升级更新,包含大版本!
你可以在官网下载和体验 Browser Deputy。
> 下载 少数派 2.0 客户端、关注 少数派小红书,找到数字时代更好的生活方式 🎊
> 实用、好用的 正版软件,少数派为你呈现 🚀
© 本文著作权归作者所有,并授权少数派独家使用,未经少数派许可,不得转载使用。