我是如何把 Klib 带到这个世界的

2017-02-22 00:22:41 +08:00
 quietjosen

友情提醒:

这里不但有 Klib 的诞生过程

还有 独立开发者的真实写照

万字长文,谨慎继续…


历时 50 天、横跨 2 年、经过 258 小时的纯手工打造, Klib 终于来到了这个世界。

这两天 Klib 刚刚发布,收到很多用户的好评。赶着手热,记录下 Klib 的诞生历程吧。

Klib 是全新的 Kindle 标注、笔记 /书摘管理工具, macOS 平台。

缘起

2016 年底的时候,我在思考接下来做什么项目,并没有定下来,只是有个大概的方向:做个稍微大些的,以及技术上的需求,就是做一个有界面交互的。因为之前做的 i 系列工具(iPiciTimeriPasteiHosts)都是非常垂直的,并且都是菜单栏工具,我想让自己有所突破

正巧,当时也给自己定了读书计划,就是要经常读书,并且读完后要在博客上写文章。主要自己是独立开发,获取信息的渠道本来就不多,就要用这种方式强迫自己不断更新。而要写文章,很重要的内容就是读书时的书摘。我习惯于用 Kindle 读电子书,于是,很直接的需求就是,把 Kindle 中的标注、笔记复制出来,用于写文章。

而我大致搜索了下当时 macOS 平台下的标注管理工具,发现并没有趁手的。于是,就开始了程序员最喜欢做的事:造轮子

时间,正好是 2017 年元旦,详见 当天日记。顺便说一句,我有每天写日记的习惯,已经坚持几年了,也是为了逼迫单打独斗的自己,能够保持思考、反省

设计

这里借用设计这个名字,只是用以涵盖产品前期的各种思考。确实是有各种思考,如市场环境、商业模式、竞品分析、名字、 Logo 、做成什么样子、交互、如何发布、怎么运营、时间节点、等等。时间有些早了,这里简单回忆下几个比较重要的点。

定位

我一向倾向于做简洁的产品,这个工具也是如此。我最核心的需求就是导出 Kindle 中的笔记,最直观的衍伸就是能持久保存、定期回顾这些笔记。好了,就是这些吧,再多就难做的精简、好用了。重复+小结一下:

定位:精简的、 macOS 平台、 Kindle 笔记管理工具

功能集:

名字、 Logo

名字好定,因为我开发的工具都是 i 开头的(iPiciTimeriPasteiHosts),这个自然也是。再加上是 Kindle 相关的,于是就叫:iKindle. 当然,你知道最后不是这个名字;这个坑,以后再填。

Logo 也很容易呀,你看我之前产品的 Logo 就知道了:

iKindle 的 Logo 一定是类似下面这个样子的:

当然,你知道,后来也改了。这是后话。

交互

有了前面的铺垫,就可以考虑具体的交互。

交互一定要直观、符合直觉。开始时,大致的想法是这样的(幸亏我还保留了当时的截图):

左侧是书列表、右上角是书中笔记列表、右下角是选中笔记的内容、方便编辑。后来当然是不断演变了。

先是书

我一开始就没打开按作者来区分,因为我本人并没有追作者的习惯;并且,即使追,也没必要非得按作者分类,搜索不就可以了。

但是,太多书放在一起,就有了排序的问题。按书名、最后阅读时间、还是别的?其实你会发现,如果书多,再怎么排序都没用。那干嘛不让书少点呢?有了,如果只看最近的几本书,比如 3 本,还需要考虑排序吗?那些更早的书,归档起来不就好了,之后能搜索到就行。

于是,这就诞生了 iKindle 独有的创意:区分当下正在阅读的书、和之前已经阅读的书。形式上就是把已读的书放在一起,可以折叠隐藏。恩,挺棒的!

然后是笔记列表

程序员的思考,很直观的就是把所有属性都列出来,比如笔记内容、类型(是标注、还是笔记)、位置、添加日期。这用列表很容易实现。

