Dev Guide / 2026 ・ No.01

Mac 应用开发入门路线图

从零开始,到能把桌面实用小工具分发给别人

发布

全局地图 ── 8 个步骤

STEP 0 准备 约 1 天 STEP 1 Swift 基础 约 2–3 周 STEP 2 SwiftUI 基础 约 3–4 周 STEP 3 Mac 特性 约 2–3 周 STEP 4 数据与存储 约 2 周 STEP 5 系统集成 约 2–3 周 STEP 6 质量打磨 全程并行 STEP 7 发布 约 1–2 周 终点:自制的小工具,能装到别人的 Mac 上

朱色框是 Mac 特有的部分。时间按每天约 1 小时估算,仅供参考

“想自己做一个在 Mac 上用的小工具。”
有这个想法的人,该从哪里学起、按什么顺序学,这里做了整理。
每一步要掌握的知识,以及动手做的练习作品,也一并介绍。

一

做什么,用什么做

CORE

目标是“能从菜单栏或快捷键调出来的实用小工具”。工具选 Swift 与 SwiftUI,不够的地方再补上 AppKit。不要一上来就做大作品,每一步完整做出一个小作品,才是捷径。

本文所说的“实用小工具”,指的是这样一类应用:

  • 常驻菜单栏 ── 点一下就能用的计时器、剪贴板历史、便签
  • 批量处理文件 ── 批量重命名、压缩图片、格式转换
  • 用快捷键调出 ── 启动器、常用语输入,不打断手头的工作

为什么选 Swift 与 SwiftUI

Mac 应用也可以用 Electron、Tauri、Flutter 等技术来做,好处是能用上 Web 的知识,还能同时出 Windows 版。但如果只说常驻后台的小工具,Apple 原生的 Swift 与 SwiftUI 更有优势。

  • 轻 ── 常驻也不怎么占内存、耗电
  • 直接沿用 Mac 的规矩 ── 菜单栏、设置窗口、快捷键、深色模式,乃至 macOS 26 起的新设计(Liquid Glass),标准组件就能搞定
  • 直通系统功能 ── 文件、剪贴板、通知、登录时启动等,都有官方 API
  • 能从一手资料学 ── Apple 的官方教程和文档,本身就是教材

SwiftUI 是一种“声明界面长什么样”的写法,也是 Apple 当前最重视的 UI 框架。Mac 上还有历史悠久的 AppKit,SwiftUI 够不着的细节用 AppKit 补,是现在的标准做法。一开始只用 SwiftUI 就足够了。

路线图全貌

STEP学什么参考时间练习作品
0 准备Mac 与 Xcode、Git1 天运行一个空应用
1 Swift 基础变量・类型・Optional・函数・结构体2–3 周命令行单位换算
2 SwiftUI 基础View・状态・布局・列表3–4 周把单位换算做成窗口
3 Mac 特性Scene・菜单栏・设置窗口・快捷键2–3 周菜单栏计时器
4 数据与存储设置・JSON・文件・SwiftData2 周常用语(片段)本
5 系统集成沙盒・权限・登录启动・剪贴板2–3 周剪贴板历史
6 质量打磨调试・测试・无障碍・多语言并行打磨已有作品
7 发布签名・公证・App Store1–2 周请朋友在他的 Mac 上打开

时间只是参考。如果是第一次学编程,STEP 1 和 2 花上两倍时间也很正常。这一步的练习作品能跑起来,就进入下一步——以此为信号即可。

二

STEP 0 ── 准备与 Xcode

CORE

有一台 Mac 和 Xcode(免费)就能开始。收费的 Apple Developer Program,到分发给别人(STEP 7)之前都用不着。

需要准备的东西

MacApple 芯片(M1 及以后)。2026 年 9 月发布的 Xcode 27 只能在 Apple 芯片的 Mac 上运行,且需要 macOS Tahoe 26.6 或更高版本
内存・空间内存 16GB 以上、可用空间几十 GB 会比较安心(Xcode 及相关文件体积很大)
Xcode在 Mac App Store 免费获取。写代码、做界面、运行、发布,全靠它一个
Apple 账户平时用的就行。在 Xcode 的设置里登录
Git管理修改历史。Xcode 已内置。有 GitHub 账号会更方便

