一个 macOS 独立项目的诞生记

ticklishgoose 发布于2年前
0 条问题

Melisandre 是我刚刚发布的一款 macOS 的窗口管理软件,特点是使用快捷键来自由移动和缩放窗口(能达到跟鼠标拖动一样的效果)。她是第一个完全由我自己从零开始完成的作品,名字来源于冰与火之歌里侍奉光之王的红袍女 Melisandre。

整个项目的实现过程中,从 logo 和图标的设计到客户端,web 端,后端的开发,全部自行完成,也算是当了一把全栈程序员。欢迎访问 Melisandre 的官方网站来下载体验。

起始

灵感来自于几个月前的一天,在某次鼠标和键盘间不停切换来处理窗口移动和键盘输入的时候。突然想到如果有个软件能够使用键盘来控制窗口,那不就可以避免这种情况了么。于是便去找了一些现有的窗口管理软件来使用。然而发现我找到的窗口管理软件都是支持固定大小的窗口布局,比如屏幕的二分之一,三分之一。而我希望的是一个可以自由移动的窗口管理软件。“既然没找到,那就自己撸一个吧”这就是我当时的想法。

技术栈

Melisandre 1.0 版本完成时用到的技术栈及环境:

  • 客户端:Swift 4, Xcode 9 beta
  • 官网 web 页面:js + Vue
  • 后台 API 接口:Golang
  • 数据库:PostgreSQL
  • icon 与图标设计: Sketch

服务器用的是 Vultr 的美国机房,官网页面支持 https 和 http2。

从零开始到发布上线

首先,便是调研实现客户端功能所需的技术。所有窗口管理软件使用的第一步都是需要给 App 授权 Accessibility 使用,那便去学习了 Accessibility API 的文档。虽说 macOS 开发和 iOS 开发有些不同,但花了一些时间后,还是很顺利实现了我自己的需求:使用快捷键自由移动和缩放窗口。

接下来,如何分发软件。最初的想法是上架 Mac App Store 的(这里就给自己挖了一个大坑,后面会说),毕竟 App Store 里有不少同类型的 App。最近软件自动订阅付费成为了一种趋势,于是便去学习了苹果的 In-App Purchase 的文档 以及 Raywenderlich 上的这个教程 。一边着手接入 Auto-Renewable Subscriptions,一边完善 App 的其余功能及 UI。

于此同时,我把 App 分享给了一些同事来试用。感谢这些同事,并没有因为 Demo 版本的丑陋就拒绝使用,反而提出了不少有用的意见和建议。综合了这些意见之后,我发现目前的窗口管理软件所使用的固定布局确实是一种很方便的管理方式。那既然要把 Melisandre 作为正式发布的产品,这种模式还是必不可少的。思考一番之后, Melisandre 的第二个重要功能 Pushpin(图钉) 诞生了。Pushpin 功能总结起来就是:通过预设的窗口位置,像使用图钉一样,使用快捷键把窗口钉到屏幕的指定位置。而且 Pushpin 更加灵活,自定义的程度更高。

这期间还 Melisandre 还加入了另外一个实用的功能:多显示器下的窗口移动。这虽然也不是原创功能,但是在细节上,我花费了一番功夫去调整,使得窗口在多显示器间切换显得更加自然。Melisandre 控制的窗口切换显示器后,窗口在新显示器下的位置与之前的位置相同,但是窗口大小不会变化。如果两个显示器分辨率不同。那么之前的宽度或者高度的撑满的状态在下个显示器里会被保持。其实上面的逻辑不需要专门去理解,总之就是为了让窗口在多显示器的切换更加自然。

关于付费,目前版本的 Melisandre 基础功能是可以免费使用的,包括移动和缩放窗口,预设的四个 Pushpin 功能。但是一些自定义设置,比如快捷键的自定义和 Pushpin 的完整功能需要付费解锁。付费采用订阅制,而不是之前常见的版本买断,这也是我想去做的一个尝试。

在一切准备就绪的时候(包括 Apple 的订阅功能也已经开发完成),接下来就碰到了 1.0 版本发布一个大坑:无法上架 Mac App Store!!前面已经提到,整个 App 的核心,控制窗口移动的这个功能,是使用 Accessibility API 来实现的。苹果对上架 App 的要求是必须使用 Sandbox,而一旦对 App 启动了 Sandbox,Accessibility API 就无法控制其它 App 的窗口了(自己的窗口还是可以控制的)。这其实也是我的粗心,我是在未开启 Sandbox 的情况下完成了窗口的功能,之后开启了 Sandbox 开始 接入和测试订阅付费功能,期间一直没有去验证之前的窗口功能的可用性。