可问题是,除了笔记内容,后面这些属性会占用大量屏幕内容,却又不重要,只要在我需要的时候能看到就可以了。

于是,那就把它们都隐藏起来好了,隐藏到简介里,这样笔记列表就只显示纯粹的笔记内容。恩,挺棒的!

再然后就是阅读、编辑当前笔记

你会发现,在最原始的设计中,这部分也占据了大量屏幕、却不得已有大量空白。并且,对于我实际的笔记来看,很多都是一句话的,在列表中就几乎可以展示了,不再需要额外的这个区域。再并且,真正编辑的需求并不大(最多的就是在标注时,前后有不需要的内容、符号),大部分时间只要看就行了。

于是,这部分也被我干掉了。那如果笔记确实很长怎么办呢?我自然想到了 Finder 的快速预览。 OK ,那就搬过来吧。恩,挺棒的!

下一个就是搜索

这个并不需要特别设计,跟系统的保持一致好了,不一致反而不好。恩,挺棒的!

最后,就是整体的交互

我一看,经过上面的改进,这不就是个系统「提醒事项」的样子吗?好吧,那就做成「提醒事项」的样子。恩,挺棒的!

开发

设计差不多了,就可以码了。我开发的顺序一般是三步走:数据结构、堆 UI 、完成业务逻辑。

数据结构

数据结构又主要为三块: Kindle 本身数据结构、内存中数据结构、持久化数据结构。

Kindle 本身数据结构

好吧,先吐槽一句:Kindle 的数据结构真垃圾!至少是开放出来的数据结构真增加,就是一个纯文本,类似于以下的内容:

史蒂夫•乔布斯传(Steve Jobs:A Biography) (沃尔特•艾萨克森 (Walter Isaacson))
- 您在位置 #5676-5677 的标注 | 添加于 2015 年 8 月 22 日星期六 下午 1:33:54

一家妥善经营的公司能够大量催生创新,远胜于任何一个有创造性的个人。
==========

看起来还行是吗?那再来一些多语言版本的:

- Your Highlight on Location 1-6 | Added on Thursday, January 5, 2017 4:20:05 PM
- Ihre Markierung bei Position 6-7 | Hinzugefügt am Mittwoch, 1. Februar 2017 12:21:36
- Votre surlignement à lʼemplacement 9-12 | Ajouté le mercredi 1 février 2017 12:51:12
- Tu subrayado en la posición 33-35 | Añadido el miércoles, 1 de febrero de 2017 12:42:10
- 位置 No. 39-42のハイライト |作成日: 2017 年 2 月 1 日水曜日 13:00:33
– Ваш выделенный отрывок в месте 45 – 49 | Добавлено: среда, 1 февраля 2017 г. в 13:12:38
- 您在位置 #38-39 的标注 | 添加于 2017 年 1 月 5 日星期四 下午 4:07:22

单单是识别其中的日期,就有种想 shi 的冲动:要获取日期文本、识别格式、确定 Locale ;另外,还不知道日期所在时区。

这里我做一个小机巧:数据结构的解析能力可以动态更新。也即,在二进制程序不变的情况下,可以从云端获取数据的最新解析方式。这样,当用户反馈给我不支持的格式时,我只要更新云端的解析能力,客户端就都可以无痛、后台式更新了。为自己这个设计点赞!

内存中数据结构

这个没太多好说的,定义书、笔记的各个属性即可。

一个主要的机巧在于:如何识别笔记的唯一性?前面的文本中可以看到,笔记并没有所谓唯一 ID 之类的东西。既然没有,那就把笔记整体、或整体的 Hash 值作为唯一 ID 好了,反正笔记的数据量并不大,一个人一生的笔记,可能也没有 iPhone 拍的一张图片体积大。

持久化数据结构

这一步,主要在结构化存储、和非结构化存储中纠结。前者数据规整,适合笔记存储,但扩展性差。后者优缺点大致和前者相反。

最后,我还是选择了结构化存储。具体的,就是 SQLite. 一方面笔记类数据实在是太规整了;另一方面,抱着学习的态度,反正我哪种都不熟悉,随便拉一种出来练手吧。