手上是 Intel 的 Mac,最多能用到上一代的 Xcode 26。如果准备新买,只选 Apple 芯片。

认识 Xcode 的界面

Xcode 窗口(示意图)

MyApp › My Mac ▾ MyApp MyAppApp ContentView Assets Count: 3 +1 1 2 3 4 5 6 1 工具栏:按 ▶ 运行 2 导航器:文件列表 3 编辑器:写代码 4 画布:预览 5 检查器:调整属性 6 调试区:日志与变量

对实际界面做了简化。各个区域都可以显示或隐藏

打开 Xcode,从 File › New › Project… 选择 macOS › App,就会生成一个新应用的模板。Interface 选 SwiftUI,Language 保持 Swift 即可。按 ⌘R(或左上角的 ▶)运行,什么都不写也会打开一个显示“Hello, world!”的窗口。这就是 STEP 0 的终点。

  • 在左边选文件,在中间写,在右边的画布看结果。这个往返就是基本功
  • 下方的调试区会显示 print() 的输出和错误。看到红色错误别怕,先把全文读一遍,养成习惯

怎样和 AI 助手相处

Xcode 内置了编程用的 AI。到了 Xcode 27,它能以“智能体”的方式工作:先定计划再写代码,并通过构建和预览检查结果。很方便,但学习阶段,与其“让它写”,不如“让它讲”,更能长本事。问它“这个错误是什么意思?”“这一行在做什么?”,对它给出的代码逐行阅读,不懂的词去官方文档核实。

三

STEP 1 ── Swift 基础

CORE

做界面之前,先熟悉Swift 这门语言本身。跳过这一步,SwiftUI 的代码看起来就会像“咒语”。

要掌握的知识

  • 常量与变量(let / var)── 默认用 let,只有需要改的才用 var
  • 类型与类型推断 ── Int、Double、String、Bool。不写类型,Swift 也能推断
  • Optional(?)── 用类型表达“可能没有值”,用 if let / guard let 安全取出。这是 Swift 的第一个难关
  • 集合 ── 数组(Array)、字典(Dictionary)、集合(Set)
  • 控制流 ── if、switch、for-in、while
  • 函数与闭包 ── 参数与返回值。“当场传进去的小函数”就是闭包,SwiftUI 的按钮就靠它工作
  • 结构体与类 ── struct 是值类型(会被复制),class 是引用类型(会被共享)。SwiftUI 里 struct 是主角
  • 枚举(enum)── 表示种类或状态,常与 switch 搭配
  • 协议 ── “我具备这个能力”的约定。View 和 Codable 都是协议
  • 错误处理 ── throws / try / do-catch。文件操作一定会用到
  • 异步处理入门 ── async / await,“等待耗时操作”的写法
let appName = "KIJI" var count = 0 count += 1 // String? 表示“字符串,或者什么都没有” var nickname: String? = nil if let nickname { print("Hello, \(nickname)") } else { print("nickname is not set") }

Optional 是一开始最容易困惑的地方。String? 表示“字符串,或者什么都没有”。它的作用是在运行之前,就挡住“不小心用了可能不存在的值”这类事故。

并发只需要“入门”

从 Swift 6 起,编译器会严格检查多个任务同时运行时的安全性,一开始可能会被陌生的警告吓到。不过,用 Xcode 26 及以后版本新建的项目,默认让代码运行在主线程(MainActor)上,初学者不容易撞上这堵墙。STEP 1 能读懂 async / await 就够了。

教材

Swift 官方指南 The Swift Programming Language 的 A Swift Tour 页面,左侧是目录,右侧是 Hello, world! 示例
The Swift Programming Language(6.4)的“A Swift Tour”。左侧目录就是学习顺序(来源:docs.swift.org,2026 年 9 月 26 日截图)
  • The Swift Programming Language(官方・英文)── 语言的官方指南。先通读开头的“A Swift Tour”,其余当字典查
  • Xcode 的 Playground ── 写几行就能马上看到结果,适合确认语法
  • 中文资料 ── 社区有官方指南的中文译本,可作辅助。但版本往往落后于原文,遇到出入以原文为准

练习作品:命令行单位换算

