【注意】最后更新于 May 15, 2017,文中内容可能已过时,请谨慎使用。
背景
- Xcode7插件工具Alcatraz
开发者可以在Xcode运行的时候通过注入代码去实现插件的功能。插件可以在一个Alcatraz这个优秀的APP上面提交和分发。
以往Xcode插件开发在没有官方支持的情况下,提供了Xcode-Plugin-Template和各种dump好的头文件,我们仍然需要在没有文档的情况下做各种猜测和hook。关于插件的开发,可以看看这篇文章。
- Xcode8编辑源码的插件Xcode source editor extensions
Xcode 8验证每个库和包,以防止恶意代码未经您的许可运行。苹果公司在今年的WWDC上宣布了可以通过开发
Xcode source editor extensions
来扩展以下三个功能:
- Add commands to the source editor // 给Xcode的代码编辑器扩展一些命令(在Editor菜单下增加额外的菜单)
- Edit text //通过这些命令对源代码进行编辑
- Change selections //对选中文本的编辑功能
源码编辑器插件
添加命令菜单
通过Info.plist中添加菜单
打开Info.plist
,展开NSExtension
至XCSourceEditorCommandDefinitions
,命令菜单的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <key>NSExtensionAttributes</key>
<dict>
<key>XCSourceEditorCommandDefinitions</key>
<array>
<dict>
<key>XCSourceEditorCommandClassName</key>
<string>$(PRODUCT_MODULE_NAME).SourceEditorCommand</string>
<key>XCSourceEditorCommandIdentifier</key>
<string>com.xt.APPlugins.AutoComment.SourceEditorCommand</string>
<key>XCSourceEditorCommandName</key>
<string>Source Editor Command</string>
</dict>
</array>
<key>XCSourceEditorExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).SourceEditorExtension</string>
</dict>
|
其中:
- XCSourceEditorCommandClassName指向命令的处理类,该类实现XCSourceEditorCommand。
- XCSourceEditorCommandIdentifier是命令的唯一标识,通常我们会对一组命令设置同一个XCSourceEditorCommand,在invocation中获取此标识做区分处理。
- XCSourceEditorCommandName是命令在菜单栏展示的菜单名称。
通过代码定制命令菜单
此外,菜单项是动态加载的,可以实现SourceEditorExtension.swift
的commandDefinitions:
实现
SourceEditorExtensio
n用来管理extension的生命周期相关,SourceEditorCommand
则用来处理具体的命令。
这会覆盖掉Info.plist中的定义。
1
2
3
4
5
6
7
8
9
10
| //1. 启动extension被调用,自定义相关操作
func extensionDidFinishLaunching(){}
var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: AnyObject]] {
// If your extension needs to return a collection of command definitions that differs from those in its Info.plist, implement this optional property getter.
////commandDefinitions属性的getter方法可以动态的展示或是隐藏特定的指令
return [[.classNameKey: "SourceEditorCommand",
.identifierKey: "CustomIdentifier",
.nameKey: "CustomeName"]]
}
|
命令插件功能实现
打开SourceEditorCommand.swift
,实现perform(with:completionHandler:)
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| class SourceEditorCommand: NSObject, XCSourceEditorCommand
{
//当通过Xcode菜单键调用插件时调用,实现插件功能的主体
func perform(with invocation: XCSourceEditorCommandInvocation,
completionHandler: @escaping (Error?) -> Void ) -> Void {
// 正则匹配含有 闭包 的文本
var updatedLineIndexes = [Int]()
for lineIndex in 0 ..< invocation.buffer.lines.count
{
let line = invocation.buffer.lines[lineIndex] as! NSString
do {
let results = try findClosureSyntax(line: line)
//简化所有闭包语法格式:移除闭包里面括号
_ = results.map { result in
let cleanLine = line.remove(characters: ["(", ")"], in: result.range)
updatedLineIndexes.append(lineIndex)
invocation.buffer.lines[lineIndex] = cleanLine
}
} catch {
completionHandler(error as NSError)
}
}
completionHandler(nil)
}
//使用正则表达式去遍历每一行代码是否含有闭包
func findClosureSyntax(line:NSString) throws ->[NSTextCheckingResult]
{
let regex = try NSRegularExpression(pattern: "\\{.*\\(.+\\).+in", options: .caseInsensitive)
let range = NSRange(0 ..< line.length)
return regex.matches(in: line as String, options: .reportProgress, range: range)
}
}
|
XCSourceEditorCommandInvocation
的buffer
提供了当前选中的文件源代码中的信息,包括文件的lines
, selections
, indentationWidth
等等,具体作用可以查看WWDC视频或者苹果的文档查看,在此就不一一介绍了。
测试
- 选择scheme后编译运行自定义的extensions插件
- 选择测试安装插件的Xcode
- 点击run按钮会单独启动一个全新的Xcode
在新的Xcode实例中,创建一个新的工程或是打开一个存在的工程。
- 然后执行
Editor > Clean Closure > Source Editor Command
,需要确保在当前的文件里面含有一个闭包。这样就可以看到如下的效果,刚才开发的extension工作了!
命令快捷键
设置快捷键去自动调用Clean Syntax命令
- 打开Xcode的Preferences,选择Key Bindings ;
- 搜索Clean Syntax,点击右边然后输入快捷键,例如:Command-Alt-Shift-+。
WWDC2016之初识Xcode Source Editor Extension
xcode常用的插件清单
使用 Xcode Source Editor Extension开发Xcode 8 插件
详解一步步实现Xcode 8 插件