macOS 原生应用 AI 开发指导
本文档面向 AI 助手,用于指导 Presto 未来 macOS 原生版本的开发。 截至 2026 年 3 月,基于
Presto/docs/native-app-refactoring.md提取。
适用范围
本指导适用于:
- 目标项目:Presto 未来 macOS 原生版本(Swift + SwiftUI)
- 当前状态:Presto 目前是 Go + SvelteKit + Wails 应用,此文档为未来重构做准备
- AI 助手角色:遵循 Apple HIG 原则和 SwiftUI 最佳实践生成代码
注意:当前 Presto 仓库没有 Swift 代码。此文档是预备性规范。
技术栈
| 层级 | 技术 | 版本 | 说明 |
|---|---|---|---|
| 语言 | Swift | 6.0 | Swift Concurrency (async/await, actors) |
| UI 框架 | SwiftUI | 5.0 | @Observable 宏,NavigationSplitView |
| 数据持久化 | SwiftData | 1.0 | @Model 宏,CloudKit 集成 |
| 目标平台 | macOS | 14.0+ | Sonoma 及以上 |
| 目标平台 | iOS/iPadOS | 17.0+ | iPhone 和 iPad(可选) |
| 排版引擎 | Typst | 0.14.2 | 通过 CLI 或库调用 |
| 构建工具 | Xcode | 16.0+ | Swift Package Manager |
Apple HIG 核心原则
AI 生成的所有 UI 代码必须遵守以下四大原则:
1. Clarity(清晰)
- 界面易于理解,文字清晰、图标锐利
- 视觉层次分明,用户一眼看出重点
- 实践:使用系统字体(SF Pro)、标准字号、高对比度配色
2. Deference(顺从)
- UI 退后让内容成为焦点
- 使用半透明、模糊、微妙的 UI 元素
- 实践:避免过度装饰,使用系统标准控件(.buttonStyle(.bordered))
3. Depth(深度)
- 视觉层次和流畅过渡帮助用户理解位置和导航关系
- 实践:使用 NavigationStack/NavigationSplitView、系统转场动画
4. Consistency(一致性)
- 应用内部和跨平台保持一致的交互模式
- 实践:遵循 macOS 菜单栏规范、标准快捷键、系统图标(SF Symbols)
SwiftUI 代码规范
架构模式
采用 MVVM + Repository 模式:
swift
// Model: SwiftData @Model
@Model
final class RecentDocument {
var fileURL: URL
var lastOpened: Date
}
// ViewModel: @Observable
@Observable
final class ConversionPipeline {
var isConverting = false
var previewPages: [PreviewPage] = []
func convert(markdown: String, templateId: String) async throws { }
}
// Repository: actor 隔离
actor TemplateManager {
func listTemplates() throws -> [InstalledTemplate] { }
}
// View: SwiftUI
struct ContentView: View {
@Binding var document: PrestoDocument
@StateObject private var pipeline = ConversionPipeline()
}组件命名规范
| 类型 | 命名规则 | 示例 |
|---|---|---|
| View | 功能 + View 后缀 | ContentView, MarkdownEditorView |
| ViewModel | 功能 + 无后缀 | ConversionPipeline, ScrollSyncManager |
| Model | 名词,无后缀 | RecentDocument, Manifest |
| Repository | 功能 + Manager | TemplateManager, TypstCompiler |
| Protocol | 功能 + Service | PrestoService |
| Enum | 名词 | PageContent, EditorTheme |
状态管理
使用 SwiftUI 5 @Observable 宏(不是 @ObservableObject):
swift
@Observable
final class ConversionPipeline {
var isConverting = false // 自动触发 UI 更新
var typstSource: String = ""
func convert() async { }
}
// View 中使用
struct ContentView: View {
@State private var pipeline = ConversionPipeline()
// 或 @Environment 注入
}属性包装器选择:
| 包装器 | 用途 | 示例 |
|---|---|---|
@State | View 本地可变状态 | @State private var showSheet = false |
@Binding | 双向绑定传递 | @Binding var text: String |
@Observable | 可观察对象 | @Observable final class VM { } |
@Environment | 全局依赖注入 | @Environment(\.openDocument) |
@AppStorage | UserDefaults | @AppStorage("fontSize") var fontSize = 14 |
导航模式
macOS 三栏布局:
swift
NavigationSplitView {
// Sidebar: 文件导航、模板列表
SidebarView()
} detail: {
// Main Content
HSplitView {
MarkdownEditorView()
DocumentPreview()
}
}iOS/iPadOS 自适应:
swift
NavigationStack {
// iPhone: 单栏
// iPad 横屏:Split View
// iPad 竖屏:Tab 切换
}macOS 特有规范
菜单栏结构
标准菜单(macOS 自动生成):
- 应用菜单:About Presto, Preferences (⌘,), Quit (⌘Q)
- 文件菜单:New (⌘N), Open (⌘O), Save (⌘S), Export PDF (⌘E)
- 编辑菜单:Undo (⌘Z), Redo (⌘⇧Z), Cut, Copy, Paste, Find (⌘F)
- 视图菜单:Show/Hide Sidebar (⌘⇧S), Show Preview (⌘⇧P)
自定义菜单(通过 SwiftUI Commands):
swift
struct PrestoMenuCommands: Commands {
var body: some Commands {
CommandMenu("排版") {
Button("切换模板") { }
.keyboardShortcut("t")
Button("编译并预览") { }
.keyboardShortcut("r")
}
}
}键盘快捷键
| 快捷键 | 功能 | 说明 |
|---|---|---|
⌘O | 打开文件 | 全平台 |
⌘S | 保存 | 系统自动 |
⌘E | 导出 PDF | 自定义 |
⌘T | 切换模板 | 自定义 |
⌘R | 编译预览 | 自定义 |
⌘⇧P | 显示/隐藏预览 | macOS |
⌃⌘1/2/3 | 切换视图模式 | macOS |
代码示例:
swift
.buttonStyle(.bordered)
.keyboardShortcut("e", modifiers: .command)窗口管理
单例窗口(设置、模板商店):
swift
@main
struct PrestoApp: App {
var body: some Scene {
DocumentGroup(newDocument: PrestoDocument()) { file in
ContentView(document: file.$document)
}
#if os(macOS)
Settings {
SettingsView()
}
Window("模板商店", id: "template-store") {
TemplateStoreView()
}
.defaultSize(width: 800, height: 600)
#endif
}
}窗口尺寸限制:
swift
.frame(minWidth: 800, minHeight: 600)
.frame(maxWidth: .infinity, maxHeight: .infinity)系统集成
Spotlight 搜索:
- 通过
NSUserActivity建立索引 - Core Spotlight API 索引文档内容
Quick Look 预览:
swift
.quickLookPreview($selectedDocumentURL)Share Extension:
swift
.shareLink(item: pdfData, preview: SharePreview("document.pdf", image: thumbnail))拖放支持:
swift
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
// 处理文件拖放
}无障碍要求
VoiceOver 全程支持:
swift
MarkdownEditorView(text: $document.markdown)
.accessibilityLabel("Markdown 编辑器")
.accessibilityHint("在此输入 Markdown 内容")
DocumentPreview(pages: pipeline.previewPages)
.accessibilityLabel("文档预览")
.accessibilityValue("共 \(pages.count) 页")Dynamic Type 支持:
swift
@Environment(\.sizeCategory) var sizeCategory
var editorFont: Font {
.system(
size: UIFontMetrics.default.scaledValue(for: 14),
design: .monospaced
)
}Reduce Motion 适配:
swift
@Environment(\.accessibilityReduceMotion) var reduceMotion
.animation(reduceMotion ? nil : .easeInOut(duration: 0.2), value: isConverting)最小触控目标:
- 所有可交互元素最小 44×44 pt
- macOS 可适当缩小(鼠标精确),但 iOS 必须遵守
禁止事项
不要使用 deprecated API:
- ❌
NavigationView→ ✅NavigationStack/NavigationSplitView - ❌
@ObservableObject→ ✅@Observable(SwiftUI 5) - ❌
UIAlertController→ ✅ SwiftUI.alert()modifier
不要硬编码尺寸:
- ❌
.frame(width: 375, height: 812)(iPhone 尺寸) - ✅
.frame(maxWidth: .infinity)或使用 GeometryReader
不要忽略无障碍:
- ❌ 仅视觉反馈(颜色变化)
- ✅ VoiceOver 标签 + Dynamic Type + Reduce Motion
不要自定义系统行为:
- ❌ 重写
⌘Q退出逻辑 - ✅ 使用系统标准行为
不要在主线程执行耗时操作:
- ❌ 同步编译 Typst
- ✅
Task { try await typstCompiler.compile() }
不要跨平台使用不支持的 API:
- ❌ iOS 上调用
NSOpenPanel - ✅ 使用
#if os(macOS)条件编译
参考资源
Apple 官方文档:
Presto 内部文档:
Presto/docs/native-app-refactoring.md— 完整重构方案(97KB,详细架构设计)Presto-homepage/docs/ai-guide.md— 组织级 AI 开发指南
关键设计决策:
- 优先使用 PDFKit 渲染 PDF,而非 SVG(Apple 没有公开的运行时 SVG 渲染 API)
- Document-Based App 架构(
DocumentGroup),自动支持 iCloud Drive - 模板二进制通过
Process执行(macOS),iOS 使用 Swift 原生内置模板 - SwiftUI + SwiftData + CloudKit 实现 iCloud 三设备同步
2025-2026 新趋势:
- Liquid Glass 设计语言(WWDC25)
- visionOS 空间计算指南(如需支持 Vision Pro)
- Apple Intelligence AI 集成指南
最后更新: 2026-03-01 基于版本: Presto/docs/native-app-refactoring.md(截至 2026 年 3 月)
