在我发布的第一个介绍Cent 记账的视频下面,问得最多的一个问题是,在哪里可以下载呢?没办法,Cent 作为一个 PWA ,本来是完全不需要下载的,但是如今的应用生态中,Web 天生低人一等,各家手机厂商防贼一样防着浏览器,如果不出一个详细教程,初次了解的人根本学不会怎么把一个 PWA 安装到桌面上。因此,我决定做一个 iOS 原生版 Cent 。
至于为什么是 iOS ,原因也很简单,几年前我就开通了 Apple Develop Program ,本来打算做一些小玩具练手,但因为懒,也一直没有动手,白白花了好几年的订阅费用。对比 Android ,iOS 生态的好处显而易见,完善的全球上架流程,一致的硬件架构,不需要考虑各种适配问题,比起国内安卓市场那堪比规则怪谈的上架机制,不选 iOS 简直天理难容。
我从一开始就决定完全通过 Vibe Coding 来进行开发,因为如果要让我从头开始学习一门其他语言开发,了解那些犄角旮旯里的不知名 API ,对于如今改个样式都恨不得让 AI 来做的我来说,几乎是不可能的。一旦尝过了 AI 的甜头,要想回归手写代码的时代,那就太难了。
最难的第一步
对于 Vibe Coding ,最难的一步不是想做什么,而是决定做哪些,怎么做。对我而言,最大的难题是如何选型。一方面,尽管我已经决定了要做 iOS 端,但是也不意味着完全放弃 Android 端。另一方面,对 AI 而言,代码技术也是有熟悉程度的,JS 和 Python 对于 AI 是小菜一碟,但其他领域就不一定了。为此,我跟 Gemini 掰扯讨论了很久。
一般我会先用网页版 Gemini 沟通自己的想法,我认为 Gemini 的最大优势在于搜集信息,背靠 Google 这个搜索引擎,找它问问题是十分自然的事情,关键是 flash 量大管饱,对于要求不高的任务,直接问 Gemini 比 AI Agent 省事得多,不然一言不合就开始“让我先了解项目结构”,白花花的 token 就这样扔给了文件系统,造孽啊。
这一次找 Gemini 问完,它推荐了我一个此前我从未听说过的开发技术,KMP(Kotlin Multiplatform),根据介绍,它可以将核心操作逻辑复用一套 Kotlin 代码,通过不同的 UI 技术适配不同的平台,在 Android 上用 Compose ,在 iOS 上用 Swift UI ,简直是我的梦中情码,精准命中了我的所有需求。于是我当即决定用 KMP 进行开发。
然而,我完全低估了 KMP ,它就像一个禁忌之海,海面上是诱人的阳光和云朵,海风吹着跨平台的美梦在你的耳边低语,但是你一旦踏入,臃肿、沉重、深不见底的 Gradle/Maven 平台就会像漩涡般瞬间把你的心智吸入,IDE 上不计其数的按钮,讳莫如深的术语,就像克苏鲁身上不可名状的眼睛一样,让你的 san 值直接清空。一想到我还试图在 Gemini 调查员的帮助下深入这座散发着诡异黄光,飘洒着 warning/loading 碎屑的古神小镇,我就止不住的后怕。
当我发现,无论是 Gradle 古神低语般的配置语法,还是诡异的平台适配装饰器代码,以及深不见底的项目文件夹目录结构,我都完全无法理解的时候,我仅剩的理智拉住了我。Vibe Coding 本身就已经很黑盒了,如果再接入一套黑盒(对我而言)的开发架构,最终开发的产物简直不敢想象。因此,即使那句大名鼎鼎的“忘掉代码,只看结果”的 Vibe Coding 准则一直挂在我的心里,但我还是没有勇气完全对自己“产品”的代码不管不顾,最终,我放弃了 KMP 。
我决定先专注于 iOS 一个平台,此前我了解过 Swift UI ,尽管不多,但我相信我能在关键时刻拉下刹车,至少不至于在 plan mode 时对产出的方案一头雾水,然后无脑选择 Accept all 。虽然这不符合纯粹 Vibe Coding 的定义,但事实证明后来我的选择是正确的。
渐进式开发
Cent 虽然是一个纯粹的 Web SPA ,用的都是 Web 开发中十分基础的技术,React ,Zustand ,没有什么黑科技,但是要想一句话让 AI 照着 Cent 原模原样复制一个 iOS 版出来,以现在的 AI 能力还做不到,因此必须拆成一系列小步骤,一步一步来。
得益于 Cent Web 版本身解耦做的比较充分,我拆起任务来也比较得心应手。Cent 的核心同步机制,我称之为 Tidal ,是一个纯粹的数据操作,不绑定任何特定数据库实现,因此,我首先让 Claude 帮我对 Tidal 进行迁移,先按照 Tidal 的机制做一个小 demo ,能够成功执行增删查改即可。
当然,为了让 Claude 更好的理解 Tidal 的同步机制,我不得不为整个 Tidal 代码补上完整的注释和文档,详细说明每一个步骤,每一个函数抽象出来的意义,本来这部分应该在 Tidal 诞生时就做完的,但是因为懒一直拖着,也算是为之前的工作收尾了。对于 Claude 来说,有原始代码和文档,迁移到其他语言非常简单,几乎只用了一个下午,我就得到了可用的原型。
为什么不让 Claude 自己来拆解任务和文档?我曾经试过让 Claude 自己了解 Tidal 相关的实现,但是最终效果并不好,在代码中有一些 edge case ,需要特殊处理,以及部分兼容性代码,在新的代码中已经不需要了,让 Claude 自己分析并不能很好地识别出这些代码意义,还是需要我手动补充文档,这样下来不仅耗费了更多的 token ,我干的活也一点没少,后来我还是选择老老实实补全文档,把每一个场景都写清楚,最终得到的结果也不负我的期待,并且,这些文档还可以用于指导后续其他平台的移植,磨刀不误砍柴工嘛。
抽卡成功
就这样,从核心同步机制开始,我一点点地把 Cent 的全部功能和 Claude 一起搬到 iOS 上,记账页面,搜索页面,统计页面,设置页面,iOS 的功能一点点补齐了。但是在测试过程中,我也发现了一个十分严重的问题,在账单数据较少时,App 运行一切正常,但是当我用真实的账本(约 1w 条)测试时,App 突然变得十分卡顿,使用原生的代码运行,却比 Web 端性能更差,令我十分意外,于是我不得不再一次违反不要看代码的“准则”,看了一眼当前的代码,两眼一黑。
我知道,Claude 偷懒了,它一直在用写 Demo 的思路写后续的功能,所有的数据变动全往一个 appState 里塞,几个页面的数据变动全部耦合在一起,虽然我不太懂 Swift UI 的状态管理,但也能意识到出了大问题。尽管之前我有意识地让它多考虑性能,多考虑耦合度,但是积重难返,可能是某次方案里我没怎么细看,直接说了句开干,后面的代码就像脱缰野马一样,撒欢跑的谁也不认识了。
我让 Claude 自己分析性能问题的原因,它也老实说了,核心状态管理七八百行代码,纯纯屎山代码,各个页面直接本来没有任何关系,但是数据更新全放在一个 class 里,每次干点什么操作不知道哪里触发了更新,就要反序列化 1w 多条数据,不卡才有鬼了。这下我不放心让 Claude 自己改了,找来隔壁的 Codex ,请 GPT5.5 喝了杯茶,看它洋洋洒洒列出一大摞优化建议,说的有鼻子有眼的,虽然我依旧不懂这样做能不能治本,但是没关系,反正有 Git 回档大法,这卡先抽了再说。GPT5.5 十分给力,将近 50 个文件几百次改动,还写了个脚本用来批量替换,吭哧吭哧干了一下午,我都觉得心疼,总算是改完了。最终测试结果不负我望,性能问题消失了,虽然没有经过严格的测试,但是同样的账单数据下,卡顿彻底消失了,我总算松了口气。
没有 Harness 就是最好的 Harness
Harness 的概念最近很火,名词一套接一套的,实际上就是试图给 AI 定个框架,让它在这个框架里干活,避免搞出问题来。因此我也在想,如果一开始我也能把“Harness”做好,Claude 是不是就不会整出这么大的幺蛾子来呢?但马上我就放弃了,对我来说,Swift 开发本来就不是我熟悉的领域,让我给 Claude 定规则,就像关公面前耍大刀,自不量力。
整个项目里说得上 Harness 的东西,就是一开始我给 Claude 制定的“记忆”守则,我在创建项目的时候就告诉 Claude ,我的目标是把整个 Cent Web 项目完整地移植到 Swift 中,这是一个十分巨大的目标,因此必须要一步一步来,Claude 需要制定一个精准的 README ,然后每一次移植都把这次做了些什么写进 memory.md 中,非常古老且粗糙的做法,但对我来说足够了。每次对话前,我就把 README 先塞进聊天框中,Claude 就会按照之前的做法完善 memory ,如果有比较重大且成体系的改动,例如多语言适配方案、快捷指令之类的,我就让它重新编写一份单独的指导文档。这样的好处是非常省 token ,很多时候一些小功能改进、UI 修改等,没有必要让 Claude 去读完整个项目的架构文件,只需要专注于一个或者几个文件即可,塞入太多东西到 claude.md 中不仅昂贵,还会拖慢 coding 的速度,没有必要,按需读取才是兼顾效率和划算的选择。
找回乐趣
不得不说,Vibe Coding 真的会让人上瘾,尤其是当成果慢慢变成你心目中的样子的时候,成就感会一点点放大,本来自己手写代码也能达到一样的效果,但是 Vibe Coding 把这个时间压缩了几十倍。放在两年前,要将一个 Web App 全部转成纯血 Swift UI ,让我自己来干,学习的时间不说,光敲代码就要消耗了不少时间,还要费劲心力去了解各个 API 的用法,各种新的“最佳实践”,我还记得之前写试手 app 的时候,为了实现一个 UI 效果,在 Google 、Youtube 、B 站上来回搜索,对着视频敲代码,现在只要说句话,说清楚一点,就能实现自己想要的效果,效率高了不止一点。一次不满意还可以重抽,只要是能够实现的,总能抽出来满意的结果,Vibe Coding 像抽卡果然名不虚传。
哪怕是等待 Claude 编写代码的简短空隙里,我都抑制不住脑子里疯狂涌出的奇形怪状的 idea ,恨不得挨个让它给我实现一遍,连喝口水上个厕所的时间都在想着,怎么让 Claude 再多干点活,整个人已经完全变成黑心老板的形状了。
那么,代价是什么
耗时将近两个月,我终于完成了整个 iOS Cent App 的开发和上架,准确的说,是在 Claude Opus 4.7 + Chat GPT 5.5 + Cursor Compser 2.5 的帮助下完成了开发,除了代码开发之外,我还用 Claude 帮忙编写了 Cent iOS 发布后的一个简易宣传视频,现在在 B 站搜索 Cent iOS 版看到的视频就是由 Claude 编写而成,我只负责寻找 BGM 和录制屏幕。
而在这场看起来光鲜亮丽,轻松写意的 Vibe Coding 背后,是 Token 在不断燃烧,我做了粗略的统计,仅仅只计算我的真实支出项目,Cursor + Claude Pro + ChatGPT Pro (全部用光每月额度,Cursor 甚至连 auto 也用到不能再用了),合计订阅费用就已经来到了$400 ,还没有算各种薅羊毛、注册新用户拿免费 Kiro/中转站月订阅省下来的钱,在这样的情况下,即使不算 App Store 开发者$99 年费和抽成,按照目前的 Cent iOS 版定价,也需要至少卖出 100 份才能勉强回本。从上架后的情况来看,达成这个最低目标都算遥遥无期。
这划算吗,我觉得还行,尽管从投入产出比来看,Vibe Coding 的经济效益非常低,但是它给了我一次从无到有的 App 上架体验,我第一次成功上架并且卖出了我的第一个作品,这对我意义重大,而且只花了两个月的时间。对于任何产品而言,难的都不是做出来,而是卖出去,在没有任何营销,单纯靠自己卖吆喝的情况下,能卖出去我就已经感到非常惊喜了。
Cent 在诞生之初就是个人特色十分强烈的作品,有很多地方刻意不去迎合市面上的主流记账软件,例如资产管理等等,因此在 Github 收获超千 k star 的时候,我都感觉十分意外,决定开发 iOS 版,也是为了证明 Tidal 的潜力,不想让这套同步机制埋没在浏览器限制之中。在决定 Vibe Coding 前,我也曾对 AI 的能力边界有过高过低的期待,一方面作为程序猿,我担心 AI 会抢走我的饭碗,另一方面我也好奇 AI 究竟能做到何种程度。但是实际体验下来,尽管我确实没有手写哪怕任何一行代码,但我开始对自己有了更多的信心。
AI 绝不是万能的,如果只给一句话,让 AI 直接复刻一个完整的 iOS 版本出来,它是绝对做不到的,或者说绝无可能做到现在这样的程度,Vibe Coding ,更像是 Vibe Deciding ,Coding 反而是最不重要的一环,重要的是挑选、验证,要做什么,不做什么,等着 AI 问你这样做行不行,如果有一天 AI 真的到了一句话就能抽出完美的结果的地步,那这绝对不是一件坏事,在这之前,至少我认为我自己,还不至于完全被取代,如果 AI 有一天真的消灭了工作,那才是大好事呢。