起因
去年我爸确诊高血压,医生让每天记录血压。试了几个 App,要么界面花哨操作繁琐,要么强制订阅一年好几百。我爸一个六十多岁的人,打开 App 看到数字键盘就烦躁,经常忘记记。
所以我自己写了一个。叫「健康手账」,iOS 原生,SwiftData 存储,核心卖点就一个------录入快,3 秒搞定一次血压记录。
拨轮交互这事儿
说白了就是把数字键盘干掉了。血压值的范围其实很固定,收缩压大概 80-200,舒张压 40-130。用拨轮比用键盘快得多,手指一拨就到位,还带物理阻尼感的触觉反馈。
我观察我爸用手机的习惯:他对「滑动」这个动作很熟练(刷短视频嘛),但对精确点击小按钮很抗拒。拨轮正好契合这个习惯。
实际测下来,从打开 App 到完成一次血压录入,大概 10 秒。如果加上 Siri Shortcuts,连打开都省了:
swift
struct LogHealthRecordIntent: AppIntent {
static let title: LocalizedStringResource = "记录健康数据"
static let openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult {
try await Task.sleep(for: .milliseconds(200))
NotificationCenter.default.post(name: .healthLogShowInputSheet, object: nil)
return .result()
}
}
对着手机说一句"用健康手账记录血压",直接弹出录入界面。我爸现在用得还挺顺。
数据模型的取舍
一开始我把血压、体重、血糖拆成不同的 Model。后来发现这样查询和展示都麻烦,改成了单一 HealthRecord,用可选字段区分类型:
swift
@Model
final class HealthRecord {
var id: UUID
var timestamp: Date
var systolic: Int?
var diastolic: Int?
var pulse: Int?
var weight: Double?
var note: String?
var tagIDs: [String]
var profileID: String = "default"
}
systolic 有值就是血压记录,weight 有值就是体重记录。简单粗暴,但查询的时候一个 predicate 就能过滤,趋势图渲染也方便。
有人可能觉得这样不够"规范",但对一个独立 App 来说,少建一张表就少一堆迁移问题。SwiftData 的 schema migration 说实话踩过几个坑,能简单就简单。
状态印章------把干预行为和数据关联
这个功能我觉得挺有意思。用户记录血压的时候可以顺手打一个"印章",比如今天吃了降压药、做了运动、喝了酒。
底层是一个 StatusTag 模型,默认预设了降压药、运动、好睡眠这些,用户也能自己加。每条 HealthRecord 通过 tagIDs 数组关联。
这样在看趋势图的时候,能直观看到"吃药那几天血压稳定,停药这两天又上去了"。我带我爸看诊的时候,医生看到这个关联还挺高兴,说比口头描述"好像最近有吃药"靠谱多了。
PDF 就医报告
这个功能是被逼出来的。每次去医院,医生问"最近血压怎么样",我爸掏出手机给医生看,医生根本没时间翻你的 App。
所以做了一键导出 PDF 报告:选个时间范围,生成一份带折线图和数据表格的单页 PDF,打印出来递给医生就行。格式参考了几份正规的血压监测报告模板。
说实话这个功能开发成本不高,用 UIGraphicsPDFRenderer 画就行。但用户价值挺大,同类免费 App 里基本没人做这个。
多人档案
profileID 这个字段就是为了支持家庭场景。我妈也有高血压,我不想装两个 App 或者切账号。现在一个 App 里建两个档案,数据完全隔离,趋势图和报告都是独立的。
实现上就是所有查询都带 profileID 过滤,Widget 和提醒也跟着当前选中的档案走。
商业模式
买断制,不搞订阅。慢病管理这种东西用户可能要用好几年,订阅制对他们来说心理负担太大。Pro 版解锁多人档案和 PDF 导出,基础的单人记录和趋势图免费用。
目前还在冷启动阶段,下载量很小。但我自己每天都在用,我爸妈也在用,这个 App 对我来说已经有价值了。
一些技术细节的坑
- SwiftData 的
cloudKitDatabase: .automatic在用户 iCloud 没登录时会静默失败,我加了个手动开关让用户自己决定要不要同步 - WidgetKit 读不了 SwiftData 的数据库,我单独写了个
WidgetDataStore用 App Group 的 UserDefaults 传数据 @Observable配合 SwiftData 的@Model混用时偶尔有刷新时序问题,最后把设置类和数据类彻底分开了
回头看
这个项目从立项到上架大概花了六周的业余时间。对我来说最大的收获是:给真实的人(我爸)解决真实的问题,比做一个"有市场前景"的东西有动力得多。每次看到我爸晚上测完血压顺手拨两下就记好了,比任何下载数据都让我开心。
如果你也在做健康类或者工具类的独立 App,欢迎评论区聊聊你们的录入交互方案,我对这块挺感兴趣的。