还有一个考量就是:数据结构的公开性。数据是用户的、宝贵的,如果使用私有的二进制数据结构,万一哪天要停止维护了,用户就没有任何办法可以解析这些数据。而使用公开的 SQLite 则不怕,任意一款 SQLite 工具都可以查看用户自己生成的数据。这样做,对用户是负责任的。

堆 UI

这部分倒没有太多可说的。

一方面就是一些核心控件的用法,如 NSOutlineView, NSTextView, NSPopover, NSSearchField. 其中,Source List 模式的 NSOutlineView 是有些坑的,会出现诸如 UI 刷新不及时、图标丢失等现象。有遇到类似问题的朋友,可以找我聊聊。

另一方面就是界面的细节,如字体、字号、颜色、留白、间距、等等。这方面我倒是偷懒了,因为基本就是「复刻」系统「提醒事项」

实现业务逻辑

打通数据流

比如,选择右侧书时,右侧笔记列表能相应变化;编辑笔记时,数据能持久化;查看笔记时,能读取正常的数据;等等。是不是听起来都像是废话?恩,就是这么回事,做对就行了。关键是 代码结构要好,不要有复制代码、严重耦合之类的问题。

用户引导层面

比如,开始时引导用户手动导入、进行过程中引导用户如何操作。这部分我基本没做太多,因为希望程序可以做到无需引导。不过,引导用户导入这一步,做的确实不好,还要再改进。

外围的一些功能

比如,账户系统、日志及反馈系统、等等。这里就不再铺开了。

测试

单元测试

其实,单元测试是在开发一开始就进行的。尤其是对数据结构部分,要充分测试。如果这部分没问题,只要程序能跑起来,基本就没有大问题。如果这部分有问题,改 Bug 够喝一壶的。

并且,要在定义数据结构、完成新功能那一刻,立即完善单元测试。因为这时候是对代码最熟悉、最清楚、也最愿意写单元测试的时候。错过这一刻,实在是太可惜了。

完善测试用例

和单元测试一样,测试用例不是在产品开发结果后补齐,而是在每开发完一个功能逻辑后,立即完善测试用例。道理一样,因为这个时候是对逻辑理解最透彻、清晰的时候,千万不能错过。

版本测试

单元测试跑一遍,心里就有底了。再加上前面完善的测试用例,版本测试无非就是照着用例跑一遍。

我并没有做自动化测试、持续集成,主要是涉及 UI 的自动化测试并不好做,而且投入很大。对于我这种小项目、独立开发者而言,手动跑跑测试用例,可能是性价比最高的。

值得介绍的一点是,推荐使用虚拟机进行版本测试。事先建立好干净的系统、生成镜像。每次测试时,只要将虚拟机恢复到指定的镜像即可。再者,就是可以在当前系统(如 macOS 10.12 )中,测试其它版本的系统(如 macOS 10.11 )

上架

自己辛苦做出来的东西,当然是希望越多人用户越好。要触到更多的用户,最直接有效的办法就是 上架 Mac App Store (MAS) 了。

内测、公测

在上架前,要保证自己的产品的可用、好用的,最好的办法就是,先让一部分用户用起来,也就是内测、公测(以下统一称公测)。

特别要强调的是,公测的目的并不是发现 Bug ,这是你自己要做好的事。公测主要有以下目的:

我的内测主要是在之前的用户群中 、 V2EX 中,在此特别感谢参与公测的朋友!

Mac App Store

好吧,终于来到 Mac App Store (MAS) 这个大坑。说 MAS 是大坑,主要是以下方面:

沙盒限制

沙盒会限制很多接口、系统权限的使用,对程序设计、产品交互有明显的影响。

比如,除非用户手动操作(及用操作本身来授权)沙盒不允许产品访问用户的文件系统。这就使用 iKindle 无法在一开始时直接自动导入当前连接的 Kindle 设备,而必需由用户手动操作。这就需要很恰当的引导。而一旦使用过一次,后续就不再需要用户手动操作。这也是为什么 iKindle 后续可以自动导入。

