在玩了几个小时 SecurityFoundation 之后,我发现它对我的口味来说太复杂了。另外,文档已经过时。灵感来自this question。你可以通过 AppleScript 来完成。
您需要将卸载程序分为两部分:一个默认目标
删除用户文件和一个 helper 删除系统文件(当然具有升级权限)。
选项 1
您可以将该帮助程序编写为 shell 脚本并将其作为资源包含:
文件delete_system_file.sh:
#!/bin/bash
rm -f /usr/local/dummy.txt
在您的应用中:
let helper = NSBundle.mainBundle().pathForResource("delete_system_file", ofType: "sh")!
let script = "do shell script \"\(helper)\" with administrator privileges"
if let appleScript = NSAppleScript(source: script) {
var error: NSDictionary? = nil
appleScript.executeAndReturnError(&error)
if error != nil {
print(error!)
} else {
print("Deleted /usr/local/dummy.txt")
}
} else {
print("Cannot create the AppleScript object")
}
选项 2
您可以通过在项目中添加新目标来完全在 Swift 中完成此操作。假设您的第一个目标称为MyUninstaller。
添加新目标
- 点击
File > New > Target...
- 在 OS X 下,选择
Application > Command Line Tool
- 给你的新目标起个名字,比如
DeleteSystemFile
在左侧的 Project Navigator 中,展开 DeleteSystemFile 组并单击 main.swift 文件。删除dummy.txt 文件:
do {
try NSFileManager.defaultManager().removeItemAtPath("/usr/local/dummy.txt")
} catch {
print(error)
}
回到第一个目标
现在回到第一个目标 (MyUninstaller) 并添加 DeleteSystemFile 作为目标依赖项。
您可以通过 AppleScript 调用以提升权限运行第二个目标:
let helper = NSBundle.mainBundle().pathForAuxiliaryExecutable("DeleteSystemFile")!
let script = "do shell script \"\(helper)\" with administrator privileges"
if let appleScript = NSAppleScript(source: script) {
var error: NSDictionary? = nil
appleScript.executeAndReturnError(&error)
if error != nil {
print(error!)
} else {
print("Deleted /usr/local/dummy.txt")
}
} else {
print("Cannot create the AppleScript object")
}
选项 3
使用SMJobBless,这是苹果推荐的特权助手运行方式:
import SecurityFoundation
import ServiceManagement
// 1. Obtain an Authorization Reference
// You can do this at the beginning of the app. It has no extra rights until later
var authRef: AuthorizationRef = nil
let status = AuthorizationCreate(nil, nil, [.Defaults], &authRef)
// There's really no reason for this to fail, but we should check or completeness
guard status == errAuthorizationSuccess else {
fatalError("Cannot create AuthorizationRef: \(status)")
}
// 2. Ask user for admin privilege
var authItem = AuthorizationItem(name: kSMRightBlessPrivilegedHelper, valueLength: 0, value: nil, flags: 0)
var authRights = AuthorizationRights(count: 1, items: &authItem)
let flags: AuthorizationFlags = [.Defaults, .InteractionAllowed, .ExtendRights]
let status2 = AuthorizationCopyRights(authRef, &authRights, nil, flags, nil)
if status2 != errAuthorizationSuccess {
// Can't obtain admin privilege, handle error
print("Cannot obtain admin privilege")
}
// 3. Run the privileged helper
// This label must be globally unique and matches the product name of your helper
let label = "com.myCompany.myApp.myAppPrivilgedHelper"
var error: CFError? = nil
let result = withUnsafeMutablePointer(&error) {
SMJobBless(kSMDomainSystemLaunchd, label, authRef, UnsafeMutablePointer($0))
}
if !result {
print(error!)
}
// 4. Release the Authorization Reference
AuthorizationFree(authRef, [.Defaults])
这涉及到一些设置,您可以在SMJobBless 的文档中了解这些设置。还有一个sample project in ObjC。
免责声明:由于我没有个人签名密钥,因此无法全程测试。我可以从上面删除第 2 节,但为了完整起见将其包含在此处——示例项目就是这样做的。