我用 AI 造 App(五):推翻 AI 的建议,自研一个 HealthKit 插件
系列第五篇。前四篇讲了立项、AI 分工、第一周的数据库、第二周的假数据和 AI 晨报(文末有链接)。这篇进入第三周:把假数据换成 iPhone 上的真实健康数据------以及一个重要转折:AI 调研推荐现成插件,我推翻了它,选择自己写。
这一周做成了什么
一句话:HealthKit 真实数据同步跑通 + 手动记录 UI 完成 + 种子用户内测准备就绪。
时间范围 Day 15--28(2026-07-29 至 2026-08-11),原计划估约 40 小时;进行中又挂了两项计划外任务------语音功能约 1.5 天、改登录约 0.5 天。当周净投入约 21 小时,AI 参与度约 70%。
AI 参与度为什么从上周的 80% 掉了十个百分点?后面账本一节细算。
一、两次"AI 给错了答案"
第三次周之前的路线,本来不是 iOS App。AI 调研的结论是:用 DCloud 市场的第三方插件,可以在微信小程序里读取健康数据。
这个结论有一个根本性错误:微信小程序根本不支持 HealthKit。 HealthKit 是 Apple 的私有 API,只在 iOS App 里可用;小程序跑在微信的容器里,拿不到这份数据。
发现这个错误的,是人不是 AI。AI 在调研插件市场时只看了功能描述,没有验证"微信小程序能否访问 HealthKit"这个前置条件。AI 擅长整理信息,但同样擅长相信信息------当你需要质疑一个前提假设时,AI 的置信度和一个新手的置信度是一样的。
这次推翻发生在第三周之前(就是前面说过的"三周变四周")。而 W3 开工第一天,轮到当周自己的决策:用现成的第三方插件,还是自己写?
AI 调研报告一开始推荐现成插件------一个叫 szy-healthkit 的 UTS 插件,MIT 许可、文档完善、当月还在更新,看上去完美。但调研往深处挖,结论反转了,改判理由三条:
- 市场上一部分 HealthKit 插件已经 1--2 年没更新,iOS 17+ 兼容性要自己验证
- 真机调试和版本兼容不够稳定
- HOP 实际只需要 MVP 级别的读取
调研报告把四个方案摆上桌面:
| 方案 | 说明 |
|---|---|
| A | szy-healthkit,免费 MIT |
| B | wrs-uts-health,商业插件、要私聊作者进交流群 |
| C | 原生插件旧模式,要购买且不支持离线打包 |
| D | 自研 UTS 插件(最终拍板) |
拍板理由:自研的维护可控性远高于第三方插件。 健康数据是这个 App 的命根子,第三方插件停更了只能干等;自己写的,随时自己改。
这里藏着一份"估时被执行打脸"的活教材:对比矩阵里方案 D 预估 5--8 天 ;任务表按 8 小时 记账;而 Cursor 实际在同一个日历日------调研完成的当天------交出了初稿:Swift 原生读取层、UTS 封装、entitlements、类型定义、业务适配层全齐。(准确表述是"同一日历日交初稿,账记 8 小时"------我一天只有两三个小时可干活。)
预估 5--8 天的活,8 小时干完了。你把任务书写清楚之后,AI 执行的下限会远超人类直觉。 这个项目的总账是:三次推翻 AI 的建议(小程序→iOS、第三方插件→自研、OpenRouter→SiliconFlow),合计约 8--10 小时决策成本。AI 擅长执行,不擅长决策------但有些成本,付了才买得到"可控"两个字。
二、一个人怎么"写"出原生插件
先破除迷信:自研插件 ≠ 我从零学会了 Swift。
插件住在 uni-app/src/uni_modules/health-agent-healthkit/,三明治结构:
bash
uni-app/src/uni_modules/health-agent-healthkit/
├── utssdk/app-ios/HealthKitBridge.swift ← Swift 原生读取层,直接调 Apple API
├── utssdk/app-ios/index.uts ← UTS 导出入口,把 Swift 包成 Promise 接口
├── utssdk/app-ios/UTS.entitlements ← HealthKit Capability 声明
├── utssdk/interface.uts ← 接口契约定义
└── utssdk/types.ts ← 类型定义
uni-app/src/lib/healthkit/index.ts ← 业务适配层,全 App 统一从这调
对业务代码来说,HealthKit 的全部复杂性被压缩成四个函数:
ts
import { isAvailable, authorize, fetchToday, syncTodayFromDevice } from '@/lib/healthkit'
isAvailable() // 是否可用(仅 iOS 真机为 true)
authorize() // 请求授权
fetchToday() // 读取今日数据
syncTodayFromDevice() // 读取并同步到 Supabase(推荐入口)
W3 结束时,fetchToday() 真正读回来的是:步数、主动消耗能量、锻炼分钟数、站立小时数、睡眠(深睡/REM/清醒分段)、静息心率 + 平均心率、所有运动记录------最后这项是惊喜,Apple 的 Workout API 支持 80 多种运动类型,一次查询全拿回,每条带时长、热量、距离(米和公里双字段)。
两个诚实脚注:
- 插件清单里写了"基础代谢",但 W3 当周并没有真正查询它------字段在接口里,数据没进去。连同 HRV、血氧、VO₂ 等,真正写入是第四周的事。拿现在的代码去对第三周,会对不上。
- 第二周建
workout_logs表时预留的"主观疲劳度/练后心情"字段------HealthKit 永远不会填这两个字段,它们只属于手动记录。 真数据住进来的,是时长、热量、距离和 Apple 的 workout_id。
还有一层设计值得抄:非 iOS 环境怎么办? 答案在适配层:#ifndef APP-PLUS 直接返回"不可用"。注意,插件层不会 自动落回 Mock------它只负责诚实地说"我不可用",要不要用假数据兜底,是晨报和首页层的事。一个插件只做一件事、说真话,降级策略交给上层。
三、同步:三张表,和一次权威的 ALTER
读到了数据,下一步送到云端。sync-healthkit 这个 Edge Function 把当日数据写进三张表:
- daily_summaries :upsert 步数/热量/心率等,冲突键
user_id, date,来源标记source: 'healthkit' - sleep_logs:按日 upsert 睡眠时长/深睡/REM
- workout_logs:每条运动一行
一个容易含糊的点:表是第一周建的,幂等键不是。 真正的权威变更是 supabase/migrations/20260806_w3_sync_healthkit.sql------给 workout_logs 加 workout_id 列和 UNIQUE(user_id, workout_id) 约束,给 sleep_logs 补 (user_id, date) 唯一约束。
两个新手坑:
坑一:睡眠是跨日的。 凌晨 1 点睡到 8 点,算哪天的睡眠?HOP 的窗口是昨日 18:00 → 今日 12:00,窗口内统一记到今日。不做处理,深夜的睡眠会被撕成两半。另一个现实:睡眠分段依赖 iOS 16+,而种子用户门槛是 iOS 14+------老系统上可能只有总时长。
坑二:必须幂等。 用户一天打开十次 App 就同步十次------没有 upsert 和唯一约束,表里会躺十条一模一样的跑步记录。幂等不是优化,是同步功能的底线。
顺带一个"影子 SQL"教训:施工稿里 AI 顺手另写了一套建表语句,还标注了"不要重复执行与现网冲突的 CREATE"。数据库结构变更只有一个权威来源,就是 migrations 目录。
W3 的同步形态要说清楚:以授权页手动刷新为主。ADR 里设想过的 onShow 自动触发、后来的半小时自动同步、跨日同步,都是第四周的事。先跑通,再优雅。
四、授权:Apple 设的一个"坑"
正常想象:用户点"拒绝授权",authorize() 返回 false,清清楚楚。实际:Apple 的读权限没有明确的 denied 状态------用户拒绝了,查询不会报错,只返回空数据。代码永远无法区分"用户拒绝了"还是"健康 App 里本来就没数据"。
这是隐私优先的设计哲学,但对开发者是实打实的坑。应对方案就一句:"读权限无明确 denied,以能否读到数据为准"------不做无谓的状态推断,读到了就用,读不到就回退。
前端永远留一条"稍后授权"的路,不逼授权。真机调试还有环境坑:HBuilderX 必须装 App 版(dev:h5 验证不了 HealthKit),要走"自定义基座 → 真机";测试机的"健康"App 里得有历史数据 ,否则读出来全是 0------不是 Bug,是你真的没走几步。还有一条踩实了的经验:自定义基座用开发证书,TestFlight 用发布证书,两套证书搞混,打包环节能折腾掉你一晚上。
五、一天三连变更:2026-08-07
第三周进行到一半,三件事同一天记录在案:
登录改为邮箱 + 密码。 手机号验证码退场------短信通道的成本与审核,对十几人的内测是高射炮打蚊子。
AI 助手长了嘴和耳朵(计划外加分)。 语音输入:按住录音 → SiliconFlow 的 SenseVoice 转写 → 填入输入框。语音播报:CosyVoice2 合成 → 音频存 Storage 的 tts-cache 桶 → 前端拿签名 URL 播放。工程巧思是合成结果做缓存------同一段回复播第二遍直接取缓存,不再花一次合成费用。
种子分发定为 TestFlight。 用已有的 Apple Developer 付费账号。两条歧路排掉:小程序体验码(测不了 HealthKit)、自定义调试基座(只有开发者本机能装)。
"计划是地图,用户反馈和现实条件才是方向盘。"和"三周变四周"的被迫返工不同,这次是有准备的变更------每条都有原因、代码位置、兼容说明,落进文档,随时可查。
六、真数据的备胎:手动记录与 Mock 回退
真数据有缝隙:手表没戴、手机忘桌上、"健康"App 本来就是空的。用户练了一小时力量手表没记上,这堂课也要算数------所以做了手动记录 UI。
手动数据和自动数据怎么共存?答案比"合并成完整图景"现实:按日摘要和睡眠是覆盖,运动是按条去重追加。 sleep_logs 按日 upsert------同一天先手填睡眠、又触发同步,同步会整行盖掉手填的,不是两条拼在一起。运动记录可以并存:HealthKit 记录靠 workout_id 去重,手动记录的 workout_id 是空(Postgres 允许多个 NULL),各记各的、一起展示。设计里没有魔法,只有约束------先想清楚谁覆盖谁、谁和谁并存,再让 AI 写代码。
Mock 回退的口径变化更有意思:任务稿最早写"回退时用户无感知"------静默兜底;上线时的实际产品是首页必须有来源徽标:真实 / 模拟 / 混合。计划阶段想过偷偷用假数据糊弄过去,做的时候自己推翻了自己:宁可告诉用户"这段简报基于模拟数据",也不冒充真实。
七、第一批用户:种子内测,和一张空表
目标人群:5 人以内、必须持有 iPhone(iOS 14+)。筛选标准里有条值得抄:"不是核心熟人"------避免人情干扰反馈的真实性。 太熟的朋友不忍心说难听话,而内测要的恰恰是难听话。名单建议:1--2 名有运动习惯的、1--2 名关注健康但不怎么运动的、1 名有产品或技术背景的------各取所需,各挑各的毛病。
然后是这篇必须诚实交代的结局:第三周结束时,我自己的 TestFlight + 真机路径完全跑通;但正式的种子用户反馈,没有归档------反馈追踪表至今仍是空模板。
为什么会空?复盘下来,第三周的重心不可避免地倒向了工程侧------插件推翻、中期变更、真机调试,每一件都在抢时间;到分发阶段,人已经疲了。单人项目的经典陷阱:开发和运营用的是同一份精力,开发永远有下一个 Bug,运营永远可以"明天再说"。
八、验收与账本:一笔不能算错的账
官方 P0 清单是八项------工程六项全过(数据同步真机通过、Mock 回退、手动记录 UI、晨报反馈机制、邮箱登录、语音);种子运营两项(多人体验、反馈报告)为空。
账本要摆清楚,因为几个数字放一起会互相打架:
- 原计划估约 40h;中期任务表挂了语音约 1.5 天、改登录约 0.5 天
- 回顾记账:整周净投入约 21h,分项是调研 3 + 插件 8 + 数据同步 3 + 授权 UI 2 + 运动类型 2 + 内测 SOP 3
发现问题了吗?分项里没有单列语音、改登录、手动记录、Mock 回退 ------这些计划外的活,被揉进 21 小时里了。而且"1.5 天"是任务表的写法,我晚上干活,一个"天"不等于八个工时。所以这笔账不能 读成"计划 40 + 计划外 16,只干了 21"------估时和记账是两套口径,OPC 的账本永远以净投入分项为准。
AI 参与度 70% 的归因也不能偷懒:插件那 8 小时依然是 AI 主力;把人的占比抬上来的,是调研核实、真机证书、HBuilderX、自定义基座 这些必须人手把手干的活。三次推翻 8--10 小时是全项目总账,不是这一周的。W3 的真实分工变化是:从"AI 写、我审"变成"AI 写、我审、我还要去按真机上的按钮"。
三周累计:5 周、净投入约 46 小时、AI 平均参与约 73%。一个人的五周,加上 AI,做出了一个能装进别人手机里的产品。
第三周结束,App 第一次吃上了真粮------AI 说的每句话背后,是真实的步数、真实的睡眠、真实的心跳。
但真数据一进来,新问题跟着进来:数据太干净时是故事,数据连续、杂乱、有缺口时,才是生活。第四周要面对的就是生活本身:恢复分算法整个重做、自动同步补上、五 Tab 登场。至于那张空着的反馈表------第四周也没顾上填它,因为真数据暴露出来的产品问题,比问卷更急。
系列前篇:
- 我用 AI 造 App(一):一个不会写代码的人,决定造一个健康 App
- 我用 AI 造 App(二):三个 AI 员工和它们的分工
- 我用 AI 造 App(三):7 张表、RLS 双保险和登录
- 我用 AI 造 App(四):先造假数据------零基础的 AI 功能周
文中 HOP = Health On Palm,作者的个人健康助手项目。所有设计、SQL、Prompt 均来自项目真实执行记录。