在 Xcode 里选择 macOS › Command Line Tool,可以做一个没有界面的程序。试着用 enum、switch 和函数,写“厘米换英寸”“摄氏换华氏”之类的换算。读取键盘输入的 readLine() 返回的是 Optional。想一想如果输入的不是数字怎么办?,Optional 的意义就真正明白了。

四

STEP 2 ── SwiftUI 基础

CORE

SwiftUI 不直接改写界面。先声明“状态(数据)是这样时,界面就是那样”,改变状态后,SwiftUI 会重新绘制界面。习惯这种思路,就是 STEP 2 的目标。

SwiftUI 的思路 ── 状态决定界面

@State var count状态(数据) var body根据状态计算界面 界面只重绘变化的部分 count += 1按钮只改状态 不直接改界面 改了状态,界面自动跟上

按钮不改文字,只改状态,剩下的交给 SwiftUI 重绘

import SwiftUI struct CounterView: View { @State private var count = 0 var body: some View { VStack(spacing: 12) { Text("Count: \(count)") .font(.title) Button("+1") { count += 1 } } .padding() } } #Preview { CounterView() }

加在 count 上的 @State,意思是“这个值一变就重绘界面”。按钮只做 count += 1,哪里都没有改写显示的代码,数字却会 1、2、3 地涨上去。

要掌握的知识

  • View 与修饰符 ── 给 Text、Button 等组件链式加上 .font()、.padding() 来调整外观。顺序不同,结果不同,要留意
  • 布局 ── VStack(纵向)、HStack(横向)、ZStack(叠放)、Spacer、Grid
  • 常用组件 ── Text、Image(SF Symbols 图标)、Button、Toggle、TextField、Picker、Slider
  • 列表 ── 用 List 和 ForEach 把数组直接变成行。用来区分各行的 Identifiable 也在这里学
  • 状态放在哪 ── 只在本界面用就用 @State;交给子界面去改就用 @Binding;多个界面共享的数据,整理进 @Observable 类
  • 界面切换 ── Mac 上常用带侧边栏的 NavigationSplitView
  • 预览 ── 写上 #Preview,不运行也能在画布上看到界面

看教材先看“是哪一年的”

Apple 的 SwiftUI Tutorials 页面,显示着“不再展示当前 SwiftUI 与 Xcode 做法”的提示
旧版“SwiftUI Tutorials”。开头写着“不再展示当前 SwiftUI 与 Xcode 的做法”(来源:Apple Developer,2026 年 9 月 26 日截图)

一搜索,Apple 的“SwiftUI Tutorials”往往排在前面。但这个页面现在写着“不再展示当前 SwiftUI 与 Xcode 的做法”,并引导大家去看新的 Develop in Swift Tutorials。SwiftUI 的写法每年都在小幅变化,无论文章还是视频,都要养成先确认发布日期和对应 Xcode 版本的习惯。

Apple 的 Develop in Swift Tutorials 首页
现行的官方教程“Develop in Swift Tutorials”(来源:Apple Developer,2026 年 9 月 26 日截图)

Develop in Swift 是官方教材,从 Xcode 的用法到 SwiftUI、数据存储(SwiftData)、测试、发布,边动手边按顺序学。题材以 iPhone 应用为主,但 SwiftUI 的思路和写法在 Mac 上几乎一样。先把 SwiftUI 的章节学完,Mac 特有的部分在下一步 STEP 3 补上。

练习作品:把单位换算做成窗口

给 STEP 1 的单位换算加上界面:在 TextField 输入数字,用 Picker 选单位,结果的 Text 当场变化。计算部分应该能直接复用 STEP 1 的代码。把界面和计算分到不同文件里,之后写测试会轻松很多。

五

STEP 3 ── Mac 特性

CORE

把 iPhone 应用放大,并不会变成 Mac 应用。菜单栏、多个窗口、设置窗口、键盘快捷键——掌握 Mac 用户默认期待的这些规矩。做小工具,真正的主场从这里开始。

Mac 应用的骨架 ── App 与 Scene

@main struct MyApp: App 应用入口,在这里并列 Scene WindowGroup普通窗口 Settings设置窗口(⌘ ,) MenuBarExtra常驻菜单栏 ContentView SettingsView MenuView Scene 是“场景”,虚线框的 View 是其内容。需要哪个 Scene 就写哪个 加上 .commands { },可以往屏幕顶部的菜单里添加项目

