O. 背景
Xcode 8前,開發(fā)者可以通過運行時注入代碼來實現(xiàn)添加插件,甚至在著名的插件管理工具Alcatraz上提交和分發(fā)插件。但是,Xcode 8成為這種開始方式的終結者,因為它提供了自家的Xcode Source Editor Extension方式開發(fā)插件。
接下來,你將了解到:
- 如何創(chuàng)建Xcode Source Editor Extension(插件)
- 如何測試插件
- 如何快速進行插件開發(fā)
- 開發(fā)完成后如何讓小伙伴們使用
- 插件的優(yōu)勢和局限
一. 著手創(chuàng)建插件
1. 首先,創(chuàng)建一個macOS下的Cocoa Application工程,作為插件的母體:

2. 設置工程參數(shù),完成工程創(chuàng)建。
這里工程名: SourceEditorExtensionDemo 語言: Swift

3. 添加Target

選擇Xcode Souce Editor Extension:

配置Target名稱等信息,F(xiàn)inish完成設置:

此時彈出是否啟用此Target的Scheme對話框,點擊Activate按鈕啟用:

4. 文件結構

二. 測試插件
1. 配置簽名(不配置的后果是,測試時不顯示插件按鈕)

配置好簽名后,兩個Target: SourceEditorExtensionDemo 和 MyPlugin 的簽名(Siging)要一致,如圖:


2. 運行插件

點擊左側運行按鈕 或者 Command + R運行插件,彈出下面的對話框,選擇Xcode(如果有多個Xcode,選擇Xcode 8以上的版本),并點擊Run按鈕:

然后Dock中出現(xiàn)一個黑色的東東,沒錯,黑色的Xcode:

雙擊右側的一個工程,就可以假裝把插件安裝到Xcode里,進行插件調試了(插件按鈕放在Editor最下面):

(關于插件,因為我們啥都沒寫,所以點擊插件按鈕暫時啥也沒有 = =)
三. 認識并開發(fā)插件
1. 插件相關文件
SourceEditorExtension.swift 和插件的生命周期和配置有關,實現(xiàn)了
XCSourceEditorExtension協(xié)議中的一個方法extensionDidFinishLaunching()和一個變量commandDefinitions,默認是被注釋掉的。方法
extensionDidFinishLaunching()是指剛剛加載好插件但還未點擊插件按鈕時,可以執(zhí)行某些準備工作。-
變量
commandDefinitions返回字典類型的數(shù)組,可以為每個插件重寫名字、標識符和自定義類名等信息,相當于設置后面要介紹的的Info.plist文件中對應的XCSourceEditorCommandName、XCSourceEditorCommandIdentifier和XCSourceEditorCommandClassName信息。
SourceEditorExtension.swift -
SourceEditorCommand.swift 實現(xiàn)了
XCSourceEditorCommand協(xié)議中的perform方法,點擊插件按鈕所執(zhí)行的具體邏輯就是在這個方法中完成的,因此是實現(xiàn)插件功能的核心。
SourceEditorCommand.swift -
Info.plist 其中,每個插件的名字、標識符和自定義類名分別對應
XCSourceEditorCommandName、XCSourceEditorCommandIdentifier和XCSourceEditorCommandClassName信息。XCSourceEditorExtensionPrincipalClass對應插件的默認實現(xiàn)類:SourceEditorExtension.swift。
Info.plist
2. 制作一個簡單的插件
在SourceEditorCommand.swift文件中書寫如下代碼:
class SourceEditorCommand: NSObject, XCSourceEditorCommand {
func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void {
// Implement your command here, invoking the completion handler when done. Pass it nil on success, and an NSError on failure.
// 第一個選中區(qū)域
let firstSelectObject: XCSourceTextRange = invocation.buffer.selections.firstObject as! XCSourceTextRange
// 開始刪除的位置
let start = firstSelectObject.start.line
// 要刪除的行數(shù)
let end = firstSelectObject.end.line
// 設置刪除的范圍
let deleteRange = IndexSet(integersIn: start...end)
// 刪除對應行的代碼
invocation.buffer.lines.removeObjects(at: deleteRange)
completionHandler(nil)
}
}
然后運行插件,即可通過插件按鈕實現(xiàn)對選中的代碼進行整行刪除。
原理分析:
- 插件一旦運行起來 -> 立即調用SourceEditorExtension.swift中的 extensionDidFinishLaunching() 方法做準備工作 -> 同時檢查是否自定義
commandDefinitions變量,以確定實現(xiàn)插件的類名、插件名字和唯一標識符信息 -> 如果沒有自定義此變量,直接讀取 Info.plist 文件中的配置。 - 點擊插件中的按鈕 -> 直接執(zhí)行我們自定義的或者 Info.plist 中 XCSourceEditorCommandClassName 對應的類文件中的
perform方法,該方法中的參數(shù)invocation為XCSourceEditorCommandInvocation類型,包含我們點擊的插件的唯一標識符屬性,以及我們在點擊插件前光標選中的區(qū)域等信息。 -
XCSourceEditorCommandInvocation有個XCSourceTextBuffer類型的buffer屬性,此類型有兩個比較常用的屬性lines和selections,分別代表我們點擊插件前光標所在文件的所有行 和 光標選中的區(qū)域。 - 多個插件可以對應一個類實現(xiàn)文件,在代碼中,用唯一標識符來區(qū)分不同的插件。
四. 讓插件及時傳播給小伙伴們使用
開發(fā)時,插件只要運行起來,我們就可以在Xcode菜單欄中的Editor最下面看到插件名字和使用,但一旦停止運行,插件并不會常駐Xcode供我們使用。
但是,我們可以這樣做: 找到Products文件夾下生成的的.app文件

右鍵點擊此文件 -> “在Finder中顯示” -> 將這個.app文件拷貝到你或者小伙伴電腦上的"應用程序"里 :

在“應用程序”中雙擊.app文件運行。然后,打開“系統(tǒng)偏好設置” -> "擴展" -> "Xcode Source Editor" -> 確認插件名字前已打鉤 -> 此時Xcode中菜單欄Editor下的插件雖然顯示,但是為灰色,無法點按,所以要 -> 重啟Xcode -> 大功告成!

我們也可以為插件添加快捷鍵: Xcode -> "Preferences" -> "Key Bindings" -> 搜索插件名字 -> 添加對應的快捷鍵:

五. 優(yōu)缺點總結
優(yōu):
- 支持上架到Mac App Store
- 每個Extension運行在獨立的進程,如果它出了事故,不會引起Xcode的崩潰
- 可以分配快捷鍵
缺:
- 無UI相關接口
- 只能做文本相關處理
總之: 第一版插件 too young too simple,但我們要對蘋果心懷期待,假裝未來很美好~


