Huang Codes

Engineering · Indie Apps

这些小 App 是怎么实现的:技术栈、实现方式和难点

我的这些 App 看起来分散:有日语读音、中文拼音、开店测算、健康打卡、喝水提醒、工时记录、票据裁切和错事复盘。

但它们背后的实现思路很接近:能用系统能力就用系统能力,能在本地完成就不引入服务器,能用清楚的数据结构解决就不先做复杂后台。

共同的技术取向

除了 WorkHours 是 Expo / React Native 路线,其余大部分都是 SwiftUI 原生 App。原因很简单:这些工具都很贴近 iOS 系统能力,比如通知、小组件、Watch、照片、OCR、朗读、HealthKit 和本地存储。

  • 界面:以 SwiftUI 或 React Native 组件构建,优先保证日常操作快、少层级。
  • 数据:大多使用本地存储、UserDefaults、App Group 或 iCloud 这类系统能力。
  • 系统能力:OCR 用 Vision,朗读用系统语音,小组件用 WidgetKit,手表端用 watchOS/Watch App。
  • 发布配套:官网承载 App 详情页、支持页、隐私政策、使用条款和更新记录。
  • 产品边界:尽量不做账号、广告、分析追踪和不必要的云端服务。

Sumiyomi:日语读音标注工具

Sumiyomi 的核心是把日语文本变成更容易读的文本:给汉字标注假名,支持 OCR、朗读、单词复习、小组件和可选 iCloud 同步。

通过什么实现:SwiftUI 做主界面,Share Extension 接收外部文本,WidgetKit 显示学习状态,系统 OCR 和朗读负责图片识别与语音播放,学习记录和收藏词在本地保存,跨设备同步尽量交给 iCloud。

怎么实现:用户可以粘贴文本,也可以从分享扩展或图片 OCR 进入。App 会把文本拆成可显示的片段,再按 furigana、kana、romaji 等模式渲染。收藏词、历史记录、小组件展示使用同一套数据来源,避免“主 App 看到一套,小组件看到另一套”。

难点:日语难在分词和读音并不总是一一对应,OCR 也会在漫画、截图和低清图片里产生噪声。另一个长期难点是入口很多:主 App、分享扩展、小组件、iCloud 同步、App Store 多语言文案都要一致。

Pinyin Scholar:中文拼音标注工具

Pinyin Scholar 解决的是中文学习里很具体的问题:粘贴或扫描中文文本,快速生成拼音标注、朗读,并导出成学习材料。

通过什么实现:SwiftUI 构建界面,Vision 做本地 OCR,AVSpeechSynthesizer 负责中文朗读,偏好设置用 UserDefaults 保存,分享扩展和 App Group 用来把外部文本带进 App。

怎么实现:文本进入后先做标准化,再生成拼音片段。显示层可以切换声调符号、数字声调和 IPA。导出时把同一份标注结果重新排版成 PDF、图片或 RTF,而不是重新识别一次。

难点:中文拼音最麻烦的是多音字、变调、繁简体和长文本排版。技术上不是“查字典就完了”,而是要让标注结果在学习场景里足够稳定、可读、可导出。

开店先算账:餐饮开店测算

开店先算账 是一个偏计算模型的工具。它不需要云端,重点是把加盟费、租金、人工、客单价、订单量、毛利率、回本周期这些数字组织清楚。

通过什么实现:SwiftUI 原生界面,加一层独立的测算模型。用户填写的数据和保存的方案用本地 JSON 快照保存到 UserDefaults。

怎么实现:输入层只负责收集数据,计算层把收入、成本、毛利、净利、保本订单量、保本流水和回本周期拆成明确公式。结果页再把这些计算结果变成可阅读的报告和压力测试。

难点:难点不是公式本身,而是边界和语气。开店测算很容易让用户误以为这是投资建议,所以页面要强调“测算参考”,同时又要把真实成本拆得足够细,避免只看流水不看利润。

小桃提肛:健康训练和提醒

小桃提肛 是一个很轻的训练 App:每天 1 分钟,记录、提醒、日历、统计都尽量在本地完成。

通过什么实现:SwiftUI 做训练界面和日历,UserDefaults 保存偏好与训练数据,HealthKit 可选同步训练结果,系统通知做提醒,音效反馈用 AVFoundation。

怎么实现:训练流程被拆成计时、收缩/放松节奏、完成记录、日历展示和统计。提醒不是简单每天固定时间轰炸,而是根据用户设置的时间窗和训练习惯做得更克制。

难点:健康类 App 最难的是分寸。它不能像医疗产品一样做承诺,也不能把提醒做得太吵。另一个难点是隐私:训练记录、HealthKit 权限和本地数据要让用户知道边界。

