標籤:

Xcode Source Editor Extension

在 Xcode 8.0 的 beta 裡面,Apple 引入了一個新的面向 macOS 的 App Extension,叫做 Xcode Source Editor Extension(以下簡稱插件),今天看到一個提問說是這個怎麼弄,我簡單看了一下 WWDC 的 Session:developer.apple.com/vid 現在總結在這裡。首先我要說一下結論:我對這個所謂的官方 Xcode 插件開發介面,非常的失望

其實官方還是宣揚了一些優點的,其中最顯著的莫過於可以上架 Mac App Store,但這點優點實在是無法彌補他的弱雞。他能做的事情幾句話就可以說完,開發一個這樣的插件是極其的簡單。簡單說吧,一個插件能做下面的事情:

  1. 獲取 Xcode 正在編輯的文本

  2. 獲取所有的選中區域

  3. 替換 Xcode 正在編輯的文本

  4. 選中 Xcode 正在編輯的文本

  5. 在 Xcode 的 Editor 菜單裡面給你的插件生成一個子菜單,用於調用插件

  6. 可以在 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

分別表示了傳遞過來的文本,以及選中區域。所以要做的事情已經一目了然了,流程如下:

  1. 在菜單或快捷鍵觸發插件

  2. XCSourceEditorCommand 攔截了消息

  3. 從 invocation 中拿到 buffer

  4. 在 buffer 中根據當前行,獲取到你要的數據

  5. 把數據替換後塞回去

所以這個 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有中文界面這麼遭鄙視?

TAG:WWDC | Xcode | Mac |