从MVVM到Provider注册表:Claude Usage Tracker用量追踪架构深度解析完整指南
【免费下载链接】Claude-Usage-TrackerNative macOS menu bar app for tracking Claude AI usage limits in real-time. Built with Swift/SwiftUI.项目地址: https://gitcode.com/gh_mirrors/cl/Claude-Usage-Tracker
Claude Usage Tracker是一款原生 macOS 菜单栏应用,用 Swift/SwiftUI 构建,能够实时追踪 Claude AI 用量限制(5 小时会话窗口与每周限额),并以多种图标风格呈现在菜单栏中。本文带你深度解析它的MVVM 架构与Provider 注册表设计,看看一个"看起来不大"的菜单栏工具,如何用清晰的工程结构支撑起多服务商(Anthropic、OpenAI Codex)扩展能力。
🧭 项目全景:一个菜单栏 App 的核心能力
对普通用户来说,Claude Usage Tracker 解决的是一个问题:额度还剩多少,还剩多久重置。它把答案直接放在 macOS 菜单栏上:
- 📊 实时显示 5 小时会话窗口与每周用量百分比,支持电量条、进度条、百分比、圆环等多种图标风格
- 👤 多 Profile 管理,支持 Anthropic 与 OpenAI Codex 双服务商
- 🔔 接近额度上限时推送通知,附带用量历史图表与 CSV 导出
- 📍 Notch HUD:在刘海区域显示 Claude Code 实时会话状态
上面这张图展示的就是它引以为傲的Navbar Icon Styles(彩色与单色两种变体)。图标渲染由 MenuBarIconRenderer.swift 完成——注意,渲染逻辑被单独抽成一个 Renderer 文件,而不是混在视图代码里,这正是后文要讲的"视图保持愚蠢"原则的体现。
🏛️ MVVM 四层分工:谁负责什么
项目在 CONTRIBUTING.md 中明确声明遵循MVVM(Model-View-ViewModel)模式,整个仓库的目录结构就是这张架构图:
| 层 | 目录 | 职责 |
|---|---|---|
| Model(模型) | Claude Usage/Shared/Models/ | 纯 Swift 数据结构,零 UI 依赖 |
| View(视图) | Claude Usage/Views/、Claude Usage/MenuBar/ | SwiftUI 视图,只负责展示 |
| ViewModel(视图模型) | MenuBarManager.swift | 业务逻辑与状态中枢 |
| Service(服务) | Claude Usage/Shared/Services/ | API 请求、Keychain、通知等系统交互 |
Model 层:一个 Struct 统一所有服务商的数据
所有服务商的用量最终都被映射到同一个结构体 ClaudeUsage.swift:百分比 + 重置时间作为各服务商的"最大公约数",Token 数、计划类型、余额等字段则按需提供。这种统一数据契约是整套架构的基石——UI 永远只认识ClaudeUsage,从不知道数据来自 Anthropic 还是 Codex。
ViewModel 层:MenuBarManager 是应用的心脏
MenuBarManager.swift(近 1900 行)是整个应用的"大脑"。它作为ObservableObject对外暴露一组@Published状态:
@Published private(set) var usage: ClaudeUsage = .empty @Published private(set) var isRefreshing: Bool = false @Published private(set) var hasCredentialError: Bool = false它还统一管理刷新定时器、多 Profile 切换、弹窗(Popover)生命周期、深色模式图标缓存等细节。而 App 的启动流程则由 AppDelegate.swift 编排:隐藏 Dock 图标(NSApp.setActivationPolicy(.accessory))、加载 Profile、决定是否弹出设置向导……入口文件 ClaudeUsageTrackerApp.swift 甚至只有十几行——因为这是纯菜单栏应用,默认不创建任何窗口。
View 层:让视图保持"愚蠢"
打开 PopoverContentView.swift 你会发现,SwiftUI 视图通过属性包装器订阅 ViewModel 状态:
@ObservedObject var manager: MenuBarManager @StateObject private var profileManager = ProfileManager.shared视图本身不做任何网络请求或业务判断,只把manager.usage画出来。配合 SettingsView.swift 等设置界面,形成了"状态变化 → 自动刷新 UI"的单向数据流。
🔌 Provider 注册表:多服务商扩展的关键设计
如果说 MVVM 解决的是"代码怎么分层",那么Provider 注册表(ProviderRegistry)解决的就是"功能怎么扩展"——这是整个架构中最精彩的设计。
一个唯一的"接缝"
ProviderRegistry.swift 的文件头注释写得很直白:
The single seam between the app and its usage providers.(App 与其用量服务商之间的唯一接缝)
整个应用从不出现provider == .anthropic这种硬编码判断。UI 和服务层只通过两个入口获取服务商相关信息:
descriptor(for:)—— 获取声明式的"事实":品牌名、Logo、状态页 URL、能力开关service(for:)—— 获取负责拉取用量的服务实例
描述符 + 能力开关:用数据代替 if-else
ProviderDescriptor.swift 定义了ProviderCapabilities能力标志:tokenCounts(是否报告 Token 数)、perModelBreakdown(是否有分模型明细)、consoleBilling(是否有计费 API)、cliAccountSync(是否可同步本地 CLI 账号)……
以 Codex 为例,它不报告 Token 数,于是tokenCounts: false——菜单栏的 Token 指标自动隐藏,CSV 导出自动省略 Token 列,图标自动回退到百分比显示。所有这些"隐藏行为"都不需要写任何if provider == .codex,而是由数据驱动。
协议契约:UsageProviderService
每个服务商都实现 UsageProviderService.swift 协议,只需回答三个问题:有没有凭证?怎么拉取用量?活动 Profile 是否允许更宽的认证链(如 Anthropic 的 Keychain CLI 回退)?调用方永远依赖协议,而非具体实现类——这是典型的面向接口编程 + 依赖倒置。
服务商的具体实现按目录隔离:AnthropicUsageProvider.swift 与 CodexUsageProvider.swift,各自带独立的认证服务和 API 客户端。
🛠️ 这套架构对贡献者意味着什么?
docs/ADDING_A_PROVIDER.md 给出了新增加盟服务商的 10 步清单:加一个枚举 case、建一个目录、注册一条描述符与一条服务、配好能力开关、补齐凭证 UI 与本地化……最关键的是文末那句承诺:
What you should NOT need to touch:
MenuBarManager、ProfileManager、PopoverContentView、SetupWizardView等核心文件无需修改。
也就是说,扩展是纯增量的(additive)——新增一个服务商就像插一块新板卡,而不是给主板动手术。Codex 的实现(Shared/Services/Providers/Codex/)就是官方参考实现,配套的 CodexProviderTests.swift 展示了 fixture 驱动的容错解码测试写法。
📚 新手阅读源码的路径建议
按这个顺序读,能最快建立整体认知:
- 入口:ClaudeUsageTrackerApp.swift → AppDelegate.swift,理解"菜单栏应用没有窗口"这件事
- 数据契约:ClaudeUsage.swift → Provider.swift
- 状态中枢:MenuBarManager.swift,重点看
@Published属性与 Combine 观察者 - 注册表:ProviderRegistry.swift → ProviderDescriptor.swift
- 扩展规范:docs/ADDING_A_PROVIDER.md,动手前的必读清单
存储层同样值得翻一翻:DataStore.swift 负责 UserDefaults 持久化,ProfileStore.swift 管理多 Profile,而敏感凭证则交给 KeychainService.swift 安全保管——分层清晰,各司其职。
✅ 小结
Claude Usage Tracker 的架构可以浓缩成三句话:
- MVVM 分层:Model 纯数据、View 纯展示、MenuBarManager 当状态中枢、Service 管系统交互
- 统一数据契约:所有服务商的用量都映射为
ClaudeUsage,UI 不感知来源 - Provider 注册表:描述符声明"是什么",协议约定"怎么取",能力开关驱动"显不显示",让多服务商扩展成为纯增量操作
对想学习 Swift/SwiftUI 工程实践的开发者来说,这是一个小而美的范本:没有重型框架,只有清晰的分层、接口和约定。想深入了解项目背景与贡献流程,可以参考 README.md 与 CONTRIBUTING.md。
【免费下载链接】Claude-Usage-TrackerNative macOS menu bar app for tracking Claude AI usage limits in real-time. Built with Swift/SwiftUI.项目地址: https://gitcode.com/gh_mirrors/cl/Claude-Usage-Tracker
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考