没办法,只能放弃 Mac App Store 上架这条路,而选择自己渠道发布。你可能会问那现有商店里的窗口管理软件是怎么上架的呢。这是由于他们上架都非常早,上面的那条限制只对新 App 有效,不过可以预见的是,苹果会逐步关闭这个通道。不过按照 macOS 现在这个爹不亲,妈不爱的状态,也难说。接下来就是自己发布所碰到了一连串的问题了。

首先是支付的问题。调研一番后,国内的服务就先排除了,已经没有针对个人的支付接口了。本来打算使用 stripe 来接入支付,但发现账户激活需要美国的身份,遂放弃。最终选择了 Fastspring 的支付服务,看了 Fastspring 的文档后,发现其功能做的相当完善。接入后,连 E-mail 服务都提供好了,当然代价就是收费贵。

其次是 License 的管理,由于我想采用订阅付费的授权形式,就没有找到合适的现成服务。这里提一下,如果使用买断式的 License,国外有个叫 DevMate 的服务可以使用,免费版本配合 Fastspring 已经足够用了。我最后是自己实现了一套 License 机制,使用 hmac hash + RSA 来验证。

由于是自己的实现,就需要一个后台服务来生成 Licese,这个后台服务本来打算用 Swift 来写的。但是 Swift 本身语言还不稳定,服务器开发环境也不成熟,考虑到后期的维护成本,就放弃了这个想法。在 Python 和 Go 中间犹豫了很久之后,最终选择了 Go 实现了这个后台。数据库系统选择了 PostgreSQL。这里不得不说一句,使用 VS Code 来开发 Go,体验真是好的不要不要的,甩出 Xcode 下写 Swift 一条街。

最后是客户端的收尾工作,由于之前为了上架 App Store,一些功能受限,而去掉 Sandbox 后,居然还有些功能不能正常工作了。于是对整个 App 测试整理,并调整了一些 UI。接着又用 Vue 撸了一个官网的页面。1.0 版本总算是可以发布了,于是在 iPhone X 发布的当天,Melisandre 1.0 版本上线了。

然后就是服务器的配置了。嗯,独立开发嘛,运维也要自己做。Go 语言的一个好处就是部署很简单,省了不少事情。官网的网页就是静态文件,一个 nginx 网关就可以直接提供服务了。

目前 Melisandre 最新的版本是 1.0.1。主要是接入了 Sparkle 的版本自动更新服务。这也是非 App Store 应用里必要的功能了。其实 1.0 版本已经接入了 framework,但是有一些配置的问题,1.0.1 中修复了一下。

这期间碰到的技术细节其实还有很多,之后另开博文来写吧。

上面的没有提到设计的过程,其实设计是一直穿插在开发中进行的。虽然作为一个后台服务的 App,涉及的页面并不多,但是还是有一些的图标需求的。作为一个设计门外汉,我是一边摸索一边学习,找来了一些 icon 的素材,结合自己的需要做一些改动。包括官网 web 页面的设计,也是借鉴了不少的网站照葫芦画瓢完成的。这里不得不感叹一下 Chrome 浏览器的 inspect 功能,真的非常好用。虽说设计得很业余,但最终的效果我还是很满意的。

总结

由于我选择了一条困难的路径,整个过程还是非常折腾的。不过总算是从零开始完整地实现了一个产品。全栈技能 ++,还有就是收获了满满的打破舒适区的成长感。

当然对用户来说,他们并不会关心你背后的技术怎么实现。他们只需要你的产品能够解决需求,同时用起来舒服,如果能再好看一些,那就更完美了。Melisandre 还有很多可以优化的空间,接下来的时间再一步步完善了。

独立开发不易,同时也深刻体会到 App Store 是项伟大的发明,它使得开发者将精力聚焦在好的 App 实现上,而不用太过关注发布和更新。对独立开发来说,把 App 上架绝对是一件省时省力的途径。

虽说是选择了一条困难的道路,但还是有不少现成的服务可以使用的,国外针对这样的服务还是有不少的。我目前选择的方案,都是我在调研或者看了别人分享的经验之后所选择的。不一定是最优,如果你知道更好的方案,欢迎一起来讨论。

我觉得以后应该还会有更多的独立作品。

不知不觉已经写了不少了,非常感谢你能耐心看完。如果觉得对你有帮助,不妨请我喝杯咖啡,又或者…买个 Melisandre 的 License 呢,嘻嘻。

查看原文: 一个 macOS 独立项目的诞生记

  • crazymouse
  • silverfish
  • silverfrog
  • bigfrog
  • tinytiger
  • yellowduck
  • silvergorilla
  • goldenbear
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。