还有一个很关键的:即使在开发过程中打开了沙盒限制,苹果在审核时还是可能以沙盒为理由拒绝。虽说苹果准备了齐全的文档来说明这些限制,但一般开发者明显不能完全领会。就像你不可能读完所有法律以后再迈出家门第一步,一定碰到问题再解决问题更实际。好在,这次我没碰到这个问题。

名字

好吧,来说前文的坑。也许你会奇怪,为什么直到「沙盒限制」用的还是「 iKindle 」这个名字?是不是打错了?没错,确实是叫 iKindle.

但是,苹果不允许在 App 名字中出现 Kindle 这个字样。于是,只能改名了。这真是个痛苦的过程。一方面,想出一个好名字并不容易,我纠结了 N 久(具体可见我的博客),最终写了 Klib (Kindle library) 这个名字。

另一方面,所有的东西都是跟名字相关的,比如代码、 App 名称、文案、截图、等等。一旦名字改了,所有这些都要改。这是极其繁琐的事,我再也不想做第 2 次。

比如 Logo ,变成了下面的样子:

发布、推广

终于,在被拒了 2 次后, Klib 终于顽强上架 Mac App Store.

接下来的问题就是,怎么让更多人知道 Klib,也即,如何发布、推广?

Product Hunt

在这一环节,我最没有办法的,就是 如何在海外推广。毕竟咱们是中国人,很难打入敌人内部,更别说在外国产生影响力。

怎么办呢?我目前找到最直接有效的方法,就是在 Product Hunt 中有个成功的发布。这样国外的媒体也会在 Product Hunt 中找较好的产品进行报道。所以,Product Hunt 成了 Klib 走出国门、走向世界的关键

Product Hunt 的发布有很多可说的,这里挑几点重要的来说:

最终,Klib 在 Product Hunt 上获取 200+ 点赞, 10 几位国际用户参与讨论,基本满意。

用户群

Mac App Store 上的好评、留言,自然是非常重要的。而 最有可能给你好评的,就是你之前产品的用户。我一开始没有经营自己的用户群,现在回想起来深觉可惜。好在,现在慢慢有了 微信群Telegram 群也有了一些非常友善的用户,感谢你们!

媒体

媒体的爆光自然必不可少,他们的影响力是个人无法比拟的。最好是:

其他影响力

动用所有可以动用的影响力,让 Klib 触达更多的人。比如让朋友帮忙转发及好评,微博转发抽奖,朋友圈,等等。

其他

定价

定价是门玄学,太高没人买、太低自己不划算。目前 Klib 采用的是免费 + 内购(一次性买断)的方式。免费是为了方便更多的人使用 Klib 、降低门槛。虽然我很想继续尝试订阅模式,无奈用户对订阅始终接受程度不高,且 Klib 本身的属性也不太适合订阅模式,于是采取买断式。

趁这个机会,表达下个人观点:买断对开发者并不友好,订阅才符合实际。毕竟通常开发者需要对产品进行持续改进(这并不是说当初发布的产品有问题,而是产品本身有改进),却不能从改进中获得收益,这并不健康。伤害的最终可能还是用户自己的利益,因为开发者一旦坚持不下去而不更新,用户只能使用有待改进的版本。

定价目前是 ¥ 50 ,首发限时半价优惠,即¥ 25 ,入手即赚。

首发的意义

目前, Klib 是 macOS 平台、首款上架 Mac App Store 的 Kindle 笔记管理工具。这种唯一性是很有意义的。因为没有直接竞品,甚至有一定的定价权。

对用户而言,首发更容易使产品在这一品类中形成定位,后来者要想抢走在用户心智中的定位,就不容易了。

单日销量的意义

如果单日销量可以使 App 出现在排行榜的前面,会带来一些附加的流量,因为别的用户可能因为榜单的原因多看两眼。

