起因
去年我爸确诊高血压,医生让他每天记录血压。我给他试了七八个 App,发现一个共同问题:老年人面对数字键盘输入三组数字(收缩压、舒张压、脉搏),平均要 30 秒以上,还经常按错。
我爸用了三天就不记了。说"太麻烦"。
这事儿让我决定自己做一个。核心目标就一个:把一次血压录入压缩到 10 秒以内。
为什么选拨轮而不是键盘
做之前我列了几种方案:
- 数字键盘(市面主流)—— 要点 6-9 次才能输完三组数字
- 滑动条 Slider —— 精度不够,血压差 5 个点意义很大
- 系统原生 Picker(滚轮)—— 太慢,要滚很久
- 自定义拨轮 —— 有物理阻尼感,转一下跳一格,快速转动可以加速
我选了方案 4。说实话写 UI 组件的时间比写业务逻辑还长,光拨轮的惯性衰减曲线就调了一周多。但最后效果确实不错:转动拨轮选收缩压 → 自动跳到舒张压 → 再跳到脉搏,三步连贯完成,实测大概 3-5 秒。
数据模型的设计取舍
我用 SwiftData 做本地存储。一条健康记录长这样:
@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" // 多人档案
}
几个决策点:
- 血压和体重放在同一个 Model 里,没有拆成两张表。因为用户一次录入可能同时记血压和体重,拆开反而要处理关联关系。字段全部 optional,哪个有值存哪个。
profileID是后来加的多人档案支持。我爸妈两个人的数据都在我手机上,靠这个字段区分。默认值"default"保证老数据迁移不出问题。tagIDs存的是状态印章的 ID 数组。比如今天吃了降压药、做了运动,这些"干预行为"和这条记录绑定,后面看趋势图的时候能对照分析。
状态印章这个功能是怎么来的
有一次我看我爸的血压曲线,某天突然降了很多。问他那天干嘛了,他想了半天说"好像去公园走了一个小时"。
这让我意识到:光记数字不够,还得记"那天做了什么"。但如果让用户写文字备注,太重了,没人会坚持。
所以我做了印章系统——预设几个常见标签,点一下就行:
@Model
final class StatusTag {
var id: String
var name: String
var sfSymbolName: String
var colorHex: String
var orderIndex: Int
}
extension StatusTag {
static let defaults: [StatusTag] = [
StatusTag(id: "pill", name: "降压药", sfSymbolName: "pills.fill", colorHex: "#007AFF", orderIndex: 0),
StatusTag(id: "exercise", name: "运动", sfSymbolName: "figure.run", colorHex: "#FF9500", orderIndex: 2),
StatusTag(id: "coffee", name: "黑咖啡", sfSymbolName: "cup.and.saucer.fill", colorHex: "#A2845E", orderIndex: 4),
]
}
默认提供降压药、减脂餐、运动、好睡眠、黑咖啡五个。用户也可以自定义。录入的时候底部一排图标,该按的按一下,半秒钟的事。
后来看趋势图的时候,印章会以标记点的形式叠加在折线上。比如"吃了降压药的日子血压平均低 8 个点"——这种洞察对慢病管理挺有价值的。
PDF 就医报告:解决"最后一公里"
数据记了一个月,陪我爸去复诊。医生问"最近血压怎么样",我爸说"还行"。
医生其实想看具体数字和趋势。我当时打开 App 给医生看手机屏幕,医生扫了一眼说"你能不能打印出来"。
回来我就加了 PDF 导出功能。一键生成一份标准格式的报告:患者姓名、记录时间段、血压趋势图、异常值标注、用药情况汇总。A4 纸排版,直接 AirDrop 到医院的打印机,或者存到手机里给医生看 iPad。
这个功能在同类免费 App 里几乎没见过。说实话实现起来不难(用 UIGraphicsPDFRenderer),但就是没人做。
Siri 快捷指令
后来我加了 AppIntents 支持,可以通过 Siri 或快捷指令触发录入:
对着手机说"用健康手账记录健康数据",App 会打开并直接弹出录入面板。这对早上刚测完血压、手还湿着的场景挺方便。
关于商业模式
我选了买断制,不做订阅。原因很简单:这个 App 的目标用户是慢病患者,他们可能要用三年五年甚至更久。让一个高血压老人每月付费,我觉得不太对。
免费版能记录和看趋势,Pro 解锁多人档案、PDF 报告、iCloud 同步这些。一次买断,终身使用。
目前 App 刚上线不久,下载量还很小。没做任何投放,就是在几个社区发发帖。说实话独立开发者的冷启动确实难,但我觉得产品本身解决的问题是真实的,慢慢来吧。
几个踩坑记录
-
SwiftData + CloudKit 的坑:
cloudKitDatabase: .automatic开启后,模型的所有属性必须有默认值或者是 optional,否则同步直接崩。我profileID一开始没给默认值,线上闪退了。 -
拨轮手势冲突:拨轮用的是自定义 DragGesture,和 ScrollView 会打架。最后用
simultaneousGesture+ 手动判断方向解决的,代码写得很丑但能用。 -
提醒调度:用户可以设置每周哪几天提醒。我把
reminderDays存成Set<Int>(1=周日,7=周六),每次 App 进前台时重新调度 UNUserNotificationCenter。一开始偷懒只在设置页调度,结果用户改了系统时间后提醒就乱了。
写在最后
这个 App 从立项到上架花了大概两个半月,全职一个人做。SwiftUI + SwiftData 的开发体验比我预期好很多,特别是 @Observable 宏出来之后,状态管理清爽了很多。
如果你也在做健康类或者工具类 App,关于拨轮交互的实现细节、SwiftData 多档案的数据隔离方案,欢迎在评论区聊。