Xcode8開放了新的一個Extension:Xcode Source Editor Extension,目的是讓開發者可以正規的自主為IDE編寫插件,雖然說系統現提供的功能還比較拮据,但是不妨礙我們瞭解和使用,本文主要介紹Xcode Source Editor Extension的功能,並演示一個 ...
Xcode8開放了新的一個Extension:Xcode Source Editor Extension,目的是讓開發者可以正規的自主為IDE編寫插件,雖然說系統現提供的功能還比較拮据,但是不妨礙我們瞭解和使用,本文主要介紹Xcode Source Editor Extension的功能,並演示一個簡單的插件的實現~
一、實現功能
1.刪除無用的類頭文件,要求類名和文件名一致
2.刪除重覆導入的頭文件,只保留一個
二、編寫代碼
1.新建項目,然後新建一個Target,類型選擇Xcode Source Editor Extension,完成之後設置target的簽名和項目的簽名一致。
2.在info.plist中可以修改插件顯示名稱Bundle name和其它對Extension的設置。
3.系統預設為我們生成SourceEditorCommand文件,此處我們也可以在info裡邊修改配置項,類似於項目中系統生成的Main.storyboard。插件的重點基本在:
- (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler
用戶調用我們的插件時,系統會回調這個方法,
XCSourceEditorCommandInvocation
Information about the source editor command that the user invoked, such as the identifier of the command, the text buffer on which the command is to operate, and whether the command has been canceled by Xcode or the user.
其中invocation.buffer是編輯器的全部文本
/** 當前編輯器的全部文件內容 */
@property (readonly, strong) NSMutableArray <NSString *> *lines;
/** 是當前選中的文本 */
@property (readonly, strong) NSMutableArray <XCSourceTextRange *> *selections;
我們在回調方法中編寫如下代碼:
//headerDict存放文本中所有的頭文件
NSMutableDictionary <NSString*, NSNumber *>*headerDict = [NSMutableDictionary dictionary];
//willCheckDict存放將要刪除的頭文件
NSMutableDictionary <NSNumber*, NSString *>*willCheckDict = [NSMutableDictionary dictionary];
//遍歷編輯器每一行
for (int idx = 0; idx < invocation.buffer.lines.count; idx++) {
NSString *lineCode = invocation.buffer.lines[idx];
//若willCheckDict文件不為空,則進行是否使用了該頭文件的判斷
if (willCheckDict.count > 0) {
[willCheckDict enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSString * _Nonnull checkString, BOOL * _Nonnull stop) {
if ([lineCode containsString:checkString]) {
if (![lineCode containsString:@"#import"]) {
if ([headerDict[checkString] isEqualToNumber: @1]) {
//若使用了該頭文件,則從willCheckDict字典中提出該項
[willCheckDict removeObjectForKey:key];
//同時設置該頭文件已經檢查過,若後續仍出現該頭文件,則可以進行刪除
headerDict[checkString] = @0;
}
}
}
}];
}
//檢測代碼是否含有#import為頭文件標誌;+號我們認為是類擴展的標誌
if ([lineCode containsString:@"#import"] && ![lineCode containsString:@"+"]) {
//解析獲取類名
NSRange range1 = [lineCode rangeOfString:@"\""];
NSRange range2 = [lineCode rangeOfString:@"\"" options:NSBackwardsSearch];
NSRange zeroRange = NSMakeRange(0, 0);
if (!(NSEqualRanges(range1, zeroRange) || NSEqualRanges(range2, zeroRange))) {
NSRange findRange = NSMakeRange(range1.location + 1, range2.location - range1.location - 3);
NSString *classString = [lineCode substringWithRange:findRange];
willCheckDict[@(idx)] = classString;
headerDict[classString] = @1;
}
}
}
//取出需要刪除的行
NSMutableIndexSet *index = [NSMutableIndexSet indexSet];
[willCheckDict.allKeys enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[index addIndex:obj.integerValue];
}];
//刪除不符合條件的行
[invocation.buffer.lines removeObjectsAtIndexes:index];
//通知系統完成
completionHandler(nil);
三、測試結果
1.運行,選擇Xcode8
2.可以看見灰色的Xcode實例。隨便選擇一個項目打開
3.測試。測試文件中含有未使用的頭文件和冗餘的頭文件
4.Editor中選擇插件運行
5.檢驗運行結果
啦啦啦,多餘的頭文件已經被成功檢測到並且移除了了~
四、總結
至此,我們完成並測試通過了一個簡單的Xcode插件的編寫。主要目的是簡單瞭解和使用Xcode8的插件,如果覺得有用,可以找到product裡邊的文件複製出來打開,然後在系統設置輔助功能中啟用,最後在Xcode中綁定快捷鍵即可食用。當然,功能十分簡陋,還請大神勿怪~
不足:受限於系統現有API,運行插件時,只能獲取到當前編輯的文件,無法獲取整個項目文件來分析,故很多功能暫時無法實現,如支持更加智能的檢測等等,以後系統若能提供項目空間的文件訪問和GUI支持,則插件可以發揮更大作用~
知識鏈:
WWDC2016
iOS 10 Day By Day: Xcode Source Editor Extensions
使用 Xcode Source Editor Extension開發Xcode 8 插件
歡迎加群討論其它~:578874451