去掉 WindowGroup、只留 MenuBarExtra,就成了只在菜单栏里的应用

要掌握的知识

  • App 与 Scene ── 在应用入口(@main 的 App)里,并列窗口等“场景”
  • WindowGroup 与 Window ── 同类窗口能开多个的是 WindowGroup,只有一个的专用窗口是 Window
  • Settings ── 只要写上,应用菜单里就会出现“设置…”(⌘ ,)
  • MenuBarExtra ── 常驻屏幕右上角菜单栏的图标,macOS 13 起可用。设为 .menuBarExtraStyle(.window) 可以弹出类似气泡的小窗口
  • 不在 Dock 显示 ── 把 Info.plist 的 LSUIElement 设为 YES,就不会出现在 Dock 和应用切换器里。菜单栏工具的标配
  • 菜单与快捷键 ── 用 .commands 往顶部菜单加项目,用 .keyboardShortcut 分配按键
  • 工具栏与侧边栏 ── .toolbar 与 NavigationSplitView。窗口初始大小用 .defaultSize 决定
  • 拖放与选择文件 ── 从访达拖入文件(.dropDestination),用“打开”面板选择(.fileImporter)
  • 与 AppKit 衔接 ── SwiftUI 没有的功能用 AppKit:剪贴板的 NSPasteboard、打开应用和文件的 NSWorkspace 等。还可以用 NSViewRepresentable 把 AppKit 组件嵌进 SwiftUI
import SwiftUI @main struct StampApp: App { var body: some Scene { MenuBarExtra("Stamp", systemImage: "clock") { Button("Copy timestamp") { let text = Date.now.formatted() NSPasteboard.general.clearContents() NSPasteboard.general.setString(text, forType: .string) } .keyboardShortcut("t") Divider() Button("Quit") { NSApplication.shared.terminate(nil) } .keyboardShortcut("q") } } }

就这么几行,菜单栏里会出现一个时钟图标,选“Copy timestamp”,当前日期时间就进了剪贴板。NSPasteboard 是 AppKit 的组件,但在 Mac 上可以直接从 SwiftUI 调用。因为这个应用没有窗口,退出按钮也要自己放。

了解 Mac 的规矩

Apple 的 Human Interface Guidelines(HIG)整理了 Mac 应用应有的行为:菜单的排列、快捷键惯例(⌘ , 打开设置、⌘ W 关闭、⌘ Q 退出)、窗口的处理。按标准的方式用标准组件,外观和操作自然就有 Mac 味。macOS 26 起的新设计(Liquid Glass),标准组件只要用新 SDK 重新构建就会体现出来。

练习作品:常驻菜单栏的计时器

做一个 25 分钟工作、5 分钟休息循环的番茄钟。在菜单栏显示剩余时间,点击开始和停止;再用设置窗口(Settings)改时长,STEP 3 的要素就基本齐了。

六

STEP 4 ── 数据与存储

CORE

工具要能“接着上次用”才算好用。学习把设置、用户数据、用户选择的文件,各自存到合适的地方。

要保存的东西用什么例子
小的设置值@AppStorage(UserDefaults)显示格式、功能开关
成块的数据Codable + JSON 文件常用语列表
大量记录的检索与排序SwiftData(macOS 14 及以后)几千条历史
用户的文件FileManager、文件面板批量重命名的对象
struct Snippet: Codable, Identifiable { var id = UUID() var title: String var text: String } func save(_ items: [Snippet], to url: URL) throws { let data = try JSONEncoder().encode(items) try data.write(to: url, options: .atomic) } // 设置只要加在变量上,就会自动保存和恢复 @AppStorage("showInMenu") var showInMenu = true

要掌握的知识

  • @AppStorage ── 加在变量上,值就会自动保存,下次启动也能恢复
  • Codable ── 把自定义的 struct 转成 JSON(编码),再从 JSON 还原(解码)
  • 存放位置 ── 应用数据放进“Application Support”文件夹。不要擅自在文稿或桌面上创建文件
  • FileManager 与 URL ── 列出、复制、移动文件。删除时以“移到废纸篓”(trashItem)为主,避免无法挽回的删除
  • SwiftData ── 给类加上 @Model,就能像数据库一样保存和查询。数据一多就用它
  • 书签 ── 在沙盒里,即使是用户选过一次的文件夹,下次启动也读不了。要记住它,得保存“安全范围书签”

