前言
因为每次在设置里手动切换代理太浪费时间了,所以这两天想做一个可以mac app, 可以打开或关闭网络代理
,app不能有主窗口,只在菜单栏上有个按钮
,这样比较简洁。
步骤一:创建项目
new project->macos->app。
步骤二:去掉主窗口
打开main.storyboard,打这两项删掉(直接按delete),再次run之后发现主窗口不显示了
步骤三:添加菜单栏
在AppDelegate.m里添加代码如下:
@interface AppDelegate ()
@property (nonatomic,strong) NSStatusItem *statusItem; //必须应用、且强引用,否则不会显示。
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
NSStatusItem *statusItem = [statusBar statusItemWithLength: NSSquareStatusItemLength];
[statusItem setHighlightMode:YES];
[statusItem.button setImage: [NSImage imageNamed:@"abc"]]; //设置图标,请注意尺寸
[statusItem.button setToolTip:@"代理切换"];
NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@"Load_TEXT"];
[subMenu addItemWithTitle:@"打开"action:@selector(clickItem1) keyEquivalent:@"E"];
[subMenu addItemWithTitle:@"关闭"action:@selector(clickItem2) keyEquivalent:@"R"];
statusItem.menu = subMenu;
self.statusItem = statusItem;
}
- (void)clickItem1{
}
- (void)clickItem2{
}
注意:图片是本地,选个大小适中的图片。
运行后发现menu上显示app图标,说明成功了。
步骤四:执行shell命令
一. 切换网络代理命令
切换网络代理命令会用到networksetup
指令,
打开自动代理配置命令:
sudo networksetup -setautoproxyurl 网络名称 pac地址
//设置自动代理的url
sudo networksetup -setautoproxystate 网络名称 on
//自动代理的状态改为 打开
关闭自动代理配置命令:
sudo networksetup -setautoproxystate 网络名称 off
注意:“网络名称”一般都是“Wi-Fi”,也可能是其他的,但是不是你家wifi的名字...,看下图:
二. 运行shell命令的几种方法
1. c语言的system方法
system(@"sudo networksetup -setautoproxystate Wi-Fi on".UTF8String);
此方法只能运行非root权限命令
2. NSTask
可以运行root或非root权限命令。运行root命令时会弹用户名密码提示
代码如下:
- (NSString *)runNSTaskCmd:(NSString *)cmd
{
NSMutableArray *components = [[cmd componentsSeparatedByString:@" "] mutableCopy];
NSString *launchPath = components[0];
[components removeObjectAtIndex:0];
// 初始化并设置shell路径
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: launchPath];
// -c 用来执行string-commands(命令字符串),也就说不管后面的字符串里是什么都会被当做shellcode来执行
NSArray *arguments = components;
[task setArguments: arguments];
// 新建输出管道作为Task的输出
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
// 开始task
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
// 获取运行结果
NSData *data = [file readDataToEndOfFile];
return [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
}
调用代码如下:
[self runNSTaskCmd:@"/usr/sbin/networksetup -setautoproxystate Wi-Fi off"];
3. 第三方库STPrivilegedTask Github地址
- (void)runPrivilegedTask:(NSString*) cmdStr{
STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init];
NSMutableArray *components = [[cmdStr componentsSeparatedByString:@" "] mutableCopy];
NSString *launchPath = components[0];
[components removeObjectAtIndex:0];
[privilegedTask setLaunchPath:launchPath];
[privilegedTask setArguments:components];
[privilegedTask setCurrentDirectoryPath:[[NSBundle mainBundle] resourcePath]];
//set it off
OSStatus err = [privilegedTask launch];
if (err != errAuthorizationSuccess) {
if (err == errAuthorizationCanceled) {
NSLog(@"User cancelled");
return;
} else {
NSLog(@"Something went wrong: %d", (int)err);
// For error codes, see http://www.opensource.apple.com/source/libsecurity_authorization/libsecurity_authorization-36329/lib/Authorization.h
}
}
[privilegedTask waitUntilExit];
}
调用代码如下:
[self runPrivilegedTask:@"/usr/sbin/networksetup -setautoproxystate Wi-Fi off"];
其实NSTask
或者是STPrivilegedTask
的传的参数是一样的,不过内部原理不同(STPrivilegedTask
使用了AuthorizationRef
进行提权)。
推荐使用NSTask,理由如下:
NSTask
除了在第一次使用时会弹权限提示窗口,后面即便杀掉app再启动时执行shell命令也不会弹权限提示窗口,总之只弹了一次,我很奇怪,这样难道不更不安全了吗?而STPrivilegedTask
无论重不重启app,只要执行一次shell命令就会弹权限窗口,非常的烦。
结语
代码放在了 github上,欢迎大家指正~~