前言
作为 iOS 开发者,我每天都在跟 CocoaPods、Fastlane 这些命令行工具打交道。但昨晚加班到凌晨两点时,我突然想到:为啥不自己写个命令行工具来解决重复性工作呢?用 Swift 来写命令行工具听起来很高级,但其实超级简单。今天就跟大家分享下,如何用 Swift Package Manager 快速创建一个命令行工具,解放双手。
何时需要命令行工具?
说实话,我之前一直认为命令行工具是那些 Linux 极客的专属玩具。直到有一天,我发现自己每周都要在 Sketch 里重复同样的操作:打开模板、修改文本、导出、重命名……这种操作虽然只要 3-5 分钟,但每次都要动鼠标切来切去,烦死了!想想看,你日常工作中是不是也有类似的场景:
1.每次发版时要重复一堆操作(改版本号、打包、上传、发通知...)
2.经常要处理同一类型的图片或资源文件
3.总是要修改一堆配置文件
4.隔三差五要调用同一个 API 测试功能
如果你点头了,那就接着往下看!
利用 Swift Package Manager 创建命令行工具
我决定创建一个名为"Banner"的工具,用来自动生成博客配图(因为我懒得每次都打开 Photoshop)。顺便说一句,起个酷炫点的名字很重要,毕竟这可能是你唯一的个人产品。
首先创建项目(三行命令搞定):
$ mkdir Banner
$ cd Banner
$ swift package init --type executable
执行完毕后,SPM 会生成一堆文件(想必你已经很熟悉了)。此时 main.swift 里就一个无聊的 "Hello, world!" 打印语句。
$ swift build
$ swift run
Hello, world!
嗯,很好,跑起来了,但功能太弱鸡了。接下来咱们加点料!
加入 ArgumentParser 框架
要让命令行工具接收参数,我们需要苹果开源的 ArgumentParser 框架。这框架简直是神器,用了属性包装器(Property Wrappers),让参数处理变得超级优雅。
打开 Package.swift,加入依赖:
let package = Package(
name: "Banner",
products: [
.executable(name: "banner", targets: ["Banner"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1")
],
targets: [
.target(name: "Banner", dependencies: ["ArgumentParser"]),
.testTarget(name: "BannerTests", dependencies: ["Banner"]),
]
)
然后在 main.swift 中引入框架:
import ArgumentParser
print("Hello, world!")
创建主命令
接下来就是写具体的代码了。首先创建主命令结构体:
import ArgumentParser
struct Banner: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "懒人必备:一键生成博客配图的命令行工具",
subcommands: [Generate.self])
init() { }
}
Banner.main()
我把描述写得有点调皮,但谁说命令行工具就一定要正经巴交的?这结构体继承自 ParsableCommand 协议,配置了命令描述和子命令。最后一行 Banner.main() 是整个程序的入口。
添加子命令和参数
下面是实现 Generate 子命令的代码,这部分最关键:
struct Generate: ParsableCommand {
publicstaticlet configuration = CommandConfiguration(abstract: "告诉我你想要啥样的配图,我来生成")
@Argument(help: "博客标题(必填)")
privatevar title: String
@Option(name: .shortAndLong, default: nil, help: "博客周数,用于文件命名")
privatevar week: Int?
@Flag(name: .long, help: "啰嗦模式,显示更多日志")
privatevar verbose: Bool
func run()throws {
if verbose {
let weekDescription = week.map { "和第 \($0) 周" }
print("正在为标题「\(title)」\(weekDescription ?? "") 创建配图...")
}
// 堆代码 duidaima.com
// 真正的生成逻辑...
// 我用了 NSImage 和 Core Graphics 来处理图片
// 限于篇幅,这部分代码就不贴了
}
}
这里定义了三种不同类型的参数:
@Argument:必填参数,没它玩不转(标题)
@Option:可选参数,加点配置(周数)
@Flag:开关参数,要么开要么关(详细日志)
run() 方法包含了实际执行的代码。我在实际项目中加了大概 50 行左右的图片处理代码,就不全贴出来了,毕竟重点是展示命令行工具的框架结构。
试用一下
现在就能用起来了!先看看帮助信息:
$ swift run banner
概述: 懒人必备:一键生成博客配图的命令行工具
用法: banner <子命令>
选项:
-h, --help 显示帮助信息
子命令:
generate 告诉我你想要啥样的配图,我来生成
再看看 generate 子命令的帮助:
$ swift run banner generate --help
概述: 告诉我你想要啥样的配图,我来生成
用法: banner generate <title> [--week <week>] [--verbose]
参数:
<title> 博客标题(必填)
选项:
-w, --week <week> 博客周数,用于文件命名
--verbose 啰嗦模式,显示更多日志
-h, --help 显示帮助信息
实际使用一下:
$ swift run banner generate "用 Swift 创建命令行工具,不用写重复代码" -w 19 --verbose
正在为标题「用 Swift 创建命令行工具,不用写重复代码」和第 19 周创建配图...
配图已保存到 ~/Desktop/banner_week19.png
顺便说一下,这篇文章的封面就是用这个工具生成的,我用这个工具每周能节省 15 分钟左右。虽然不多,但很爽,而且写代码的过程本身就很有趣。
安装到系统中
最后一步是把工具安装到系统里,这样就能随时随地用了(不用每次都 swift run):
$ swift build --configuration release
$ cp -f .build/release/banner /usr/local/bin/banner
这两行命令执行完,你就可以在任何目录下直接使用 banner 命令了!有没有很酷?
实际案例分享
除了我自己做的这个小工具外,社区里有不少实用的 Swift 命令行工具:
Poes:一键发送推送到 iOS 模拟器(测试推送时超级方便)
GitBuddy:自动处理版本发布、生成更新日志,再也不用手动写 release notes
XcodeGen:从 YAML 生成 Xcode 项目,团队协作必备
Sourcery:自动生成样板代码
这周末有空的话,我打算再写一个自动整理桌面文件的工具,因为我桌面永远乱七八糟...
结语
Swift 命令行工具最大的魅力就是可以用熟悉的语言,解决日常烦恼。你不需要学 Python、Ruby 或 Shell,就能创建出强大的自动化工具。等你掌握了这个技能,你会发现越来越多可以自动化的场景。那种把一个重复性工作"一劳永逸"的感觉,比写五个页面的成就感还要强!对了,你有什么烦人的重复性任务可以自动化吗?评论区见!