生成日期:2026-05-20
如果从零开始重建 TagLauncher,新架构要同时满足五个目标:
新版不建议继续用重 SwiftUI 作为主界面骨架。
原因不是 SwiftUI 不好,而是目标里明确有 macOS Catalina 和老 Intel Mac。Catalina 时代的 SwiftUI 能力、性能和兼容性都不够稳定,尤其是:
更稳的方案是:
AppKit-first + 纯 Swift 领域核心 + SQLite 本地存储 + 预计算搜索索引 + 虚拟化 App 网格
SwiftUI 可以保留为小范围可选能力,但不作为主界面和核心交互的基础。
架构按用户任务拆分,而不是按技术控件拆分。
核心用户任务:
业务规则不直接写在窗口、ViewController 或 AppKit delegate 里。
例如:
主线程只处理:
以下工作不在主线程做:
任何可能超过一帧预算的工作都要满足:
避免依赖新系统能力:
MenuBarExtra。采用成熟稳定 API:
| 层 | 方案 |
|---|---|
| UI | AppKit |
| 主 App 外壳 | NSApplicationDelegate + NSStatusItem |
| 主界面窗口 | NSPanel / borderless overlay |
| App 网格 | NSCollectionView + diffable data source |
| Quick Search | 独立 NSPanel / NSViewController / NSTextField / NSTableView 或 NSCollectionView |
| 设置 | NSWindowController + AppKit 表单 |
| 本地数据库 | SQLite + WAL |
| 数据访问 | Repository 模式 |
| 异步任务 | OperationQueue + GCD |
| 搜索索引 | 内存索引,必要时辅以 SQLite FTS5 |
| 图标缓存 | 内存 LRU + 磁盘缩略图缓存 |
| 本地化 | Bundle JSON 或 stringsdict,可封装成 LocalizationService |
| 全局快捷键 | Carbon RegisterEventHotKey |
| 开机登录 | LaunchAgent,必要时未来再封装 SMAppService 分支 |
flowchart TD
Shell["App Shell<br/>菜单栏、Dock、快捷键、窗口"]
MainUI["Main Launcher UI<br/>浏览、标签导航、拖拽整理"]
SearchUI["Quick Search UI<br/>输入、建议、结果、启动"]
SettingsUI["Settings UI<br/>语言、外观、数据、快捷键状态"]
AppCore["Domain Core<br/>App、标签、备注、分类方案、规则"]
SearchCore["Search Core<br/>索引、匹配、评分、快速建议"]
SmartCore["Smart Start Core<br/>默认目录、初始分类、默认备注"]
DataCore["Data Core<br/>SQLite、备份、导入导出、版本管理"]
SystemAdapters["System Adapters<br/>App 扫描、NSWorkspace、Hotkey、Login Item"]
ResourceLayer["Resources<br/>本地化、Smart Catalog、图标资源"]
Infra["Infrastructure<br/>任务调度、日志、缓存、事件总线"]
Shell --> MainUI
Shell --> SearchUI
Shell --> SettingsUI
MainUI --> AppCore
SearchUI --> SearchCore
SettingsUI --> AppCore
AppCore --> DataCore
AppCore --> SmartCore
SearchCore --> AppCore
SmartCore --> ResourceLayer
SystemAdapters --> AppCore
DataCore --> Infra
SystemAdapters --> Infra
职责:
建议组件:
AppCoordinator
MenuBarController
HotkeyController
WindowCoordinator
LoginItemController
ApplicationMenuController
设计要点:
WindowCoordinator 管理,避免窗口层级逻辑散落。职责:
建议组件:
MainLauncherViewController
TagNavigatorViewController
AppGridViewController
AppDetailBubbleController
EditModeController
QuickSearchViewController
SettingsWindowController
设计要点:
NSCollectionView,按 section 表示标签分组。职责:
建议模块:
AppLibrary
Tagging
Notes
UsageHistory
CategoryScheme
UncommonApp
LocalizationDomain
核心对象:
AppRecord
Tag
TagAssignment
AppNote
UsageStat
CategoryScheme
UncommonMarker
LaunchResult
设计要点:
AssignTagsCommand、MoveAppCommand、EditNoteCommand。TagsChanged、NoteChanged、AppLaunched。职责:
建议组件:
SearchIndexBuilder
SearchIndex
QueryNormalizer
MatchEngine
Ranker
QuickSuggestionsProvider
设计要点:
职责:
建议组件:
SmartCatalogProvider
SmartMatcher
SmartDraftBuilder
SmartApplyService
DefaultNoteLocalizer
设计要点:
职责:
推荐 SQLite,而不是单一 JSON。
原因:
建议表:
| 表 | 含义 |
|---|---|
apps |
已知 App 基础身份缓存 |
tags |
标签定义 |
app_tags |
App 与标签多对多关系 |
notes |
App 备注 |
usage_stats |
打开次数和最近打开 |
uncommon_markers |
不常用标记和来源 |
category_schemes |
分类方案元信息 |
scheme_backups |
备份索引 |
disabled_system_categories |
用户删除过的系统分类 |
settings_shadow |
需要随导出包携带的业务设置 |
smart_start_state |
Smart Start 运行状态 |
设计要点:
职责:
建议组件:
AppDiscoveryAdapter
IconProvider
LaunchServicesAdapter
HotkeyAdapter
LoginItemAdapter
FilePanelAdapter
ScreenAdapter
设计要点:
AppRecord
├── appID
├── displayName
├── localizedNames
├── bundleIdentifier
├── path
├── isAppleApp
├── sourceLocation
├── iconCacheKey
└── lastSeenAt
设计规则:
appID 是内部稳定 ID,不直接等于 path。Tag
├── tagID
├── displayName
├── color
├── sortOrder
├── systemCategoryID
└── isUserDeletedSystemTag
设计规则:
systemCategoryID 保持稳定身份。AppNote
├── appID
├── text
├── source
├── languageCode
└── updatedAt
备注来源:
设计规则:
CategoryScheme
├── schemeID
├── name
├── createdAt
├── changedAt
├── origin
└── previousSchemeID
设计规则:
启动 App
-> 初始化数据库和配置
-> 加载用户设置
-> 注册菜单栏和快捷键
-> 后台预热 AppLibrary 快照
-> 后台预热 SearchIndex
-> 用户触发主界面或 Quick Search 时直接使用最近快照
体验收益:
扫描标准目录
-> 生成 AppDiscoverySnapshot
-> 身份解析与去重
-> 增量写入 apps 表
-> 对新增/移除 App 发出事件
-> AppLibrary 生成新快照
-> UI 和 SearchIndex 增量更新
设计规则:
用户触发主界面
-> WindowCoordinator 显示 overlay
-> MainLauncherViewController 使用 AppLibrary 当前快照
-> AppGrid diff 更新
-> 后台检查是否需要刷新扫描
设计规则:
用户触发 Quick Search
-> 立即打开搜索面板
-> 输入框获得焦点
-> 空查询显示 Quick Suggestions
-> 用户输入
-> SearchIndex 同步返回轻量结果
-> 图标和备注异步补齐
-> Enter 启动
设计规则:
用户添加/移除/拖拽标签
-> UI 产生 command
-> Domain 校验规则
-> DataCore 在事务中写入
-> 产生 domain event
-> AppLibrary 快照更新
-> Main UI diff 更新
-> SearchIndex 增量更新标签字段
设计规则:
AppLibrary 快照就绪
-> SmartStartPolicy 判断是否需要运行
-> SmartMatcher 生成 draft
-> 新用户自动应用
-> 老用户显示建议
-> 应用前创建 scheme snapshot
-> 写入标签和默认备注
设计规则:
采用:
避免:
采用:
目标:
采用:
目标:
采用三级缓存:
设计规则:
采用:
目标:
建议:
macOS 10.15 Catalina
Universal binary: x86_64 + arm64
TagLauncher/
├── App/
│ ├── AppCoordinator.swift
│ ├── MenuBarController.swift
│ ├── WindowCoordinator.swift
│ └── HotkeyController.swift
├── Presentation/
│ ├── MainLauncher/
│ ├── QuickSearch/
│ ├── Settings/
│ └── SharedViews/
├── Domain/
│ ├── AppLibrary/
│ ├── Tagging/
│ ├── Notes/
│ ├── Search/
│ ├── SmartStart/
│ └── CategoryScheme/
├── Data/
│ ├── Database/
│ ├── Repositories/
│ ├── Backup/
│ └── ImportExport/
├── SystemAdapters/
│ ├── AppDiscovery/
│ ├── IconProvider/
│ ├── Launcher/
│ ├── LoginItem/
│ └── Hotkeys/
├── Resources/
│ ├── Localization/
│ ├── SmartCatalog/
│ └── Assets/
└── Tests/
├── DomainTests/
├── SearchTests/
├── SmartStartTests/
└── SchemaVersionTests/
必须覆盖:
必须覆盖:
建议设定基准:
最低覆盖:
| 设计主题 | V1 选择 |
|---|---|
| 主 UI 技术 | AppKit-first,保证 Catalina 和 Intel Mac 稳定性 |
| App 网格 | NSCollectionView 虚拟化和 cell reuse |
| 业务规则 | Domain Core 独立,UI 只发送 command |
| 搜索 | SearchIndex 常驻,输入路径纯内存 |
| 用户数据 | SQLite + 事务 + WAL |
| App 身份 | 内部 appID + bundleIdentifier/path/name 多线索解析 |
| 备注 | Note source 显式建模,用户备注优先 |
| Smart Start 资源 | 默认目录和默认备注资源化,备注按语言拆分加载 |
| 兼容性 | macOS 10.15 deployment target,x86_64 + arm64 universal binary |
在完全从零开始、没有任何历史包袱的前提下,最合理的新版 TagLauncher 架构是一个 AppKit-first 的本地原生架构:
AppKit 稳定交互
+ 纯 Swift 领域核心
+ SQLite 本地数据
+ 预热搜索索引
+ 虚拟化 App 网格
+ 系统 API 适配层
这套方案的核心价值是:
这就是我建议的 TagLauncher 新版架构方案 V1。