练习作品:常用语(片段)本

把常用的句子登记好,从菜单栏选中就进入剪贴板。列表存成 JSON;用菜单还是用小窗口显示列表,由 @AppStorage 的设置来切换。

七

STEP 5 ── 系统集成

CORE

小工具的价值,取决于能和系统功能结合得多深。同时,从这里开始就要考虑“在哪里发布”,因为发布方式会改变你能做的事。

App Sandbox ── 把应用装进盒子

通过 Mac App Store 发布的应用,必须在名为 App Sandbox 的“盒子”里运行。盒子里的应用,只能碰用户选择的文件和被允许的功能(网络、摄像头等)。需要的功能在 Xcode 的 Signing & Capabilities 里声明(这种声明叫 Entitlements)。

对用户是安全,对开发者是限制。比如移动其他应用的窗口、读取其他应用的内容这类工具,要用辅助功能(Accessibility)API,而它在沙盒里基本用不了。这类工具通常在自己的网站发布。

要掌握的知识

想做的事用什么注意
登录时启动SMAppService(macOS 13 及以后)做成由用户自己打开的设置
读写剪贴板NSPasteboard没有变化通知,要定期检查 changeCount
发通知UserNotifications首次需要请求许可
打开应用・文件・URLNSWorkspace—
从快捷指令或 Spotlight 调用App Intents把功能公开为“操作”
全局快捷键全局热键(旧 API 或封装它的包)选不与其他应用冲突的键
操作其他应用的窗口辅助功能 API需要用户授权,放在沙盒外
读取屏幕内容ScreenCaptureKit需要录屏权限
import ServiceManagement // 由“登录时启动”的开关调用 func setLaunchAtLogin(_ on: Bool) throws { if on { try SMAppService.mainApp.register() } else { try SMAppService.mainApp.unregister() } }

先说明理由,再请求权限

在 macOS 上,辅助功能、录屏、完全磁盘访问等权限,由用户在“系统设置 › 隐私与安全性”里逐项批准,应用无法擅自获得。先在界面上说明为什么需要这个权限,再去请求,这是礼貌,也决定了用户会不会继续用下去。

练习作品:剪贴板历史

记录最近复制的最多 50 条文字,可以从菜单栏重新选取。定时检查 NSPasteboard 的 changeCount,一有变化就加进历史。存储用 STEP 4 的 SwiftData,登录启动用 SMAppService。也想一想密码之类的敏感信息该怎么处理(比如看到密码管理器加上的“请勿记录”标记,就不写进历史)。

八

STEP 6 ── 质量打磨

CORE

质量不是最后一口气补上的东西,而是从 STEP 1 起一点点并行养成的。

调试

  • 先用 print() 输出值来确认。熟练之后,用断点(点击行号)让程序停下,查看当时的变量
  • 停下后在调试区输入 po 变量名,就能看到内容
  • 运行卡顿、内存一直涨,就用 Instruments(Time Profiler、Leaks)找原因

测试

