Xcode Source Editor Extension
其實官方還是宣揚了一些優點的,其中最顯著的莫過於可以上架 Mac App Store,但這點優點實在是無法彌補他的弱雞。他能做的事情幾句話就可以說完,開發一個這樣的插件是極其的簡單。簡單說吧,一個插件能做下面的事情:
- 獲取 Xcode 正在編輯的文本
- 獲取所有的選中區域
- 替換 Xcode 正在編輯的文本
- 選中 Xcode 正在編輯的文本
- 在 Xcode 的 Editor 菜單裡面給你的插件生成一個子菜單,用於調用插件
- 可以在 Xcode 的 Key Binding 裡面給插件分配一個快捷鍵
沒了,這就是他全部能做的事情,下面我通過一個選中文字轉換成 Base64 編碼的例子來簡單介紹一下。
你需要在一個現有的或者新建的 macOS 項目裡面新建一個 Xcode Source Editor Extension target,然後理論上這個時候已經可以有插件的效果了,但是今天我試了很多次就是不行,其實沒有什麼特別的原因,就是因為 Xcode 目前的階段極其不穩定,我沒寫錯任何東西。
有幾個核心的觀念要介紹一下,首先,實現了 XCSourceEditorExtension 介面的類,用於管理插件的生命周期或者對 Command 進行動態配置,說的很牛逼其實一共兩個方法。
- (void)extensionDidFinishLaunchingn{n // If your extension needs to do any work at launch, implement this optional method.n}nn- (NSArray <NSDictionary <XCSourceEditorCommandDefinitionKey, id> *> *)commandDefinitionsn{n // If your extension needs to return a collection of command definitions that differs from those in its Info.plist, implement this optional property getter.n return @[];n}n
分別是在插件成功載入後調用和動態配置 Command,但是 Command 也可以靜態的配置在插件的 Plist 上面,我的例子裡面其實就是這麼一個結構:
<dict>n <key>XCSourceEditorCommandDefinitions</key>n <array>n <dict>n <key>XCSourceEditorCommandClassName</key>n <string>SourceEditorCommand</string>n <key>XCSourceEditorCommandIdentifier</key>n <string>app.cyan.XcodeExtension.xEncoders.SourceEditorCommand</string>n <key>XCSourceEditorCommandName</key>n <string>Base64 Encode</string>n </dict>n </array>n <key>XCSourceEditorExtensionPrincipalClass</key>n <string>SourceEditorExtension</string>n</dict>n
沒什麼好解釋的,指定了實現類的名稱和菜單的名稱。
XCSourceEditorCommand 是另外一個核心概念,其實就是當插件被觸發之後,你有機會在代理方法裡面攔截到這個消息(XCSourceEditorCommandInvocation),做出處理之後將內容返回給 Xcode,僅此而已。
XCSourceEditorCommandInvocation 裡面會存放一些 meta 數據,其中最重要的是 identifier 和 buffer,identifier 就是用來做區分的,buffer 則是整個插件中最最重要的概念,但是他其實也很簡單。
XCSourceTextBuffer 最重要的環節有兩個,
/** The lines of text in the buffer, including line endings. Line breaks within a single buffer are expected to be consistent. Adding a "line" that itself contains line breaks will actually modify the array as well, changing its count, such that each line added is a separate element. */n@property (readonly, strong) NSMutableArray <NSString *> *lines;nn/** The text selections in the buffer; an empty range represents an insertion point. Modifying the lines of text in the buffer will automatically update the selections to match. */n@property (readonly, strong) NSMutableArray <XCSourceTextRange *> *selections;n
分別表示了傳遞過來的文本,以及選中區域。所以要做的事情已經一目了然了,流程如下:
- 在菜單或快捷鍵觸發插件
- XCSourceEditorCommand 攔截了消息
- 從 invocation 中拿到 buffer
- 在 buffer 中根據當前行,獲取到你要的數據
- 把數據替換後塞回去
所以這個 Base64 的例子,最後就只有這麼幾行代碼(只處理一行文本的情況下)
static inline NSString *Base64Encode(NSString *string) {n NSData *encodedData = [string dataUsingEncoding:NSUTF8StringEncoding];n return [encodedData base64EncodedStringWithOptions:0];n}nn- (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler {n n XCSourceTextRange *selection = invocation.buffer.selections.firstObject;n NSInteger index = selection.start.line;n NSString *text = invocation.buffer.lines[index];n invocation.buffer.lines[index] = Base64Encode(text);nn completionHandler(nil);n}n
最後把插件編譯運行到 Xcode 8 beta 上面,會出現一個被調試的 Xcode,是黑色的:
然後隨便選個項目跑起來,菜單裡面就有了插件了
我說的輕巧,其實現實中(目前 beta1),出現菜單的概率非常的低,這讓我非常惱火。
當然這個插件開發模式的確能拿來做一些事情,比如代碼的格式化,或者是編解碼,總之都集中在 文本的處理體驗上面。但他的局限性也是非常的明顯,例如:
- 沒有 UI 相關的介面,純粹只能做文本處理
- 沒辦法直接綁定快捷鍵,還得到 Xcode 的設置裡面去設置
- 就目前而言,調試的成功率實在是太低了,很鬱悶
- 只能通過菜單或快捷鍵調用
- 只能處理編輯區域
這些和第三方的 Xcode 插件比起來要差的很遠,不過這還算是一個不錯的開端,希望在日後可以把更多的介面開放出來,讓 Xcode 這個本來就讓人惱火的東西變得更好用。
推薦閱讀:
※如何評價 Xcode 8 不支持第三方插件?
※如何看待網易雲音樂等網易App使用第三方渠道下載的Xcode進行開發?
※如何評價疑似 XcodeGhost 作者的澄清說明?
※為什麼希望xcode有中文界面這麼遭鄙視?