Hydropace:喝水提醒

Hydropace 的方向和小桃提肛有点像,但它更强调“低打扰”:记录一杯水之后,提醒节奏会自动放轻。

通过什么实现:SwiftUI 主 App,WidgetKit 做 iPhone 小组件和 Watch 小组件,watchOS App 做手表快速记录,UserDefaults + App Group 在 App、小组件和 Watch 之间共享本地数据,本地通知负责喝水提醒。

怎么实现:饮水记录、目标和提醒设置集中在一个共享 store。主 App 负责完整编辑和统计,小组件/手表只做快速记录和展示。记录后会产生一个 quiet window,避免刚喝完又提醒。

难点:多 target 是主要复杂度:主 App、小组件、Watch App、Watch 小组件都要读同一份数据。发布前还要确认哪些功能真的在编译目标里,避免设计稿里有、实际包里没有。

WorkHours:工时和收入记录

WorkHours 是这里比较特别的一个,它不是纯 SwiftUI,而是 Expo / React Native 路线。

通过什么实现:Expo、React Native、TypeScript 和 Expo Router 负责 App 结构,AsyncStorage 保存本地数据,expo-notifications 做提醒,expo-local-authentication 用于 Face ID / Touch ID,expo-sharing 和 view-shot 支持报告导出与分享,EAS 负责 iOS 构建和提交。

怎么实现:核心数据是每天的工时记录、薪资模式、加班规则和地区设置。计算层把记录转成月度工时、预计收入、加班费和历史图表。首次设置里先展示工资预览,是为了让用户马上知道这些配置会算出什么。

难点:难点一半在业务,一半在发布。业务上要处理时薪/月薪、15 分钟取整、午休、节假日、多币种和加班规则;发布上要处理 Expo 依赖、lockfile、native build number、iOS deployment target 和 App Store 上传链路。

票据裁切:自动裁切和本地 OCR

票据裁切 是一个图像处理工具:从相册导入票据或文件,自动识别边缘、透视校正、增强,然后保存。

通过什么实现:SwiftUI 做操作界面,Photos 负责导入和保存,Vision 做文档边缘检测和 OCR,本地图像处理管线负责裁切、透视校正和增强。

怎么实现:图片进入后先检测可能的纸张矩形,再把四角转换成可编辑点。自动结果可信时直接预览,不可信时提示用户检查四角。保存时输出处理后的结果,不覆盖原图。

难点:自动裁切最难的不是找到一个矩形,而是判断这个矩形是不是用户真正要的票据。桌面纹理、包装、广告牌、阴影都会误导检测,所以要给用户一个低成本的人工校正入口。

错事白板:复盘和防复发卡片

错事白板 是一个复盘工具,用来记录小失误、拆原因、整理下次怎么避免。

通过什么实现:SwiftUI 做白板、记录库和卡片界面,Observation 管理状态,本地 UserDefaults 保存 JSON 数据,系统通知用于提醒回顾,导出功能把记录整理成可分享内容。

怎么实现:一条记录从“发生了什么”开始,逐步补上原因、分类、下次避免方式。未处理的记录像便利贴一样留在白板上,处理完成后归档,并沉淀成防复发卡片。

难点:这个 App 的难点很少是技术炫技,而是数据流和语气。编辑记录以后,详情页、白板、归档、统计和导出都必须看到同一份最新数据;同时文案不能像批评用户,而要像一个温和的复盘工具。

为什么现在还不急着做后台

这些 App 的共同特点是:数据很私人,使用频次高但每次很短,核心价值不依赖社区、账号或跨平台协作。所以现阶段官网负责展示、协议、更新记录和博客,App 自己负责本地功能。

以后如果某个产品真的需要云同步、订阅后台、Web 管理台或多人协作,我会再引入服务器和数据库。但不是因为“看起来更高级”而引入,而是因为产品真的需要。

我从这些 App 里学到的共同难点

  • 小工具最怕功能边界变模糊,越做越像大系统。
  • 本地优先能降低信任成本,但要处理好备份、迁移和多入口一致性。
  • 系统能力很好用,但真正的体验差异在失败状态:OCR 错了、通知没到、数据不同步时怎么办。
  • App Store 上架不是最后一步,支持页、隐私政策、版本记录和官网也属于产品的一部分。
  • 个人开发者最重要的不是技术堆满,而是把可维护范围控制住。

所以我现在更喜欢把产品做成“安静的小工具”:打开就能用,数据边界清楚,出了问题能解释,长期维护也不会压垮自己。