import Testing @testable import Renamer @Test func numbersFileNames() { let name = FileNamer.make(base: "photo", index: 3) #expect(name == "photo-003") }

加上 @Test 的函数就是测试,在 #expect 里写“应该是这样”(上面的 FileNamer 是举例用的类型)。先测计算、文件名拼接这类“逻辑”,而不是界面,比较容易上手。STEP 2 把界面和计算分开,在这里就派上用场了。

为用户着想

  • 无障碍 ── 只有图标的按钮要加 .accessibilityLabel。用旁白(VoiceOver)操作一遍
  • 多语言 ── 界面文字会自动汇集到 String Catalog(.xcstrings)。Xcode 27 还能让智能体帮忙翻译
  • 深色模式 ── 颜色不用固定值,用系统颜色或资源目录里的颜色
  • 图标 ── 用 Icon Composer 制作应用图标

用 Git 留下历史

每个作品建一个仓库,能跑就小步提交。改坏了也能回退的安心感,直接关系到学习的速度。

九

STEP 7 ── 发布

CORE

在自己的 Mac 上能跑,和在别人的 Mac 上能跑,是两回事。经过 Apple 的签名与检查,才会成为在别人 Mac 上也能顺利打开的应用。

发布的三条路

做好的应用 MyApp.app ① 只在自己的 Mac 上用免费 在 Xcode 里运行即可,无需注册和费用拿到别的 Mac 上会被警告,无法直接打开 ② 通过 Mac App Store 发布每年 99 美元 必须沙盒・需要审核・更新也走 App Store自动启动、自动更新等有专门规定 ③ 在自己的网站发布每年 99 美元 Developer ID 签名 → 公证(Apple 自动检查)沙盒可选,更新可用 Sparkle 等

②和③需要加入 Apple Developer Program(每年 99 美元;各地区以本地货币定价,价格可能调整)

在自己的网站发布(Developer ID + 公证)

通过网站或 GitHub,以 .dmg 或 .zip 分发。公证(notarization)是 Apple 的服务器自动检查应用(有没有恶意软件、签名是否有问题),合格后签发“票据”的机制。它和 App Store 的审核不是一回事,通常一小时内就会返回结果。

① 在 Xcode 用 Product › Archive 生成归档
② 打开 Window › Organizer,点 Distribute App
③ 分发方式选 Developer ID,用 Upload 提交公证
④ 通过后票据会自动附加上去,导出后即可分发

公证要求用 Developer ID 证书签名,并启用 Hardened Runtime(运行时保护)。推送更新,常见做法是集成 Mac 应用中广泛使用的开源框架 Sparkle。

通过 Mac App Store 发布

对用户最放心,支付和更新也可以交给商店。相应地,审核指南(2.4.5)里有 Mac 专属的规定:

  • 必须在 App Sandbox 中运行
  • 未经用户同意,不得在登录时自动启动
  • 只能通过 App Store 更新,不能自带自动更新
  • 不得请求管理员权限,不得要求输入许可证密钥

如 STEP 5 所说,有些工具在沙盒里做不出来。想做的东西一旦确定,就尽早决定在哪里发布,免得之后推倒重来。

练习作品:请朋友在他的 Mac 上打开

做一个已公证的 .dmg 发给朋友,请他打开。能否毫无警告地打开?不加说明,他能否看懂怎么用?第一次接触的人的反应,是最好的测试。

十

坚持下去的窍门

  • 每天都碰一点 ── 与其每周一次 1 小时,不如每天 20 分钟。手会记住 Xcode
  • 错误信息读全文 ── 即使是英文,也翻译着读一遍,答案常常就写在里面
  • 用一手资料核实 ── 搜到的文章,去 Apple 文档确认一下。越旧的文章,写法越可能已经变了
  • 把 AI 当家教 ── 让它讲解、让它比较,最后自己动手写。粘贴进来的代码,要做到能逐行解释
  • 做自己每天会用的东西 ── 解决自己不便的工具,才有动力做完
  • 把作品公开 ── 代码放到 GitHub,附上简短说明。会连到下一份工作和新的伙伴

想继续深入的话

  • WWDC 的专题视频 ── 每年 6 月发布,新功能的一手信息。先从“What's new in SwiftUI”看起
  • Hacking with macOS(SwiftUI Edition) ── 专讲 Mac 的实战书(英文・付费),边做边学的结构
  • 开源的 Mac 应用 ── 在 GitHub 上找 Swift 写的菜单栏应用来读。真实的代码是最好的教材

结 — 一页总结

  1. 目标是“从菜单栏或快捷键调用的小工具”。工具选 Swift + SwiftUI,不够再用 AppKit。
  2. STEP 0:Apple 芯片的 Mac 加免费的 Xcode 就能开始。收费注册到发布时才需要。
  3. STEP 1–2:Swift 的难关是 Optional。SwiftUI 是改了状态,界面自动跟上。
  4. STEP 3:App 与 Scene。用 MenuBarExtra・Settings・菜单与快捷键做出 Mac 味。
  5. STEP 4–5:存储位置的区分,以及与系统的结合。沙盒的限制决定发布方式。
  6. STEP 6:调试、测试、无障碍,从一开始就并行。
  7. STEP 7:自己网站发布靠 Developer ID 与公证,App Store 发布靠沙盒与审核。
  8. 看教材要看发布日期和对应版本。对 AI,与其让它写,不如让它讲。

参考

开发指南 No.1 — Mac 应用开发入门路线图 / 2026.09

← 返回文章列表