为什么要做这个App
去年我给自己定了个规矩,每天至少专注写稿25分钟。试了一圈市面上的番茄钟,发现都是同一个套路:倒计时、叮一声、结束。
用了两周就不想打开了。没有留存感,没有成就感,就是个闹钟。
我想要的是——每次专注结束后,能看到点什么东西。像打游戏一样有经验条在涨,像跑步一样有公里数在累积。所以去年12月我上架了「声境护照」,把专注计时包装成了一段段飞行旅程。
现在版本 1.4,说实话下载量很一般,但这个项目本身让我学到了不少东西,值得记录一下。
数据模型:把专注包装成「远征」
我设计了一套叫 Expedition 的数据结构。每个"章节"对应一座城市(也就是一个声景),章节下面挂着几个任务:
struct ExpeditionChapterDefinition: Identifiable, Codable, Equatable {
let id: String
let sceneId: String
let cityName: String
let tagline: String
let bonusBounces: Int
let missions: [ExpeditionMissionDefinition]
}
struct ExpeditionMissionDefinition: Identifiable, Codable, Equatable {
let id: String
let title: String
let kind: ExpeditionMissionKind // .sessionCount / .focusMinutes / .deepFocusCount
let targetValue: Int
let rewardMiles: Int
}
```
任务类型就三种:累计专注次数、累计分钟数、深度专注次数。全部完成解锁下一座城市。这个结构看着简单,但给用户的感知完全不一样——不是"你今天专注了50分钟",而是"你在东京飞行了50公里,解锁了雨夜任务"。
实现成本其实不高,每次 `focusSession` 结束时遍历一遍当前章节的 missionStates,更新 progress 值,判断是否触发 completedAt 就行了。
## 统计和分享卡片:花了三周才搞定
战报卡片是我花时间最多的部分。每次专注结束要生成一张好看的卡片,包含时长、声景、效率指数、经验值增长。周末还有周回顾卡片,画7天分布图。
ViewModel 层我写得很薄,核心计算全部收在 `StatsService` 里:
```swift
var stats: StatsData {
StatsService.buildStatsData(
focusLogs: logs,
streakDays: store.streakDays,
rangeKey: .d7,
now: Date(),
isDemo: store.focusLogs.isEmpty
)
}
```
这里有个取舍:当用户 focusLogs 为空时,我会塞 demo 数据进去。新用户打开统计页看到的不是空白,而是示例卡片。这样他知道"坚持几天后我也能得到这个"。试了不放 demo 数据的版本,新用户留存明显差一截。
卡片渲染这块踩了坑。一开始用 SwiftUI 直接转 UIImage,部分机型偶现空白图。后来改成先塞进 UIHostingController、等布局完成再截图,问题解决了但代码丑了不少。
## 智能推荐下一次专注
1.4 版本加了一个功能:专注结束后根据历史数据推荐下一次的时间和时长。
逻辑考虑三个因素:今天日计划还差几段、当前连续天数、历史高频专注时段。连续天数不同,提示文案不同——连续5天以上说"重点是保持节奏",刚开始的用户说"先连续3天试试"。
这些文案我改了好几版。最早写得太鸡汤被我自己嫌弃了,太冷冰冰又没有推动力。最后的原则是:只说事实和建议,不说"加油你可以的"。
## 灵动岛
1.2 版本接了 ActivityKit 做灵动岛显示。锁屏状态能看到剩余时间和当前声景名。ContentState 里放了 endDate、phase、remainingSeconds、progress 这些字段。
这个功能实现不复杂,主要注意更新频率别太高就行。对用户体验提升挺明显——不用解锁手机就能瞄一眼还剩多久。
## 踩过的坑和一些数字
上架时间:2024年12月。当前版本 1.4。
说实话最近一周下载基本为零。独立开发没预算推广,冷启动全靠搜索和口碑,这事儿急不来。
几个教训:
- 声景音频文件把包体撑到了 200MB+,后来改按需下载才降到 60MB
- - 成长系统经验值公式改了三次。第一版两天就满级,完全没有盼头。现在大概连续用两周到 Lv.5
- - 分享功能做得再花哨,如果用户没养成打开 App 的习惯,就没有分享的机会。所以通知策略比卡片设计更重要
## 后续想法
在考虑做鸿蒙版本。ArkUI 声明式写法和 SwiftUI 思路接近,ViewModel 逻辑迁移应该不难。音频后台播放和卡片服务(对标 Live Activity)这块还在研究 HarmonyOS 的文档。
如果你也在做 iOS 或者鸿蒙的工具类独立应用,可以评论区聊聊,互相参考一下踩坑经验。
App Store 搜索「声境护照」能找到。
341



被折叠的 条评论
为什么被折叠?



