前几天刚刚粗略了学习了一下Xcode的插件开发,一时心痒,就准备做个简单的插件练练手。
哎哟我擦,正当我准备大展身手的时候,我突然想到我该做个啥呢? 我这真是有了程序员,只差一个好Idea了。
好吧,正巧这个时候,我发现我和朋友协作的一个iOS项目在两个分支上同步开发,每次要合并后拉下分支,都发现Pod.lock文件都产生了变化,无法编译成功。每当这个时候,我都要进入terminal
输入一大堆的cd ..
进入对应的文件目录执行pod install
命令,甚是繁琐。
当然啦,你可以通过alias
配置快速的执行命令,但是,你仍然得切换出Xcode的窗口,对于我们这种效率控来说不能接受。
所以,我就想到了,在Xcode中利用插件集成一下关于pod的一些功能,同时绑定快捷键提高操作效率。
说干就干。
首先我们利用Xcode的plugin template生成项目的一些基本流程结构。关于插件的具体思路可以参考我之前的一篇文章《DXXcodeConsoleUnicodePlugin源码解析》
在这里,我们着重介绍一下利用 NSTask
去执行诸如pod install
这样的命令。
实现思路
在实现真正的Objective-C
代码之前,我们首先现在terminal
中随便找个安全的目录敲入pod install
来试试看结果,如下所说:
pod install
[!] No `Podfile' found in the project directory.
从这个错误提示中我们可以大致了解,pod install
的命令依赖于所谓的Podfile
。于是,我们输入pod install --help
查看其对应的帮助手册:
--project-directory=/project/dir/ The path to the root of the project
directory
--no-clean Leave SCM dirs like `.git` and `.svn`
intact after downloading
--no-integrate Skip integration of the Pods libraries
in the Xcode project(s)
--no-repo-update Skip running `pod repo update` before
install
--silent Show nothing
--verbose Show more debugging information
--no-ansi Show output without ANSI codes
--help Show help banner of specified command
从第一条帮助命令张,我们可以看到,我们需要通过–project-directory=来设置pod install
的根目录,也即Podfile
的所在。
好,事情到这里,我们在编写插件前需要的准备工作就基本完成了,我们现在只需利用NSTask
将我们在命令行中输入的命令执行即可。
让我们来看看实现的代码:
// 1.
[self searchMainProjectPath];
// 2.
NSTask *podInstallAction = [[NSTask alloc] init];
podInstallAction.currentDirectoryPath = self.mainProjectPath;
podInstallAction.arguments = @[@"install"];
podInstallAction.launchPath = @"/usr/bin/pod";
// 3.
NSPipe *pipeOut = [NSPipe pipe];
[podInstallAction setStandardOutput:pipeOut];
NSFileHandle *output = [pipeOut fileHandleForReading];
[output setReadabilityHandler:^(NSFileHandle * _Nonnull fileHandler) {
NSData *data = [fileHandler availableData];
NSString *text = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(@"text is %@", text);
}];
[podInstallAction launch];
[podInstallAction waitUntilExit];
- 我们首先要寻找到当前项目的主目录,也就是
Podfile
的路径 - 然后我们构建NSTask,将其的执行目录设置成我们的主目录,然后
/usr/bin
中调出pod
的可执行文件,执行pod install
。 - 我们利用NSPipe将默认的NSTask的输出(stdout)重定向到我们的指定的地方,这样有助于我们查看log或者进行流程工程。
到这里,基本上一个简单的小插件就完成了,但是我在这里想要强调一点关于主工程路径搜索的一些问题,我们首先来看代码:
NSArray *workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") workspaceWindowControllers];
[workspaceWindowControllers enumerateObjectsUsingBlock:^(id controller, NSUInteger idx, BOOL *stop) {
if ([[controller valueForKey:@"window"] isMainWindow]) {
id workspace = [controller valueForKey:@"_workspace"];
NSString *filePath = [[workspace valueForKey:@"representingFilePath"] valueForKey:@"pathString"];
NSString *projectName = [[filePath lastPathComponent] stringByDeletingPathExtension];
NSLog(@"CocoaPodUI::ProjectName::%@", projectName);
NSString *text = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Podfile"];
self.mainProjectPath = [filePath stringByDeletingLastPathComponent];
NSLog(@"pod ifle is %@", text);
}
}];
基于Xcode的插件开发实际上利用了大量的私有头文件。由于Objective-C著名的runtime特性,因此,很多时候,我们可以利用key-value-coding的方式获取我们普通途径下无法得到的结果。
同时,当有一个类的方法是私有方法的时候,你可以利用一个category
声明同样的函数签名,不需要实现,Objective-C
的runtime会自动帮你转发对应的message passing
。。