Files
kangkang/docs/superpowers/specs/2026-05-30-faceid-app-lock-design.md
link2026 d2c77d5c51 feat: 国际化(i18n) en/ja/ko + App 内语言切换
主体:多语言支持(简体中文源 + 英/日/韩)
- 基础设施:Localizable.xcstrings(String Catalog,sourceLanguage=zh-Hans)
  + pbxproj developmentRegion/knownRegions 注册 en/ja/ko
- 全部硬编码 Locale("zh_CN") → Locale.current;中文 dateFormat → Date.FormatStyle(跟随系统)
- UI 中文字面量统一为 String(appLoc:)(显式绑定所选语言 bundle+locale,即时切换)
  Text 字面量走环境 \.locale + Bundle 重定向
- 549 个 catalog key 全部 en/ja/ko 翻译完成(0 未翻译)
- App 内语言切换:我的 → 语言(LanguageManager + 即时生效,无需重启)
- 双用预设(症状/监测指标/慢病)本地化:static→computed 避免缓存

注:本提交为 WIP,一并打包了并行进行的功能模块
(HealthExport 健康导出、Security/Face ID 锁、DiaryAssist 日记 AI 辅助)
及 App 图标、CLAUDE.md、docs/scripts。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 10:28:24 +08:00

6.2 KiB

Face ID 启动锁 — 设计文档

日期:2026-05-30(W2) 作者:link2026 + Claude 关联卖点:#4 隐私三件套(系统级加密 + Face ID + 永久删除) 优先级:P1(CLAUDE.md §6 / §8 / §11,原排期 W5 末,提前实现)


1. 一句话定位

可选的 Face ID/Touch ID 启动锁(默认关)。开启后,冷启动与「后台超过 1 分钟再回前台」都需要系统认证才能进入 App;失败可用设备密码兜底。完全基于系统 LocalAuthentication,不自造任何密码学(对齐红线 §10.2)。


2. 设计决策(已与用户确认)

决策点 选择
锁屏时机 冷启动 + 后台超过宽限才重锁
后台宽限 60 秒
认证策略 .deviceOwnerAuthentication(Face ID/Touch ID 优先,自动跳设备密码兜底,避免锁死)
默认状态 关(§6)
开关位置 「我的」Tab 现有的 Face ID 卡,改为可交互 Toggle
任务切换器隐私遮罩 加,仅锁开启时生效(进 .inactive/.background 盖品牌遮罩,防多任务快照泄露;默认关用户无感)

关于 §6「截屏黑屏防护…不做」:那条针对的是截图防护(iOS 无官方 API);本设计的任务切换器遮罩是 .inactive 盖视图,是官方支持的标准做法,性质不同。


3. 架构

KangkangApp
  └─ WindowGroup { AppLockContainer { RootView() } }   ← 仅包一层,RootView 零改动(§10.7)
                      │
        ┌─────────────┴──────────────────────────────┐
        │ AppLockContainer<Content>                   │
        │  @Environment(\.scenePhase)                 │
        │  渲染 content                                │
        │   .overlay { if isLocked        → LockScreen}│
        │   .overlay { else if showsCover → PrivacyCover}│
        │  onAppear → handleAppear();                 │
        │  onChange(scenePhase) → handleScenePhase()  │
        └─────────────────────────────────────────────┘
                      │ 读写
        ┌─────────────┴──────────────────────────────┐
        │ AppLock.shared  (@MainActor @Observable)     │  ← Security/AppLock.swift
        │  enabled  ←→ UserDefaults("faceIDLockEnabled")│
        │  isLocked / showsPrivacyCover                │
        │  biometryAvailable / biometryLabel           │
        │  gracePeriod = 60s,lastBackgroundedAt        │
        │  authenticate() / enableWithAuth() / disable()│
        └──────────────────────────────────────────────┘

单例写法与项目既有 ModelDownloadService.shared 一致(@MainActor @Observable final class + static let shared)。


4. 触发逻辑(状态机)

scenePhase / 事件 行为
容器 onAppear(冷启动) enabled 为真且尚未冷启动锁过 → isLocked = true + 触发认证
.background lastBackgroundedAt = now;showsPrivacyCover = enabled
.inactive(任务切换器) showsPrivacyCover = enabled && !isLocked
.active 隐藏遮罩;若 enabled && !isLocked && 离开 > 60sisLocked = true;若 isLocked → 触发认证;清空 lastBackgroundedAt
认证成功 isLocked = false
认证失败/取消 保持锁定,锁屏提供「解锁」按钮重试(isAuthenticating 防重入,不重复弹窗)

冷启动时 scenePhase 初值为 .active 不触发 onChange,由 handleAppear() 负责冷启动锁;两路触发由 isAuthenticating 守卫去重。


5. 能力探测与兜底

  • refreshAvailability():LAContext.canEvaluatePolicy(.deviceOwnerAuthentication)biometryAvailable;读 biometryType 决定文案(Face ID / Touch ID / 密码)。
  • 设备未设密码/无生物识别 → biometryAvailable = false,「我的」开关置灰,副标题「本设备未设置 Face ID 或密码」。
  • 认证全程系统弹窗;失败/取消不抛错给 UI,只是停留锁屏。

6. 文件清单

文件 改动
康康/Security/AppLock.swift 新增:单例 + LAContext 封装 + 触发逻辑
康康/Security/AppLockContainer.swift 新增:包裹层 + scenePhase 驱动 + 两个 overlay
康康/Security/LockScreenView.swift 新增:LockScreenView + PrivacyCoverView
康康/App/KangkangApp.swift RootView()AppLockContainer { RootView() }
康康/Features/Me/MeView.swift 静态 Face ID 卡 → 可交互 Toggle 卡
康康.xcodeproj/project.pbxproj INFOPLIST_KEY_NSFaceIDUsageDescription(Debug + Release)

工程用文件系统同步组,新增 Security/ 下的源文件自动纳入编译,无需手改 pbxproj 注册。


7. UI

锁屏(LockScreenView,全遮罩,走 Tj tokens):

   🔒 (lock glyph)
   康康 已锁定
   你的健康档案已加密保护
   [  Face ID 解锁  ]   ← onAppear 自动触发一次认证;按钮文案随设备能力变

隐私遮罩(PrivacyCoverView):品牌色底 + app 名,无交互,仅用于遮挡多任务快照。

「我的」Face ID 卡:Toggle 开启时先认证一次(成功才置 enabled),关闭直接关。副标题动态:「已开启 · Face ID」/「关闭」/「本设备未设置 Face ID 或密码」。


8. 红线对齐(CLAUDE.md §10)

  • 不自造密码学,只用系统 LocalAuthentication
  • 默认关,可选开关
  • 不引云
  • 不重构 Tab/RecordSheet 骨架,只加一层包裹
  • 清单内功能(§6/§8/§11 明列 Face ID 启动锁)

9. 测试与验收

  • 单元测试价值低(核心是系统弹窗 + scenePhase),不强求;AppLock 的宽限判定逻辑可抽纯函数测(可选)。
  • 真机验收:① 开关开启走 Face ID;② 杀进程冷启动需认证;③ 后台 <60s 回来不锁、>60s 回来锁;④ 多任务切换器快照被遮罩;⑤ 关 Face ID 录入(模拟失败)能跳设备密码;⑥ 默认关时全程无感。
  • 模拟器:Features → Face ID → Enrolled / Matching Face 可模拟。