首发第 2 天(第 1 天 App Store 更新太慢,没有数据),Klib 在中国区、 Utilities 这一品类中, Free 排名 22 、 Crossing 排名 14,算是不错的成绩了。

之后

目前, Klib 算是成功上架 Mac App Store ,也收到了很多用户的反馈。接下来,我会继续改进 Klib ,如增加多看支持、与 Amazon 中的笔记同步、与 Kindle 其他平台的笔记同步、导出至 Evernote 、等等。当然,我会非常谨慎地加功能,保持 Klib 的精简。

致谢

真的有很多人需要感谢…

最后

希望 Klib 能让大家多读点书、记点笔记,哪怕只是一点点改变,也是 Klib 的莫大荣幸。


博客原文

5695 次点击
所在节点    程序员
68 条回复
zhangxiao
2017-02-22 01:01:09 +08:00
好文,希望楼主带来更多作品。
quietjosen
2017-02-22 01:06:49 +08:00
@zhangxiao 客气,也欢迎转载、扩散。
araraloren
2017-02-22 08:45:07 +08:00
。。。我还以为是底层库或者什么开发库呢,这名字起的,感觉不怎么好
isCyan
2017-02-22 08:49:40 +08:00
没 Mac 好伤感
quietjosen
2017-02-22 09:03:19 +08:00
@araraloren 还真是有 Klib 这个库…

好在, Google 告诉我,这名字值得一试:

quietjosen
2017-02-22 09:03:34 +08:00
@isCyan 又是买 Klib 、送 Mac 系列…
akring
2017-02-22 09:26:01 +08:00
@quietjosen 赞一个,做好产品只是第一步,后续的版本迭代和客服(回邮件,回 Devmate 的 report 各种)才是万里长征,需要极大的热情和精力去维护,与君共勉
cqcn1991
2017-02-22 09:37:28 +08:00
刚在 PH 看到了 Klib ,然后想起在 V 站看到过,于是就找来了这里
LZ 加油。其实就想问下能挣多少钱,哈哈哈
lvwzhen
2017-02-22 09:50:26 +08:00
为独立开发者点赞,产品推广的经验值得学习。
frazy
2017-02-22 09:50:38 +08:00
学习了 ~~ 赞一个
jiaweiszu
2017-02-22 10:02:30 +08:00
赞一个,写得很棒
quietjosen
2017-02-22 10:22:50 +08:00
@akring 我已经有好几个产品了,深知维护所需要花的时间和精力,共勉。
quietjosen
2017-02-22 10:23:41 +08:00
@cqcn1991 哪天你看到我不再坚持独立开发了,就知道赚了多少钱……
quietjosen
2017-02-22 10:24:05 +08:00
@lvwzhen 谢谢,也可以帮忙扩散一下。
chipmuck
2017-02-22 10:39:33 +08:00
独立开发的路很难。从构思、设计、交互、体验、编码、测试、发布,都是一个人,像一个将军统领全局。自己也在开始做一个新的小工具,希望最后能得到收获吧!共勉!
quietjosen
2017-02-22 10:54:12 +08:00
@chipmuck 一看就是懂行的人,谢谢你,我会坚持的。
fds
2017-02-22 11:15:22 +08:00
支持!已付费。
lib 确实太程序员了些,可以考虑 Kinotes
quietjosen
2017-02-22 11:19:41 +08:00
@fds 因为我就是程序员……

谢谢建议。名字确实很纠结, Kinotes 确实在候选列表。最后是出于简洁考虑,选了 Klib 这个名字,任性一把吧。
dbdd
2017-02-22 11:26:27 +08:00
厉害,点个赞
incompatible
2017-02-22 11:27:33 +08:00
文章虽然写的很用心,但是槽点也够多的。
笔记 id 你用 hash 难道不怕冲突吗?不要用几率小做借口,只要几率不是 0 的事情就一定会发生的。你应该去研究一下 uuid 或者 sqllite 里的自增字段。
以及从云端同步时间 parser 难道不是很平常的设计吗。。不是很能理解你的公开自我陶醉。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/342204

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX