最近搞了个新的 side project:「听城 (City Whisper)」,一个位置触发的城市故事 App。你散步、通勤、开车路过某个有故事的地方,手机轻轻弹一条通知,告诉你眼前这个东西是什么来头、为什么长这样、为什么在这里。TestFlight link,欢迎试用,正式上架审核流程实在太慢了还没过审…
从 6 月 9 号晚上开工到现在一个月出头:206 个 commit,五个 TestFlight 版本,九个城市包,App Store 上架材料也备齐了。全程就我一个人,加上 Claude 和 Codex 两个 AI 同事。这个系列想把这一个月的开发过程和心路记下来,拆成几篇慢慢写。这是第一篇:缘起,和第一个晚上。

现在的听城会把一处地点整理成这样的故事卡;第一晚完成的,是这套体验最早的骨架。
缘起:两个想去的地方
这个想法其实在我的 Obsidian 项目计划里躺了挺久,当时叫「城市旁白」。最早的灵感来自我学长鸭哥的 Guide Me the City。我一直挺爱看这种城市冷知识、小故事类的东西,每天路过的街角、桥、旧厂房,为什么会在这里、曾经发生过什么,这些我都很好奇。但鸭哥那个是网页形态,一个网页放在手机上实时监控 GPS 位置,这事儿不太好做;他主业在 AI 学习社区,也没精力专门往这个方向做。所以我就想,可以借鉴这个 idea,按我自己的审美和 taste,做一个我自己想用的原生 App。
真正让它从笔记里爬出来的,是两趟旅行。
一趟是我和我老公计划去芝加哥玩。我们是自由行,也没请导游,走到哪儿看到什么奇怪的东西,经常就是一脸问号:这玩意儿为什么长这样?为什么在这里?如果有一个 App,我走到那里它自动弹出来,告诉我眼前这个东西为什么是这个样子,那不就正好。
另一趟是我爸妈报了旅行团要去伦敦。退休的人,对英文 App、英文生态都不太熟,我就想顺便给他们也装一个,用中文告诉他们眼前的东西是什么。后来内容库里那个 London 城市包,最早就是给爸妈的伦敦之行准备的。顺带一提,我爸妈的手机是安卓。记住这个细节,它马上就要影响技术选型了。
翻了下记录,当时导入给 AI 的原始需求就一行英文,现在还留在项目根目录的 AGENTS.md 里:
Geolocation notification when go to somewhere famous or have a special story
整个项目就是从这一行字长出来的。
导火索:Fable 5 发布那天
不过想法躺在笔记里的时间远比长出来的时间多。真正的导火索,文档里反而没记:6 月 9 号前后,Claude 的新模型 Fable 5 刚公开。我就想试一下它到底能力如何,有没有传的那么厉害。结果试了一下,确实还挺厉害的,当天我就在疯狂地使用 Fable 5。听城的原型,就是 Fable 5 做出来的。
当晚给我的震撼在于:它知道这个 App 应该怎么写。不光是代码,它弄出来的产品设计,在美学上我觉得都挺不错的。基本上一次就能写出一个看得过去的原型,地图是它一开始就写好的,交互像模像样,只是 marker 聚合还没做。很多细节当然还得打磨,但是能一遍跑出来一个能哄住人的东西,我觉得还挺聪明的。「新模型发布 → 找个搁置已久的想法练手 → 练着练着变成真项目」,我猜这可能是 2026 年不少人的共同经历。

这是后来打磨出的分享卡;暖白、朱红与线稿的视觉方向,最早就在第一晚的原型里定下了。
选型:一套代码,两代人的手机
先坦白:我其实不算 iOS 新手。考古的话可以挖出两个文物级项目:
- MCdemo,纯 Objective-C 写的 iPhone 蓝牙聊天 demo,我大三的时候某门课的大作业
- 还有 2018 年在usc的课程小作业 csci571,其中 hw9 是用 Swift 4 写的「Travel and Entertainment Search」iOS 版:搜地点、看地图、看评论。
对,你没看错,八年前我的作业就是地点 App,八年后我又做了一个地点 App。区别是,当年完全没有 AI,写一个 iOS App 真的是翻遍网上各种教程,遇到问题就 Stack Overflow、CSDN 各种问答社区轮着刷,GitHub翻遍了类似的项目以及热心前辈们的作业repo,从第一行 code 到实际跑在 iPhone 上并且没有 bug,得费好大的劲。而这次,一个晚上。我是真觉得赶上了一个好时代。(18~22几年间我的这个作业repo每当临近期末便浏览量飙升,有AI了之后大家都不点进来抄了,笑死)
所以这次选型的问题不是「会不会写」,而是「现在有 AI 了,有没有更快的方法」。开工当晚我丢给 AI 的原话是这样的:
我现在不清楚 iOS 开发是不是必须安装 Xcode 以及模拟器,这些太重量级了,有没有轻量级一点的选型?比如写一个 electron app,然后跑在手机上?有没有 CLI 可以用?我主要还是会使用 AI coding agent 开发
现在回头看,「我主要会使用 AI coding agent 开发」这句话,大概是我第一次把它当成选型标准写出来。agent 友好度(CLI 可驱动、纯文本配置、不依赖重量级 IDE)从此成了我技术选型的一等公民。
另一个硬约束前面说了:我爸妈的手机是安卓。所以我想要跨平台,同一套代码逻辑 build 两个平台的 App。这不是技术偏好,是「用户名单」决定的:我自己的 iPhone,加爸妈的安卓机。
调研下来的结论是 Expo + React Native + TypeScript。npx expo run:ios --device 直接装真机,免费 Apple ID 个人签名就行(7 天过期,重新 run 一次的事)。我还专门追问了为什么 EAS 云构建要 paid developer account,搞清楚了云构建和本地构建的边界。
调研完我说的最后一句话是:「OK,那你帮我把调研到的这些东西,更新到我的文档里面。」从第一天起就让 AI 把结论沉淀回文档,这个习惯后来成了整个项目的工作流地基,以后单独写一篇展开。
6 月 9 日晚上的时间线
这个晚上值得用时间线记一下(时间戳都是从当晚的 session 记录里挖出来的):
- 20:19:选型调研开始。Expo vs Electron vs 原生,上面那段原话就是这时候说的。
- 23:18:新 session 的第一句话已经是:“这是我做的新项目,你可以阅读一下 docs,然后目前我们已经成功编译并 run 在我的 iPhone 上面了,但是运行时报错了,帮我调试一下。“三个小时内,文档、脚手架、编译、装机全走完了。
- 当晚继续:真机跑通之后我立刻开始挑刺,第一批反馈现在看全是产品直觉:收藏按钮从圆形 placeholder 改成五角星;打开 App 初始地图要定位到我当前的位置;「已读」和「已去过」要区分开;还有一条:“详细介绍的文字中,逗号是半角的,对于中文来说有点奇怪”。
- 23:32:另开一个 session 设计 App 图标:“设计一个平面构成简约大气的图标”。第一版被我毙了,理由是看不出这是个什么 app;第二版改成前景 marker 加对话框、背景抽象城市天际线;第三版我的评语是"目前的这个看起来像是统计图在说话”,让它把天际线画得更像现代大城市一点,比如加个太空针的轮廓。三轮才过。AI 干活是真快,但审美这关还是得人来把。
- 次日 01:51:当晚最后一个 session 开工。最后是把 Fable 5 当天那个五小时的用量窗口整个用完了才去睡的,模型的 rate limit 成了那晚的下班铃。

从左到右是 session 里留存的五版草稿:审美反馈让它从抽象符号一路收敛到可辨认的城市天际线。
第二天早上醒来,手机里装着一个能跑的听城。Obsidian 里躺了几个月的一行字,一个晚上变成了 App。
很快就踩到的坑
当然也不是全程丝滑,有几个坑值得记一笔,说不定能帮到别人。
不要在 iCloud(或任何同步盘)目录里做 iOS 真机构建。 项目最初放在同步盘管理的目录里,后来跑构建经常卡死在 Planning build。排查下来的原因是:Xcode 读 .xcodeproj 会走 macOS 的 NSFileCoordinator,同步盘的文件元数据能让 xcodebuild -list 都直接挂起。解法很朴素:真机构建一律搬到 ~/Developer/ 这种普通本地目录做。项目 README 里现在还留着那条 rsync 迁移命令,纪念这次搬家。
出门实测要用 field build。 日常开发用 dev client + Metro 热更新;出门实测 geofence 的时候,得用 npm run ios:field 把 JS bundle 直接打进 App,不依赖电脑。这个区分是被现实教育出来的:第一次出门测试,App 冷启动后只给我一个 Expo dev client 的启动界面,故事和地图都还在家里电脑上的 Metro server 里呢。
安卓测试机是门玄学。 跨平台承诺总得在真的安卓机上验证。测试机是我老公贡献的一台旧 OnePlus 6,选它不是因为情怀,而是因为这台国产旧手机上刚好还带完整的 Google 套件,Google Maps 能直接跑起来;2021 年之后的很多国产机没有 GMS,地图直接白屏,连 smoke test 都做不了。后来安卓侧用本地 Gradle 出 APK,在这台老将上一次通过,业务代码全跨平台,算是把「一套代码,两代人的手机」这个选型给验证了。
第一条路过提醒
MVP 装上手机的第三四天,我收到了第一条真实的路过提醒。地点是「东区的世界客厅」,也就是 Crossroads Bellevue,一个美食广场像联合国食堂的购物中心。选它没什么浪漫理由:离我家最近,拿来当测试点最方便。

第一条真实提醒发生在 Crossroads Bellevue;卡片上的「实地到访」标记,也成了这次路测留下的产品痕迹。
调试日志里正好留着那个时刻:6/12 19:44,“东区的世界客厅 ✓ 已提醒”,紧跟着下一条是"✗ 距上次提醒太近”。第一条通知和频控生效,同框出现在日志里,工程师看了会心一笑。

19:44 的「✓ 已提醒」和紧随其后的「✗ 距上次提醒太近」,第一次同时验证了通知触发与频控。
当然,兴奋劲儿还没过就撞上了现实:GPS 定位不太准,购物中心是室内,GPS 偏移得厉害,还有「人明明到了圈里但就是不触发」的情况。当时一口气发现好几个 bug,修了好久,这些坑够写一整篇,之后单独展开。
这里让我老公出场一下。我说要开车出去实测 GPS 触发,他说:“为什么要开车呀,你弄一个 GPS 模拟器,叫 AI 帮你测一下不就行了吗?“听起来很有道理,但实际出去测就会发现,GPS 偏移、驾驶模式和步行模式该怎么切、通知弹出来的时机对不对,这些体验问题用模拟器在电脑上是 debug 不出来的。我觉得他的建议有时候就是太过于 technical 了。

开发首夜只有 3 条日记记录;四天后累计到了 110 条,MVP 从装上手机的那天起就在默默工作。
翻 App 里的日志页,6 月 9 号,也就是开发的第一晚,它就已经默默记下了 3 条log。四天之后,这个数字是 110。它从出生那天就开始工作了。
接下来
MVP 跑起来只是开始。后面一个月发生的事比第一晚精彩得多,也曲折得多:产品定位推翻重来了一次,和 iOS 的定位机制搏斗了很久,还给项目配了一条内容流水线和几个机器人同事。这些都值得单独写,慢慢更新。
下一篇先聊产品:为什么听城坚决不做导游 App。