├── ResignForiOS.zip
├── ResignForiOS
├── ResignForiOS.entitlements
├── ViewController.swift
├── UpdateAppleCer.sh
├── AppDelegate.swift
├── Info.plist
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── StringExtension.swift
├── ProcessTask.swift
├── Tools.swift
├── PlistHelper.swift
├── HProgregressHUD.swift
├── ProfileManager.swift
├── MainSignView.swift
├── CodeSigner.swift
└── Base.lproj
│ └── Main.storyboard
├── ResignForiOS.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── README.md
└── CoolResign
├── main.swift
└── Core.swift
/ResignForiOS.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lonely4flow/ResignForiOS/HEAD/ResignForiOS.zip
--------------------------------------------------------------------------------
/ResignForiOS/ResignForiOS.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ResignForiOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ResignForiOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ResignForiOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | }
17 |
18 |
19 | }
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ResignForiOS/UpdateAppleCer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | security find-certificate -c "Apple Worldwide Developer Relations Certification Authority" -a -Z | awk '/SHA-1/{system("security delete-certificate -Z "$NF)}'
3 | TEMP="$(mktemp -d -t com.chengcheng.ResignForiOS)"
4 | curl "https://developer.apple.com/certificationauthority/AppleWWDRCA.cer" > "$TEMP/AppleWWDRCA.cer"
5 | security add-certificates "$TEMP/AppleWWDRCA.cer"
6 | rm "$TEMP/AppleWWDRCA.cer"
7 |
--------------------------------------------------------------------------------
/ResignForiOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | func applicationDidFinishLaunching(_ aNotification: Notification) {
15 | // Insert code here to initialize your application
16 | }
17 |
18 | func applicationWillTerminate(_ aNotification: Notification) {
19 | // Insert code here to tear down your application
20 | }
21 |
22 | func applicationWillBecomeActive(_ notification: Notification) {
23 |
24 | }
25 |
26 | func applicationDidBecomeActive(_ notification: Notification) {
27 |
28 | }
29 |
30 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
31 | sender.windows.first?.makeKeyAndOrderFront(sender)
32 | return true
33 | }
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/ResignForiOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2018年 cheng. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ResignForiOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/ResignForiOS/StringExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtension.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | extension String {
11 |
12 | var lastPathComponent: String {
13 | get {
14 | return (self as NSString).lastPathComponent
15 | }
16 | }
17 | var pathExtension: String {
18 | get {
19 | return (self as NSString).pathExtension
20 | }
21 | }
22 | var stringByDeletingLastPathComponent: String {
23 | get {
24 | return (self as NSString).deletingLastPathComponent
25 | }
26 | }
27 | var deletePathExtension: String {
28 | get {
29 | return (self as NSString).deletingPathExtension
30 | }
31 | }
32 |
33 | var pathComponents: [String] {
34 | get {
35 | return (self as NSString).pathComponents
36 | }
37 | }
38 |
39 | func appendPathComponent(_ path: String) -> String {
40 | let nsSt = self as NSString
41 | return nsSt.appendingPathComponent(path)
42 | }
43 |
44 | func stringByAppendingPathExtension(_ ext: String) -> String? {
45 | let nsSt = self as NSString
46 | return nsSt.appendingPathExtension(ext)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ResignForiOS/ProcessTask.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProcessTask.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 | import Foundation
9 | struct AppSignerTaskOutput {
10 | var output: String
11 | var status: Int32
12 | init(status: Int32, output: String) {
13 | self.status = status
14 | self.output = output
15 | }
16 | }
17 | extension Process {
18 | func launchSyncronous() -> AppSignerTaskOutput {
19 | self.standardInput = FileHandle.nullDevice
20 | let pipe = Pipe()
21 | self.standardOutput = pipe
22 | self.standardError = pipe
23 | let pipeFile = pipe.fileHandleForReading
24 | self.launch()
25 |
26 | var outPutData = Data()
27 | while self.isRunning {
28 | outPutData.append(pipeFile.availableData)
29 | }
30 |
31 | pipeFile.closeFile();
32 | self.terminate();
33 |
34 | let output = String(data: outPutData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
35 | return AppSignerTaskOutput(status: self.terminationStatus, output: output!)
36 | }
37 |
38 | func execute(_ launchPath: String, workingDirectory: String?, arguments: [String]?) -> AppSignerTaskOutput {
39 | self.launchPath = launchPath
40 | if arguments != nil {
41 | self.arguments = arguments
42 | }
43 | if workingDirectory != nil {
44 | self.currentDirectoryPath = workingDirectory!
45 | }
46 | return self.launchSyncronous()
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/ResignForiOS/Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tools.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AppKit
11 |
12 | public func updateAppleCer() {
13 | let script = "do shell script \"/bin/bash \\\"\(Bundle.main.path(forResource: "UpdateAppleCer", ofType: "sh")!)\\\"\" with administrator privileges"
14 | NSAppleScript(source: script)?.executeAndReturnError(nil)
15 | return
16 | }
17 |
18 |
19 | class Log {
20 |
21 | static let mainBundle = Bundle.main
22 | static let bundleID = mainBundle.bundleIdentifier ?? "com.chengcheng.ResignForiOS"
23 | static let bundleName = mainBundle.infoDictionary!["CFBundleName"]
24 | static let bundleVersion = mainBundle.infoDictionary!["CFBundleShortVersionString"]
25 | static let tempDirectory = NSTemporaryDirectory()
26 | static var logName = Log.tempDirectory.appendPathComponent("\(Log.bundleID)-\(Date().timeIntervalSince1970).log")
27 |
28 | static func write(_ value:String) {
29 | let formatter = DateFormatter()
30 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
31 |
32 | if let outputStream = OutputStream(toFileAtPath: logName, append: true) {
33 | outputStream.open()
34 | let text = "\(formatter.string(from: Date())) \(value)\n"
35 | let data = text.data(using: String.Encoding.utf8, allowLossyConversion: false)!
36 | outputStream.write((data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count), maxLength: data.count)
37 | outputStream.close()
38 | }
39 | }
40 | }
41 |
42 |
43 | public func setStatus(_ status: String) {
44 | if !Thread.isMainThread {
45 | DispatchQueue.main.sync {
46 | setStatus(status)
47 | }
48 | } else {
49 | Log.write(status)
50 | print(status)
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 使用说明
2 | 
3 |
4 | 如上图所示,可以选择要签名的包文件,这个工具支持ipa,app,xcarchive三种文件作为签名源文件,重签名之后都会生成ipa。
5 |
6 |
7 | | 选项 | 说明 |
8 | | :---------------: | -----|
9 | | Provisioning Profile | 用于选择描述文件,会像xcode一样索所有双击安装在电脑上的描述文件,列举出来,会自动过滤掉过期的。|
10 | | Signing Certificate | 选择对应的证书,同样会检索系统钥匙串安装的可用的证书,会自动过滤掉过期的。|
11 | | new BundleID | 可以重新指定一个新的BundleID。 |
12 | | App Display Name | 可以重新设定一个app名字。 |
13 | | Signing Certificate | 选择对应的证书,同样会检索系统钥匙串安装的可用的证书,会自动过滤掉过期的。|
14 | | App Version | 可以重新指定version。 |
15 | | App Short Version | 可以重新指定Short version。 |
16 |
17 | ### 安装方法
18 | 1.直接下载源码使用xcode运行
19 | 2.从[GitHub仓库releases](https://github.com/HanProjectCoder/ResignForiOS/releases)找最新发布的dmg安装包,下载安装即可
20 |
21 | ### 命令行模式:
22 | 支持使用命令行调起签名功能:(前提是要通过dmg安装到应用目录下)
23 | 命令:
24 | ```
25 | open -a ResignForiOS --args
26 | ```
27 | 必加参数:
28 |
29 | | 参数 | 说明 |
30 | | :---------------: | ------ |
31 | | -i | 要重签名的ipa/app/xcarchive文件路径 |
32 | |-p | 描述文件路径 |
33 | |-c | 证书名字,可以在终端使用security find-identity -v -p codesigning命令列出所有在钥匙串的证书,可以挑选出所需签名的证书名字 |
34 | |-o | 输出ipa路径 |
35 |
36 |
37 | 使用举例:
38 | ```
39 | open -a ResignForiOS --args -i /xxx/xxx.ipa -p /xxx/xxx.mobileprovision -c "xxx: xx." -o /xxx/xxx.ipa
40 | ```
41 | **注意使用此命令行模式,执行命令之前,要关闭退出之前打开的窗口。**
42 |
43 |
44 | ### 签名失败可能的问题以及解决方案
45 | #### 1.目标机有多个版本xcode,命令行环境下没有select对应的当前的xcode版本:
46 | 检查一下当前命令号环境下的xcode:
47 | ```
48 | xcode-select --print-path
49 | ```
50 | 如果发现指定版本不是当前所用xocde,就使用以下命令指定xcode
51 | ```
52 | sudo xcode-select -switch /Applications/XcodeXXX.app/Contents/Developer
53 | ```
54 | #### 2.缺少Apple Worldwide Developer Relations Certification Authority证书
55 | 检查一下是否安装了AppleWWDRCA.cer:
56 | ```
57 | security find-certificate -c "Apple Worldwide Developer Relations Certification Authority"
58 | ```
59 | 如果提示找不到,就打开 [苹果官方证书下载地址](http://developer.apple.com/certificationauthority/AppleWWDRCA.cer) 点击下载后,双击cer文件即可。OK。
60 | ps:事实上如果缺少AppleWWDRCA.cer,所有申请的开发者证书,在钥匙串里面的显示都会变成不信任证书。
61 |
--------------------------------------------------------------------------------
/ResignForiOS/PlistHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlistHelper.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | open class PlistHelper: NSObject {
12 |
13 | let defaultsPath = "/usr/bin/defaults"
14 | var plistPath: String?
15 |
16 | public convenience init(plistPath: String?) {
17 | self.init()
18 | self.plistPath = plistPath
19 | }
20 |
21 | var bundleDisplayName: String? {
22 | set {
23 | setValue(newValue!, for: "CFBundleDisplayName")
24 | }
25 | get {
26 | return getValue(for: "CFBundleDisplayName")
27 | }
28 | }
29 |
30 | var bundleIdentifier: String? {
31 | set {
32 | setValue(newValue!, for: "CFBundleIdentifier")
33 | }
34 | get {
35 | return getValue(for: "CFBundleIdentifier")
36 | }
37 | }
38 |
39 | var wkAppBundleIdentifier: String? {
40 | set {
41 | setValue(newValue!, for: "WKCompanionAppBundleIdentifier")
42 | }
43 | get {
44 | return getValue(for: "WKCompanionAppBundleIdentifier")
45 | }
46 | }
47 | var bundleVersion: String? {
48 | set {
49 | setValue(newValue!, for: "CFBundleVersion")
50 | }
51 | get {
52 | return getValue(for: "CFBundleVersion")
53 | }
54 | }
55 | var shortBundleVersion: String? {
56 | set {
57 | setValue(newValue!, for: "CFBundleShortBundleVersion")
58 | }
59 | get {
60 | return getValue(for: "CFBundleShortBundleVersion")
61 | }
62 | }
63 |
64 | var bundleExecutable: String? {
65 | set {
66 | setValue(newValue!, for: "CFBundleExecutable")
67 | }
68 | get {
69 | return getValue(for: "CFBundleExecutable")
70 | }
71 | }
72 |
73 |
74 | func delete(key: String) {
75 | _ = Process().execute(defaultsPath, workingDirectory: nil, arguments: ["delete", plistPath!, key])
76 | }
77 | func getValue(for key: String) -> String? {
78 | return Process().execute(defaultsPath, workingDirectory: nil, arguments: ["read", plistPath!, key]).output
79 | }
80 |
81 | func exsistValue(for key: String) -> Bool {
82 | return getValue(for: key) != nil
83 | }
84 |
85 | func setValue(_ value: String, for key: String) {
86 | setStatus("\(plistPath!) Changing \(key) to \(value))")
87 | let task = Process().execute(defaultsPath, workingDirectory: nil, arguments: ["write", plistPath!, key, value])
88 | if task.status != 0 {
89 | setStatus("\(plistPath!) Changing \(key) to \(value)) Error!!!")
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/CoolResign/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // CoolResign
4 | //
5 | // Created by hanxiaoqing on 2018/5/17.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 | //https://github.com/stupergenius/Bens-Log/blob/master/blog-projects/swift-command-line/btc.swift
9 | //https://github.com/kylef/Commander
10 | import Foundation
11 |
12 | class CommandWorker: NSObject {
13 |
14 | var inputPath: String?
15 | var profliePath: String?
16 | var cerName: String?
17 | var outputPath: String?
18 | var bundleID: String?
19 |
20 | func work() {
21 | let signer = CodeSigner()
22 | signer.delegate = self
23 | signer.sign(inputFile: inputPath!, provisioningFile: profliePath, newBundleID: bundleID ?? "", newDisplayName: "", newVersion: "", newShortVersion: "", signingCertificate: cerName!, outputFile: outputPath!, openByTerminal: true)
24 | }
25 | }
26 |
27 | extension CommandWorker: CodeSignDelegate {
28 |
29 | func codeSignBegin(workingDir: String) {
30 | print("CodeSign begin with workingDir: \(workingDir)")
31 | }
32 |
33 | func codeSignLogRecord(logDes: String) {
34 | print(logDes)
35 | }
36 |
37 | func codeSignError(errDes: String, tempDir: String) {
38 | print(errDes)
39 | cleanup(tempDir)
40 | }
41 |
42 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String) {
43 | cleanup(tempDir)
44 | print("CodeSigneEndSuccessed, output at \(outPutPath)")
45 | exit(0)
46 | }
47 |
48 | func cleanup(_ dir: String) {
49 | do {
50 | print("Deleting: \(dir)")
51 | try FileManager.default.removeItem(atPath: dir)
52 | } catch {
53 | print("Deleting: \(dir) error")
54 | }
55 | }
56 | }
57 |
58 |
59 | let inputOpt = Option(trigger:.mixed("i","inputfile-path"))
60 | let provisionOpt = Option(trigger:.mixed("p","provision-path"))
61 | let cerNameOpt = Option(trigger:.mixed("c","certificate-name"))
62 | let bundleIDOpt = Option(trigger:.mixed("b","bundleId"))
63 | let outputOpt = Option(trigger:.mixed("o","outputfile-path"))
64 |
65 | let helpOpt = Option(trigger:.mixed("h","help"))
66 | let parser = OptionParser(definitions:[inputOpt, provisionOpt, cerNameOpt, bundleIDOpt,outputOpt,helpOpt])
67 |
68 | let arguments = CommandLine.arguments
69 | //let arguments = ["CoolResign", "-i", "/Users/hanxiaoqing/Documents/sdkTest_c.xcarchive", "-p", "/Users/hanxiaoqing/Desktop/SvnFolder/NewSDKFolder/provisioning/enterprise/DIS_ALL_NZK3GXHA6L.mobileprovision", "-c", "iPhone Distribution: Babeltime Inc.", "-o", "/Users/hanxiaoqing/Documents/sdkTest_c.ipa", "-b", "com.babeltime.sdkdemo.unity"]
70 |
71 | print("get all args: \(arguments)")
72 |
73 | let spliceArgs = arguments[1 ..< arguments.count]
74 |
75 | do {
76 | let (options, rest) = try parser.parse(Array(spliceArgs))
77 |
78 | if spliceArgs.count == 0 || options[helpOpt] != nil {
79 | print(parser.helpStringForCommandName("show codesign important args"))
80 | exit(1)
81 | }
82 |
83 | let inputArgKeyCount = options.keys.count
84 | print("input argkey count: \(inputArgKeyCount)")
85 |
86 | // require arg: -i -p -c -o, the "-h, -b" two args neednt required
87 | if inputArgKeyCount < parser.definitions.count - 2 {
88 | print("Input arg keys not enough, Please check !!!")
89 | exit(2)
90 | }
91 |
92 | guard rest.count == inputArgKeyCount else {
93 | print("One or more arguments value not set, Please check !!!")
94 | exit(3)
95 | }
96 |
97 | let worker = CommandWorker()
98 | rest.forEach { argValue in
99 | let index = spliceArgs.index(of: argValue)
100 | let argKey = spliceArgs[index! - 1]
101 |
102 | if inputOpt.matches(argKey) {
103 | worker.inputPath = argValue
104 | }
105 |
106 | if provisionOpt.matches(argKey) {
107 | worker.profliePath = argValue
108 | }
109 |
110 | if cerNameOpt.matches(argKey) {
111 | worker.cerName = argValue
112 | }
113 |
114 | if bundleIDOpt.matches(argKey) {
115 | worker.bundleID = argValue
116 | }
117 |
118 | if outputOpt.matches(argKey) {
119 | worker.outputPath = argValue
120 | }
121 |
122 | }
123 | // 判断xcode-select -p是否有值
124 | // 判断 证书是否存在
125 |
126 | worker.work()
127 | } catch let OptionKitError.invalidOption(description: description) {
128 | exit(4)
129 | print("OptionKit throw error: \n \(description)")
130 | }
131 |
132 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/ResignForiOS/HProgregressHUD.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 |
3 | fileprivate let HDefaultPadding: CGFloat = 4.0;
4 | fileprivate let HDefaultLabelFontSize: CGFloat = 15.0;
5 | fileprivate let HDefaultDetailsLabelFontSize: CGFloat = 12.0;
6 |
7 |
8 | open class HProgregressHUD: NSView {
9 |
10 | open static func showHUDAddedTo(view: NSView, animated: Bool) -> HProgregressHUD {
11 | let hud = HProgregressHUD(with: view)
12 | view.addSubview(hud)
13 | hud.showAnimated(animated)
14 | return hud
15 | }
16 |
17 | open static func hideHUDFor(view: NSView, animated: Bool) -> Bool {
18 | if let hud = HUDFor(view: view) {
19 | hud.hideAnimated(animated)
20 | return true
21 | } else {
22 | return false
23 | }
24 | }
25 |
26 | open static func HUDFor(view: NSView) -> HProgregressHUD? {
27 | for view in view.subviews.reversed() {
28 | if view is HProgregressHUD {
29 | return view as? HProgregressHUD
30 | }
31 | }
32 | return nil
33 | }
34 |
35 |
36 | open func showAnimated(_: Bool) {
37 | alphaValue = 1.0
38 | backgroundView.alphaValue = 0.4
39 | }
40 |
41 | open func hideAnimated(_: Bool) {
42 | alphaValue = 0.0
43 | removeFromSuperview()
44 | }
45 |
46 | open func hideAnimated(_: Bool, afterDelay: Double) {
47 |
48 | }
49 |
50 | open var contentColor: NSColor = .white
51 |
52 | open let backgroundView = HBackgroundView()
53 |
54 | open let bezelView = NSVisualEffectView()
55 |
56 |
57 | open let label: NSTextField = createLabel()
58 | open let detailsLabel: NSTextField = createLabel()
59 |
60 |
61 | fileprivate func commonInit() {
62 | alphaValue = 0.0
63 | wantsLayer = true
64 | layer?.backgroundColor = NSColor.clear.cgColor
65 |
66 | setupViews()
67 | updateIndicators()
68 | }
69 |
70 | public convenience init(with view: NSView) {
71 | self.init(frame: view.bounds)
72 | }
73 |
74 | public override init(frame frameRect: NSRect) {
75 | super.init(frame: CGRect())
76 | commonInit()
77 | }
78 | required public init?(coder decoder: NSCoder) {
79 | super.init(coder: decoder)
80 | commonInit()
81 | }
82 | open override var isFlipped: Bool {
83 | return true
84 | }
85 | }
86 |
87 | extension HProgregressHUD {
88 |
89 | fileprivate static func createLabel() -> NSTextField {
90 | let label = NSTextField()
91 | label.isBordered = false
92 | label.isEditable = false
93 | label.isSelectable = false
94 | label.font = NSFont.systemFont(ofSize: 13.5)
95 | label.drawsBackground = false
96 | label.alignment = .center
97 | return label
98 | }
99 |
100 |
101 | fileprivate func setupViews() {
102 | let defaultColor = contentColor
103 |
104 | backgroundView.autoresizingMask = [.width, .height]
105 | backgroundView.backgroundColor = NSColor.gray
106 | backgroundView.alphaValue = 0.0
107 | addSubview(backgroundView)
108 |
109 | let materialView = HBackgroundView()
110 | materialView.autoresizingMask = [.width, .height]
111 | materialView.backgroundColor = NSColor.black
112 | materialView.alphaValue = 0.7
113 |
114 | label.textColor = defaultColor
115 | label.font = NSFont.boldSystemFont(ofSize: HDefaultLabelFontSize)
116 | materialView.addSubview(label)
117 |
118 | detailsLabel.textColor = defaultColor
119 | detailsLabel.font = NSFont.boldSystemFont(ofSize: HDefaultDetailsLabelFontSize)
120 | detailsLabel.alignment = .left
121 | materialView.addSubview(detailsLabel)
122 |
123 | bezelView.layer?.cornerRadius = 5.0
124 | bezelView.state = .active
125 | bezelView.blendingMode = .withinWindow
126 | bezelView.alphaValue = 0.7
127 | bezelView.addSubview(materialView)
128 |
129 | addSubview(bezelView)
130 | }
131 |
132 |
133 | fileprivate func updateIndicators() {
134 |
135 | }
136 |
137 | open override func layout() {
138 | super.layout()
139 |
140 | frame = (superview?.bounds)!
141 | backgroundView.frame = bounds
142 |
143 | let verticalMargin: CGFloat = 15.0
144 | let horizontalMargin: CGFloat = 15.0
145 | let limitMinWidth: CGFloat = 200
146 |
147 | label.left = 0.0
148 | label.top = verticalMargin
149 | label.width = limitMinWidth
150 | label.height = 20.0
151 |
152 |
153 | detailsLabel.top = label.bottom + verticalMargin
154 | let detailStringWidth = (detailsLabel.stringValue as NSString).size(withAttributes: [.font: detailsLabel.font]).width
155 | if detailStringWidth < limitMinWidth {
156 | detailsLabel.left = (limitMinWidth - detailStringWidth) * 0.5
157 | detailsLabel.width = detailStringWidth
158 | detailsLabel.height = 20.0
159 | } else {
160 | detailsLabel.left = horizontalMargin
161 | detailsLabel.width = limitMinWidth - detailsLabel.left * 2
162 | detailsLabel.height = heightFor(string: detailsLabel.stringValue, font: detailsLabel.font!, width: detailsLabel.width) + 8
163 | }
164 |
165 | bezelView.width = limitMinWidth
166 | bezelView.height = detailsLabel.bottom + verticalMargin
167 |
168 | if detailsLabel.stringValue.count == 0 || detailsLabel.isHidden {
169 | bezelView.height = label.bottom + verticalMargin
170 | }
171 |
172 | // make the whole content view center
173 | bezelView.makeCenter()
174 | }
175 |
176 | func heightFor(string: String, font: NSFont, width: CGFloat) -> CGFloat {
177 | let textStorage = NSTextStorage(string: string)
178 | let textContainer = NSTextContainer(containerSize: CGSize(width: width, height: CGFloat(MAXFLOAT)))
179 |
180 | let layoutManager = NSLayoutManager()
181 | layoutManager.addTextContainer(textContainer)
182 |
183 | textStorage.addLayoutManager(layoutManager)
184 | textStorage.addAttributes([.font: font], range: NSRange(location: 0, length: textStorage.length))
185 | textContainer.lineFragmentPadding = 0.0
186 |
187 | layoutManager.glyphRange(for: textContainer)
188 | return layoutManager.usedRect(for: textContainer).size.height
189 | }
190 |
191 | open override func mouseDown(with event: NSEvent) {}
192 |
193 | }
194 |
195 |
196 | fileprivate extension NSView {
197 |
198 | fileprivate func makeCenter(in superView: NSView? = nil) {
199 | var x:CGFloat = 0
200 | var y:CGFloat = 0
201 | if let sv = superView {
202 | x = CGFloat(roundf(Float((sv.frame.width - frame.width)/2.0)))
203 | y = CGFloat(roundf(Float((sv.frame.height - frame.height)/2.0)))
204 | } else if let sv = self.superview {
205 | x = CGFloat(roundf(Float((sv.frame.width - frame.width)/2.0)))
206 | y = CGFloat(roundf(Float((sv.frame.height - frame.height)/2.0)))
207 | }
208 | self.setFrameOrigin(NSMakePoint(x, y))
209 | }
210 | fileprivate var left: CGFloat {
211 | get {
212 | return self.frame.origin.x
213 | }
214 | set(newLeft) {
215 | var frame = self.frame
216 | frame.origin.x = newLeft
217 | self.frame = frame
218 | }
219 | }
220 |
221 | fileprivate var top:CGFloat {
222 | get {
223 | return self.frame.origin.y
224 | }
225 |
226 | set(newTop) {
227 | var frame = self.frame
228 | frame.origin.y = newTop
229 | self.frame = frame
230 | }
231 | }
232 |
233 | fileprivate var width:CGFloat {
234 | get {
235 | return self.frame.size.width
236 | }
237 |
238 | set(newWidth) {
239 | var frame = self.frame
240 | frame.size.width = newWidth
241 | self.frame = frame
242 | }
243 | }
244 |
245 | fileprivate var height:CGFloat {
246 | get {
247 | return self.frame.size.height
248 | }
249 |
250 | set(newHeight) {
251 | var frame = self.frame
252 | frame.size.height = newHeight
253 | self.frame = frame
254 | }
255 | }
256 |
257 | fileprivate var right:CGFloat {
258 | get {
259 | return self.left + self.width
260 | }
261 | }
262 |
263 | fileprivate var bottom:CGFloat {
264 | get {
265 | return self.top + self.height
266 | }
267 | }
268 | }
269 |
270 |
271 | open class HBackgroundView: NSView {
272 |
273 | public var backgroundColor: NSColor = NSColor.clear {
274 | didSet {
275 | if backgroundColor != oldValue {
276 | layer?.backgroundColor = backgroundColor.cgColor
277 | }
278 | }
279 | }
280 |
281 | public override init(frame frameRect: NSRect) {
282 | super.init(frame: frameRect)
283 | wantsLayer = true
284 | }
285 |
286 | required public init?(coder decoder: NSCoder) {
287 | super.init(coder: decoder)
288 | wantsLayer = true
289 | }
290 | open override var isFlipped: Bool {
291 | return true
292 | }
293 | }
294 |
295 |
--------------------------------------------------------------------------------
/ResignForiOS/ProfileManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileManager.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 | import Cocoa
9 | import Security
10 |
11 |
12 | open class ProfileManager {
13 |
14 | fileprivate var directoryPath: String?
15 |
16 | init(directoryPath: String? = nil) {
17 | self.directoryPath = directoryPath
18 | }
19 |
20 | open var profilesPath: String {
21 | if let path = directoryPath {
22 | return path
23 | } else {
24 | let libraryDir = FileManager().urls(for: .libraryDirectory, in: .userDomainMask).first!
25 | directoryPath = libraryDir.appendingPathComponent("MobileDevice/Provisioning Profiles").path
26 | return self.profilesPath
27 | }
28 | }
29 |
30 | // MARK: expired Profiles
31 | open var expiredProfiles: [Profile] = []
32 |
33 | open func updateProfiles() -> [Profile] {
34 | var validProfiles: [Profile] = []
35 | if let contentFiles = try? FileManager.default.contentsOfDirectory(atPath: profilesPath) {
36 | let mobileprovisions = contentFiles.filter({URL(fileURLWithPath: $0).pathExtension == "mobileprovision"})
37 | for profile in mobileprovisions {
38 | if let profile = Profile(filePath: profilesPath + "/" + profile) {
39 | if profile.isExpired == false {
40 | validProfiles.append(profile)
41 | } else {
42 | expiredProfiles.append(profile)
43 | }
44 | }
45 | }
46 | }
47 | return validProfiles
48 | }
49 |
50 |
51 | open func profilesMatching(_ bundleID: String, acceptWildcardMatches: Bool = false) -> [Profile] {
52 |
53 | var matches = updateProfiles().filter({ $0.matches(bundleID) })
54 | if !acceptWildcardMatches {
55 | matches = matches.filter({ !$0.isWildcard })
56 | }
57 | return matches
58 | }
59 |
60 | }
61 |
62 |
63 | //==========================================================================
64 | // MARK: Profile Constants Keys
65 | //==========================================================================
66 |
67 | fileprivate let creationDateKey = "CreationDate"
68 | fileprivate let expirationDateKey = "ExpirationDate"
69 |
70 | fileprivate let appIDNameKey = "AppIDName"
71 | fileprivate let applicationIdentifierPrefixKey = "ApplicationIdentifierPrefix"
72 | fileprivate let developerCertificatesKey = "DeveloperCertificates"
73 | fileprivate let entitlementsKey = "Entitlements"
74 | fileprivate let nameKey = "Name"
75 | fileprivate let provisionsAllDevicesKey = "ProvisionsAllDevices"
76 | fileprivate let provisionedDevicesKey = "ProvisionedDevices"
77 | fileprivate let teamIdentifierKey = "TeamIdentifier"
78 | fileprivate let teamNameKey = "TeamName"
79 | fileprivate let timeToLiveKey = "TimeToLive"
80 | fileprivate let UUIDKey = "UUID"
81 | fileprivate let versionKey = "Version"
82 |
83 |
84 | //==========================================================================
85 | // MARK: Entitlements Constants Keys
86 | //==========================================================================
87 |
88 | // General
89 | fileprivate let applicationIdentiferKey = "application-identifier"
90 | fileprivate let betaReportsActiveKey = "beta-reports-active"
91 | fileprivate let getTaskAllowKey = "get-task-allow"
92 | fileprivate let keychainAccessGroupsKey = "keychain-access-groups"
93 | fileprivate let teamIdKey = "com.apple.developer.team-identifier"
94 |
95 | // Push
96 | fileprivate let apsEnvironmentKey = "aps-environment"
97 |
98 | // iCloud
99 | fileprivate let ubiquityStoreIdentifierKey = "com.apple.developer.ubiquity-kvstore-identifier"
100 |
101 |
102 |
103 |
104 | //==========================================================================
105 | // MARK: Provisioning Profile
106 | //==========================================================================
107 |
108 | open class Profile: NSObject {
109 |
110 | open var filePath: String?
111 |
112 | // MARK: Profile Properties
113 | open let applicationIdentifierPrefix: [String]
114 | open let appIDName: String?
115 | open var bundleID: String = ""
116 |
117 | open var developerCertificates: [String] = []
118 | open let entitlements: Entitlements!
119 | open let name: String
120 | open let provisionsAllDevices: Bool
121 | open let provisionedDevices: [String]
122 | open let teamIdentifiers: [String]
123 | open let teamName: String?
124 | open let timeToLive: Int
125 | open let uuid: String!
126 | open let version: Int
127 |
128 | open let creationDate: Date!
129 | open let expirationDate: Date!
130 |
131 | open var isExpired: Bool {
132 | return self.expirationDate.timeIntervalSinceNow < 0
133 | }
134 |
135 | open var isWildcard: Bool {
136 | return entitlements.applicationIdentifer.hasSuffix("*")
137 | }
138 |
139 | open var type: String = ""
140 |
141 | open func matches(_ bundleID: String) -> Bool {
142 | var appID = self.entitlements.applicationIdentifer
143 | if let dotRange = appID?.range(of: ".") {
144 | appID = String(describing: appID?[(appID?.startIndex)! ..< dotRange.upperBound])
145 | }
146 | if !isWildcard {
147 | return bundleID == appID
148 | }
149 | return bundleID.hasPrefix(appID!.substring(to: appID!.index(before: appID!.endIndex))) || appID == "*"
150 | }
151 |
152 |
153 | // MARK: Initialize with a decoded Profile info Dict
154 | public init?(dictionary: [String: AnyObject]?) {
155 | self.creationDate = dictionary?[creationDateKey] as? Date
156 | self.expirationDate = dictionary?[expirationDateKey] as? Date
157 | self.appIDName = dictionary?[appIDNameKey] as? String
158 | self.applicationIdentifierPrefix = dictionary?[applicationIdentifierPrefixKey] as? [String] ?? []
159 | self.entitlements = Entitlements(dictionary: dictionary?[entitlementsKey] as? [String: AnyObject])
160 |
161 | let devCerDatas = dictionary?[developerCertificatesKey] as? [Data] ?? []
162 | for cer in devCerDatas {
163 | let certificateRef = SecCertificateCreateWithData(nil, cer as CFData)
164 | let summary = SecCertificateCopySubjectSummary(certificateRef!)
165 | self.developerCertificates.append(summary! as String)
166 | }
167 |
168 | let appID = entitlements.applicationIdentifer
169 | let index = appID?.index((appID?.startIndex)!, offsetBy: (applicationIdentifierPrefix.first?.count)! + 1)
170 | self.bundleID = (appID?.substring(from: index!))!.trimmingCharacters(in: CharacterSet(charactersIn: "*"))
171 |
172 | self.name = (dictionary?[nameKey] as? String)!
173 | self.provisionsAllDevices = dictionary?[provisionsAllDevicesKey] as? Bool ?? false
174 | self.provisionedDevices = dictionary?[provisionedDevicesKey] as? [String] ?? []
175 | self.teamIdentifiers = dictionary?[teamIdentifierKey] as? [String] ?? []
176 | self.teamName = dictionary?[teamNameKey] as? String
177 | self.timeToLive = dictionary?[timeToLiveKey] as? Int ?? 0
178 | self.uuid = dictionary?[UUIDKey] as? String
179 | self.version = dictionary?[versionKey] as? Int ?? 0
180 | }
181 |
182 | public convenience init?(filePath path: String) {
183 | self.init(dictionary: Profile.decodeProfile(path: path))
184 | self.filePath = path
185 | }
186 |
187 | public convenience init?(fileURL url: URL) {
188 | self.init(filePath: url.path)
189 | }
190 |
191 |
192 | // MARK: Decode Profile with a path
193 | fileprivate class func decodeProfile(path: String) -> [String: AnyObject]? {
194 | if let profileData = try? Data(contentsOf: URL(fileURLWithPath: path)) {
195 | var optionalDecoder: CMSDecoder?
196 | var optionalDataRef: CFData?
197 |
198 | CMSDecoderCreate(&optionalDecoder)
199 | if let decoderRef = optionalDecoder {
200 | CMSDecoderUpdateMessage(decoderRef, (profileData as NSData).bytes, Int(profileData.count))
201 | CMSDecoderFinalizeMessage(decoderRef)
202 | CMSDecoderCopyContent(decoderRef, &optionalDataRef)
203 |
204 | let decodedProfileData = optionalDataRef! as Data
205 | let plistDict = try! PropertyListSerialization.propertyList(from: decodedProfileData, options: .mutableContainersAndLeaves, format: nil)
206 | return plistDict as? [String: AnyObject]
207 | }
208 | }
209 | return nil
210 | }
211 | }
212 |
213 | //==========================================================================
214 | // MARK: Entitlements
215 | //==========================================================================
216 |
217 | open class Entitlements {
218 |
219 | open let fullDictionary: [String: AnyObject]!
220 |
221 | // General
222 | open let applicationIdentifer: String!
223 | open let betaReportsActive: Bool
224 | open let getTaskAllow: Bool
225 | open let keychainAccessGroups: [String]
226 | open let teamIdentifier: String?
227 |
228 | // Push
229 | open let apsEnvironment: String
230 |
231 | // iCloud
232 | open let ubiquityStoreIdentifier: String?
233 |
234 | init?(dictionary: [String: AnyObject]?) {
235 | self.fullDictionary = dictionary
236 |
237 | // General
238 | self.applicationIdentifer = dictionary?[applicationIdentiferKey] as? String
239 | self.betaReportsActive = dictionary?[betaReportsActiveKey] as? Bool ?? false
240 | self.getTaskAllow = dictionary?[getTaskAllowKey] as? Bool ?? false
241 | self.keychainAccessGroups = dictionary?[keychainAccessGroupsKey] as? [String] ?? []
242 | self.teamIdentifier = dictionary?[teamIdKey] as? String
243 |
244 | // Push
245 | self.apsEnvironment = dictionary?[apsEnvironmentKey] as? String ?? ""
246 |
247 | // iCloud
248 | self.ubiquityStoreIdentifier = dictionary?[ubiquityStoreIdentifierKey] as? String
249 |
250 | if (self.fullDictionary == nil || self.applicationIdentifer == nil) {
251 | return nil
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/ResignForiOS/MainSignView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainSignView.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 |
10 | import Cocoa
11 |
12 | public extension NSPasteboard.PasteboardType {
13 | public static var kUrl: NSPasteboard.PasteboardType {
14 | return self.init(kUTTypeURL as String)
15 | }
16 | public static var kFilenames: NSPasteboard.PasteboardType {
17 | return self.init("NSFilenamesPboardType")
18 | }
19 | public static var kFileUrl: NSPasteboard.PasteboardType {
20 | return self.init(kUTTypeFileURL as String)
21 | }
22 | }
23 |
24 |
25 | class MainSignView: NSView {
26 |
27 | let securityPath = "/usr/bin/security"
28 | let defaults = UserDefaults()
29 | let fileManager = FileManager.default
30 |
31 | //MARK: IBOutlets
32 | @IBOutlet var inputFileField: NSTextField!
33 |
34 | @IBOutlet var profileSelcetPop: NSPopUpButton!
35 | @IBOutlet var codeSignCertsPop: NSPopUpButton!
36 |
37 | @IBOutlet var newBundleIDField: NSTextField!
38 | @IBOutlet var appDisplayName: NSTextField!
39 | @IBOutlet var appShortVersion: NSTextField!
40 | @IBOutlet var appVersion: NSTextField!
41 |
42 | @IBOutlet var StartButton: NSButton!
43 | @IBOutlet var StatusLabel: NSTextField!
44 |
45 | //MARK: Variables
46 | var provisioningProfiles: [Profile] = ProfileManager().updateProfiles()
47 | var codesigningCerts: [String] = []
48 |
49 | var currSelectInput: String? = nil {
50 | didSet {
51 | if currSelectInput != oldValue {
52 | inputFileField.stringValue = currSelectInput ?? ""
53 | }
54 | }
55 | }
56 |
57 | var currSelectProfile: Profile? {
58 | didSet {
59 | newBundleIDField.stringValue = currSelectProfile?.bundleID ?? ""
60 | }
61 | }
62 |
63 | var currSelectCert: String? = nil
64 | var currSelectOutput: String?
65 | fileprivate var openByTerminal = false
66 |
67 | //MARK: Drag / Drop
68 | fileprivate var fileTypes: [String] = ["ipa","app","mobileprovision", "xcarchive"]
69 | fileprivate var fileTypeIsOk = false
70 |
71 |
72 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
73 | if checkExtension(sender) == true {
74 | fileTypeIsOk = true
75 | return .copy
76 | } else {
77 | fileTypeIsOk = false
78 | return NSDragOperation()
79 | }
80 | }
81 |
82 | override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
83 | if fileTypeIsOk {
84 | return .copy
85 | } else {
86 | return NSDragOperation()
87 | }
88 | }
89 |
90 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
91 | let pasteboard = sender.draggingPasteboard()
92 | if let board = pasteboard.propertyList(forType: .kFilenames) as? NSArray {
93 | if let filePath = board[0] as? String {
94 | fileDropped(filePath)
95 | return true
96 | }
97 | }
98 | return false
99 | }
100 |
101 | func checkExtension(_ drag: NSDraggingInfo) -> Bool {
102 | if let board = drag.draggingPasteboard().propertyList(forType: .kFilenames) as? NSArray,
103 | let path = board[0] as? String {
104 | return fileTypes.contains(path.pathExtension.lowercased())
105 | }
106 | return false
107 | }
108 |
109 |
110 | func fileDropped(_ filePath: String) {
111 | switch filePath.pathExtension.lowercased() {
112 | case "ipa", "app", "xcarchive":
113 | currSelectInput = filePath
114 | break
115 | default: break
116 | }
117 | }
118 |
119 | override func awakeFromNib() {
120 | super.awakeFromNib()
121 |
122 | populateProvisioningProfiles()
123 | populateCodesigningCerts()
124 |
125 | setStatus("Ready")
126 |
127 | let xcsCmd = "/usr/bin/xcode-select"
128 | let checkCodeTask = Process().execute(xcsCmd, workingDirectory: nil, arguments: ["-p"])
129 | if checkCodeTask.status != 0 {
130 | _ = Process().execute(xcsCmd, workingDirectory: nil, arguments: ["--install"])
131 | NSApplication.shared.terminate(self)
132 | }
133 | setStatus("check XCode Task over")
134 | }
135 |
136 |
137 | func populateProvisioningProfiles() {
138 | var items = ["Re-Sign Only"]
139 | for profile in provisioningProfiles {
140 | items.append("\(profile.name) (\(profile.teamIdentifiers.first!))")
141 | }
142 | profileSelcetPop.removeAllItems()
143 | profileSelcetPop.addItems(withTitles:items)
144 | profileSelcetPop.selectItem(at: 0)
145 | }
146 |
147 |
148 | func populateCodesigningCerts() {
149 | codeSignCertsPop.removeAllItems()
150 | let securityResult = Process().execute(securityPath, workingDirectory: nil, arguments: ["find-identity","-v","-p","codesigning"])
151 | if securityResult.output.count < 1 {
152 | showCodesignCertsErrorAlert()
153 | return
154 | }
155 | codesigningCerts = securityResult.output.split(separator: "\"").map{String($0)}.filter({ $0.contains("iPhone")})
156 | for cert in self.codesigningCerts {
157 | codeSignCertsPop.addItem(withTitle: cert)
158 | }
159 | }
160 |
161 |
162 | func showCodesignCertsErrorAlert(){
163 | let alert = NSAlert()
164 | alert.messageText = "No codesigning certificates found"
165 | alert.informativeText = "I can attempt to fix this automatically, would you like me to try?"
166 | alert.addButton(withTitle: "Yes")
167 | alert.addButton(withTitle: "No")
168 | if alert.runModal() == .alertFirstButtonReturn {
169 | updateAppleCer()
170 | populateCodesigningCerts()
171 | }
172 | }
173 |
174 |
175 | @IBAction func doSign(_ sender: NSButton) {
176 | if codesigningCerts.count == 0 {
177 | showCodesignCertsErrorAlert()
178 | } else {
179 |
180 | NSApplication.shared.windows[0].makeFirstResponder(self)
181 | let saveDialog = NSSavePanel()
182 | saveDialog.allowedFileTypes = ["ipa","app"]
183 | saveDialog.nameFieldStringValue = inputFileField.stringValue.lastPathComponent.deletePathExtension
184 | if saveDialog.runModal() == .OK {
185 | currSelectOutput = saveDialog.url!.path
186 | Thread.detachNewThreadSelector(#selector(self.signingThread), toTarget: self, with: nil)
187 | } else {
188 | currSelectOutput = nil
189 | }
190 | }
191 | }
192 |
193 |
194 | @objc func signingThread() {
195 |
196 | //MARK: Set up variables
197 | var newBundleID :String = ""
198 | var newDisplayName :String = ""
199 | var newShortVersion :String = ""
200 | var newVersion :String = ""
201 | var inputFieldValue :String = ""
202 |
203 | DispatchQueue.main.sync {
204 | newBundleID = self.newBundleIDField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
205 | newDisplayName = self.appDisplayName.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
206 | newShortVersion = self.appShortVersion.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
207 | newVersion = self.appVersion.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
208 | inputFieldValue = self.inputFileField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
209 | }
210 |
211 | let inputFilePath :String = currSelectInput ?? inputFieldValue
212 |
213 | // check inputFile path is a dir, return directly
214 | if fileManager.fileExists(atPath: inputFilePath, isDirectory: nil) == false {
215 | DispatchQueue.main.async(execute: {
216 | let alert = NSAlert()
217 | alert.messageText = "Input file not found"
218 | alert.addButton(withTitle: "OK")
219 | alert.informativeText = "The file \(inputFilePath) could not be found"
220 | alert.runModal()
221 | })
222 | return
223 | }
224 |
225 | // Check provisioningFile
226 | if let currProfile = currSelectProfile {
227 | if currProfile.isExpired == true {
228 | setStatus("Provisioning profile expired")
229 | return
230 | }
231 | }
232 |
233 | let signer = CodeSigner()
234 | signer.delegate = self
235 | signer.sign(inputFile: inputFilePath, provisioningFile: currSelectProfile?.filePath, newBundleID: newBundleID, newDisplayName: newDisplayName, newVersion: newVersion, newShortVersion: newShortVersion, signingCertificate: currSelectCert!, outputFile: currSelectOutput!,openByTerminal: openByTerminal)
236 | }
237 |
238 |
239 | //MARK: IBActions
240 | @IBAction func chooseProvisioningProfile(_ sender: NSPopUpButton) {
241 | let title = sender.selectedItem?.title ?? "";
242 | let list = provisioningProfiles.filter { (profile) -> Bool in
243 | return title.contains(profile.name)
244 | }
245 |
246 | if list.count == 0 {
247 | newBundleIDField.stringValue = ""
248 | } else {
249 | let selectProfile = list.first!
250 | currSelectProfile = selectProfile
251 | let matchCer = selectProfile.developerCertificates.first
252 | if let matchCer = matchCer, codesigningCerts.contains(matchCer) {
253 | codeSignCertsPop.selectItem(withTitle: matchCer)
254 | currSelectCert = matchCer
255 | } else {
256 | //MARK: todo // 提醒用户没有匹配的证书
257 | }
258 | }
259 | }
260 |
261 | @IBAction func selectInput(_ sender: AnyObject) {
262 | let openDialog = NSOpenPanel()
263 | openDialog.canChooseFiles = true
264 | openDialog.canChooseDirectories = false
265 | openDialog.allowsMultipleSelection = false
266 | openDialog.allowsOtherFileTypes = false
267 | openDialog.allowedFileTypes = ["ipa", "IPA", "app", "APP", "xcarchive"]
268 | openDialog.runModal()
269 | if let filename = openDialog.urls.first {
270 | currSelectInput = filename.path
271 | }
272 | }
273 |
274 |
275 | @IBAction func chooseSigningCertificate (_ sender: NSPopUpButton) {
276 | currSelectCert = sender.selectedItem?.title
277 | setStatus("Set currSelectCert: \(currSelectCert!)")
278 | }
279 |
280 |
281 | @IBAction func openOutputFile(_ sender: NSButton) {
282 | if let outputFile = self.currSelectOutput {
283 | if fileManager.fileExists(atPath: outputFile) {
284 | NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: outputFile)])
285 | }
286 | }
287 | }
288 |
289 |
290 | @IBAction func openLogFile(_ sender: Any) {
291 | NSWorkspace.shared.openFile(Log.logName)
292 | }
293 |
294 |
295 | override init(frame frameRect: NSRect) {
296 | super.init(frame: frameRect)
297 | registerForDraggedTypes([.string, .tiff, .kUrl, .kFilenames])
298 | }
299 |
300 | required init?(coder: NSCoder) {
301 | super.init(coder: coder)
302 | registerForDraggedTypes([.string, .tiff, .kUrl, .kFilenames])
303 | }
304 |
305 | }
306 |
307 | extension MainSignView: CodeSignDelegate {
308 |
309 | func codeSignBegin(workingDir: String) {
310 | setStatus("CodeSign begin with workingDir: \(workingDir)")
311 | DispatchQueue.main.async {
312 | let hud = HProgregressHUD.showHUDAddedTo(view: self, animated: true)
313 | hud.label.stringValue = "CodeSigning"
314 | }
315 | }
316 |
317 | func codeSignLogRecord(logDes: String) {
318 | setStatus(logDes)
319 | }
320 |
321 | func codeSignError(errDes: String, tempDir: String) {
322 | setStatus(errDes)
323 | DispatchQueue.main.async {
324 | HProgregressHUD.HUDFor(view: self)?.hideAnimated(true)
325 | }
326 | cleanup(tempDir)
327 | }
328 |
329 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String) {
330 | cleanup(tempDir)
331 | DispatchQueue.main.async {
332 | HProgregressHUD.HUDFor(view: self)?.hideAnimated(true)
333 | }
334 | setStatus("CodeSigneEndSuccessed, output at \(outPutPath)")
335 | }
336 |
337 | func cleanup(_ dir: String) {
338 | do {
339 | setStatus("Deleting: \(dir)")
340 | try fileManager.removeItem(atPath: dir)
341 | } catch {
342 | setStatus("Deleting: \(dir) error")
343 | }
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/CoolResign/Core.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Core.swift
3 | // OptionKit
4 | //
5 | // Created by Salazar, Alexandros on 9/24/14.
6 | // Copyright (c) 2014 nomothetis. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | Eventually intends to be a getopt-compatible option parser.
13 | */
14 |
15 | public enum OptionTrigger : Equatable, CustomDebugStringConvertible, Hashable {
16 | case short(Character)
17 | case long(String)
18 | case mixed(Character, String)
19 |
20 | public var debugDescription:String {
21 | get {
22 | switch self {
23 | case .short(let c):
24 | return "-\(c)"
25 | case .long(let str):
26 | return "--\(str)"
27 | case .mixed(let c, let str):
28 | return "[-\(c)|--\(str)]"
29 | }
30 | }
31 | }
32 |
33 | public var usageDescription:String {
34 | get {
35 | switch self {
36 | case .short(let c):
37 | return "[-\(c)]"
38 | case .long(let str):
39 | return "[--\(str)]"
40 | case .mixed(let c, let str):
41 | return "[-\(c)|--\(str)]"
42 | }
43 | }
44 | }
45 |
46 | public var hashValue:Int {
47 | get {
48 | return self.usageDescription.hashValue
49 | }
50 | }
51 | }
52 |
53 | /// Describes an option for the parser.
54 | ///
55 | /// An Option consists of a trigger and a number of required parameters, which
56 | /// defaults to zero. It also has includes a description, which is empty by default. The
57 | /// description does not affect equality.
58 | public struct Option : Equatable, CustomStringConvertible, CustomDebugStringConvertible, Hashable {
59 | let trigger:OptionTrigger
60 | let numberOfParameters:Int
61 |
62 | /// The description of how the option works. This description has no bearing on equality.
63 | let helpDescription:String
64 |
65 | /// The designated initializer
66 | ///
67 | /// Creates an option definition from a trigger and a required number of parameters.
68 | ///
69 | /// - parameter trigger: the trigger that the parser will use to decide the option is
70 | /// being called.
71 | /// - parameter numberOfParameters: the number of required parameters. Defaults to 0.
72 | /// - parameter helpDescription: the string that will be displayed when the -h flag is triggered.
73 | ///
74 | /// - returns: An OptionDefinition suitable for use by an OptionParser
75 | public init(trigger trig:OptionTrigger, numberOfParameters num:Int = 0, helpDescription desc:String = "") {
76 | self.trigger = trig
77 | self.numberOfParameters = num
78 | self.helpDescription = desc
79 | }
80 |
81 | /// Determines if the given string matches this trigger.
82 | ///
83 | /// - parameter str: the string.
84 | /// - returns: `true` if the string matches this option's trigger, `false` otherwise.
85 | func matches(_ str:String) -> Bool {
86 | switch self.trigger {
87 | case .short(let char):
88 | return str == "-" + String(char)
89 | case .long(let longKey):
90 | return str == "--" + longKey
91 | case .mixed(let char, let longKey):
92 | return (str == "--" + longKey) || str == "-" + String(char)
93 | }
94 | }
95 |
96 | static func isValidOptionString(_ str:String) -> Bool{
97 | let length = str.characters.count
98 | if length < 2 {
99 | return false
100 | }
101 |
102 | if length == 2 {
103 | if str[str.startIndex] != "-" {
104 | return false
105 | }
106 |
107 | return str[str.characters.index(str.startIndex, offsetBy: 1)] != "-"
108 | }
109 |
110 | /* Okay, count greater than 2. Full option! */
111 | return str[str.startIndex ... str.characters.index(str.startIndex, offsetBy: 1)] == "--"
112 | }
113 |
114 | public var description: String {
115 | get {
116 | return "\(self.trigger) requires \(self.numberOfParameters) parameters"
117 | }
118 | }
119 |
120 | public var debugDescription:String {
121 | get {
122 | return "{ Opt: \(self.trigger), \(self.numberOfParameters) }"
123 | }
124 | }
125 |
126 | public var hashValue:Int {
127 | get {
128 | return self.debugDescription.hashValue
129 | }
130 | }
131 | }
132 |
133 | private struct OptionData : Equatable, CustomStringConvertible, CustomDebugStringConvertible {
134 | let option:Option
135 | let parameters:[String]
136 |
137 | fileprivate init(definition def:Option, parameters params:[String] = []) {
138 | self.option = def
139 | self.parameters = params
140 | }
141 |
142 | fileprivate var isValid:Bool {
143 | get {
144 | return parameters.count == option.numberOfParameters
145 | }
146 | }
147 |
148 | fileprivate var description: String {
149 | get {
150 | return "\(self.option), parameters \(parameters) are given"
151 | }
152 | }
153 |
154 | fileprivate var debugDescription:String {
155 | get {
156 | return "{ OptionData:\n \(self.option.debugDescription)\n \(self.parameters)}"
157 | }
158 | }
159 | }
160 |
161 | /// Represents the result of a successful parse.
162 | ///
163 | /// The dictionary is a mapping of encountered options to their parameters, where no-parameter
164 | /// options map to an empty array.. The array is the list of remaining parameters.
165 | public typealias ParseData = ([Option:[String]], [String])
166 |
167 | /// The option parser.
168 | ///
169 | /// This is the workhorse of the library. It is initialized with a list of options and parses an
170 | /// array of strings assumed to be the call paramerers.
171 | public struct OptionParser {
172 | public let definitions:[Option]
173 |
174 | /// Initializes the parser.
175 | ///
176 | /// By default, each parser has an option triggered by `-h` and `--help`. It also provides
177 | /// a console-displayable string of the options via the `helpStringForCommandName` method.
178 | ///
179 | /// - parameter definitions: the option definitions to parse for.
180 | /// - returns: a parser
181 | public init(definitions defs:[Option] = []) {
182 | let helpOption = Option(trigger:.mixed("h", "help"), helpDescription: "Display command help.")
183 | if defs.contains(helpOption) {
184 | self.definitions = defs
185 | } else {
186 | self.definitions = defs + [helpOption]
187 | }
188 | }
189 |
190 | /// Returns a default help string based on the passed command name and the existing options.
191 | ///
192 | /// The string is suitable to be displayed on the command line and consists of multiple lines,
193 | /// all under 80 characters.
194 | ///
195 | /// - parameter commandName: the name of the command.
196 | /// - returns: an English-language string suitable for command-line display.
197 | public func helpStringForCommandName(_ commandName:String) -> String {
198 | let maximumLineWidth = 80
199 |
200 | // The leading string, to properly indent.
201 | var leadingString = " "
202 | for _ in 0.. ParseData {
233 | let normalizedParams = OptionParser.normalizeParameters(parameters)
234 | let firstCall = ([OptionData](), [String]())
235 |
236 | let (parsedOptions, args) = try normalizedParams.reduce(firstCall) { tuple, next in
237 |
238 | let (optArray, args) = tuple
239 | /* First check if we are in the process of parsing an option with parameters. */
240 | if let lastOpt = optArray.last {
241 |
242 | /* Since we have parsed an option already, let's check if it needs more parameters. */
243 | if lastOpt.option.numberOfParameters > lastOpt.parameters.count {
244 |
245 | /* The option expects parameters; parameters cannot look like option triggers. */
246 | if (Option.isValidOptionString(next)) {
247 | throw OptionKitError.invalidOption(description: "Option \(lastOpt) before option \(next) was declared")
248 | }
249 |
250 | /* Sanity prevails, the next element is not an option trigger. */
251 | let shortOptArray = optArray[0 ..< optArray.count - 1]
252 | let newOption = OptionData(definition: lastOpt.option, parameters: lastOpt.parameters + [next])
253 | return (shortOptArray + [newOption], args)
254 | }
255 |
256 | /* No need for more parameters; parse the next option. */
257 | return try self.parseNewFlag(tuple, flagCandidate: next)
258 | }
259 |
260 | /* This is the first option. Parse it! */
261 | return try self.parseNewFlag(tuple, flagCandidate: next)
262 | }
263 |
264 | // We need to carry out one last check. Because of the way the above reduce works, it's
265 | // possible the very last option is in fact not valid. There are ways around that, but
266 | // that's actually slower than just checking the last element at the end.
267 | if let lastOpt = parsedOptions.last, !lastOpt.isValid {
268 | throw OptionKitError.invalidOption(description: "Option \(lastOpt)")
269 | }
270 |
271 | var dict = [Option:[String]]()
272 | for opt in parsedOptions {
273 | dict[opt.option] = opt.parameters
274 | }
275 | return (dict, args)
276 | }
277 |
278 | fileprivate func parseNewFlag(_ current: ([OptionData], [String]), flagCandidate:String) throws -> ([OptionData], [String]) {
279 | /* Does the next element want to be a flag? */
280 | if Option.isValidOptionString(flagCandidate) {
281 | for flag in self.definitions {
282 | if flag.matches(flagCandidate) {
283 | let newOption = OptionData(definition: flag, parameters: [])
284 | return (current.0 + [newOption], current.1)
285 | }
286 | }
287 |
288 | throw OptionKitError.invalidOption(description: "Invalid option: \(flagCandidate)")
289 | }
290 |
291 | return (current.0, current.1 + [flagCandidate])
292 | }
293 |
294 | static func normalizeParameters(_ parameters:[String]) -> [String] {
295 | return parameters.reduce([String]()) { memo, next in
296 | let index = next.characters.index(next.startIndex, offsetBy: 0)
297 | if next[index] != "-" {
298 | return memo + [next]
299 | }
300 |
301 | let secondIndex = next.characters.index(index, offsetBy: 1)
302 | if next[secondIndex] == "-" {
303 | /* Assume everything that follows is valid. */
304 | return memo + [next]
305 | }
306 |
307 | /* Okay, we have one or more single-character flags. */
308 | var params = [String]()
309 | for char in next[secondIndex.. Bool {
321 | switch (lhs, rhs) {
322 | case (.short(let x), .short(let y)):
323 | return x == y
324 | case (.long(let x), .long(let y)):
325 | return x == y
326 | case (.mixed(let x1, let x2), .mixed(let y1, let y2)):
327 | return (x1 == y1) && (x2 == y2)
328 | default:
329 | return false
330 | }
331 | }
332 |
333 | public func ==(lhs:Option, rhs:Option) -> Bool {
334 | return lhs.hashValue == rhs.hashValue
335 | }
336 |
337 | private func ==(lhs:OptionData, rhs:OptionData) -> Bool {
338 | return (lhs.option == rhs.option) && (lhs.parameters == rhs.parameters)
339 | }
340 |
341 | /// MARK: - Error types
342 |
343 | public enum OptionKitError: Error {
344 | case invalidOption(description: String)
345 | }
346 |
347 |
--------------------------------------------------------------------------------
/ResignForiOS/CodeSigner.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeSigner.swift
3 | // ResignForiOS
4 | //
5 | // Created by hanxiaoqing on 2018/1/23.
6 | // Copyright © 2018年 cheng. All rights reserved.
7 | //
8 |
9 |
10 | import Cocoa
11 |
12 |
13 | class Compress: NSObject {
14 |
15 | static let shared = Compress()
16 | private override init() {}
17 |
18 | let unzipPath = "/usr/bin/unzip"
19 | let zipPath = "/usr/bin/zip"
20 |
21 | @discardableResult
22 | func unzip(_ inputFile: String, outputPath: String) -> Bool {
23 | let unzipTask = Process().execute(unzipPath, workingDirectory: nil, arguments: ["-q", inputFile, "-d", outputPath])
24 | if unzipTask.status != 0 {
25 | setStatus("Error unzip \(inputFile)")
26 | return false
27 | }
28 | return true
29 | }
30 |
31 | @discardableResult
32 | func zip(_ inputPath: String, outputFile: String) -> Bool {
33 | let zipTask = Process().execute(zipPath, workingDirectory: inputPath, arguments: ["-qry", outputFile, "."])
34 | if zipTask.status != 0 {
35 | setStatus("Error zip path \(inputPath)")
36 | return false
37 | }
38 | return true
39 | }
40 | }
41 |
42 | protocol CodeSignDelegate {
43 | func codeSignBegin(workingDir: String)
44 | func codeSignLogRecord(logDes: String)
45 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String)
46 | func codeSignError(errDes: String, tempDir: String)
47 | }
48 |
49 |
50 | class CodeSigner: NSObject {
51 |
52 | let mktempPath = "/usr/bin/mktemp"
53 | let chmodPath = "/bin/chmod"
54 | let codesignPath = "/usr/bin/codesign"
55 |
56 | let fileManager = FileManager.default
57 |
58 | var delegate: CodeSignDelegate?
59 |
60 | func makeTempFolder() -> String? {
61 | let bundleID = Bundle.main.bundleIdentifier ?? "com.chengcheng.ResignForiOS"
62 | let tempTask = Process().execute(mktempPath, workingDirectory: nil, arguments: ["-d", "-t", bundleID])
63 | return tempTask.output
64 | }
65 |
66 | func checkInputAndHandel(_ input: String, _ workingDir: String) -> Bool {
67 | let payloadDir = workingDir.appendPathComponent("Payload/")
68 | let inputFileExt = input.pathExtension.lowercased()
69 | if inputFileExt == "ipa" {
70 | delegate?.codeSignLogRecord(logDes: "Unzip \(input) to \(workingDir)")
71 | Compress.shared.unzip(input, outputPath: workingDir)
72 | } else if inputFileExt == "app" {
73 | do {
74 | try fileManager.createDirectory(atPath: payloadDir, withIntermediateDirectories: true, attributes: nil)
75 | try fileManager.copyItem(atPath: input, toPath: payloadDir.appendPathComponent(input.lastPathComponent))
76 | delegate?.codeSignLogRecord(logDes: "Copying app to \(payloadDir)")
77 | } catch {
78 | delegate?.codeSignLogRecord(logDes: "Error copying app to \(payloadDir)")
79 | return false
80 | }
81 | } else if inputFileExt == "xcarchive" {
82 | do {
83 | try fileManager.createDirectory(atPath: payloadDir, withIntermediateDirectories: true, attributes: nil)
84 | let appPath = input.appendPathComponent("Products/Applications/")
85 | if let files = try? fileManager.contentsOfDirectory(atPath: appPath) {
86 | for file in files {
87 | try fileManager.copyItem(atPath: appPath + "/\(file)", toPath: payloadDir + "/\(file)")
88 | }
89 | }
90 | delegate?.codeSignLogRecord(logDes: "Copying xcarchive to to \(payloadDir)")
91 | } catch {
92 | delegate?.codeSignLogRecord(logDes: "Error copying xcarchive to to \(payloadDir)")
93 | return false
94 | }
95 | } else {
96 | delegate?.codeSignLogRecord(logDes: "Input file not support")
97 | return false
98 | }
99 | return true
100 | }
101 |
102 |
103 | //MARK: Copy Provisioning Profile
104 | func checkProfilePath(_ inputProfile: String?, _ oldProfilePath: String) -> String? {
105 | delegate?.codeSignLogRecord(logDes: "make sure which profile should be using")
106 | guard let inputProfile = inputProfile else {
107 | return oldProfilePath
108 | }
109 | do {
110 | if fileManager.fileExists(atPath: oldProfilePath) {
111 | try fileManager.removeItem(atPath: oldProfilePath)
112 | }
113 | try fileManager.copyItem(atPath: inputProfile, toPath: oldProfilePath)
114 | return inputProfile
115 | } catch {
116 | delegate?.codeSignLogRecord(logDes: "Error copying provisioning profile \(error.localizedDescription)")
117 | return nil
118 | }
119 | }
120 |
121 | func sign(inputFile: String, provisioningFile: String?, newBundleID: String, newDisplayName: String, newVersion: String, newShortVersion: String, signingCertificate : String, outputFile: String, openByTerminal: Bool) {
122 |
123 | //MARK: Create working temp folder
124 | var tempFolder: String = makeTempFolder()!
125 |
126 | let workingDirectory = tempFolder.appendPathComponent("out")
127 | let entitlementsPlist = tempFolder.appendPathComponent("entitlements.plist")
128 | let payloadDirectory = workingDirectory.appendPathComponent("Payload/")
129 |
130 | delegate?.codeSignBegin(workingDir: workingDirectory)
131 |
132 | //MARK: Codesign Test
133 |
134 | //MARK: Create workingDirectory Temp Directory
135 | do {
136 | try fileManager.createDirectory(atPath: workingDirectory, withIntermediateDirectories: true, attributes: nil)
137 | } catch {
138 | let errDescr = "Create workingDir error"
139 | delegate?.codeSignError(errDes: errDescr, tempDir: tempFolder)
140 | return
141 | }
142 |
143 | if checkInputAndHandel(inputFile, workingDirectory) == false {
144 | delegate?.codeSignError(errDes: "CheckInput: \(inputFile) fail", tempDir: tempFolder)
145 | return
146 | }
147 |
148 | // Loop through app bundles in payload directory
149 | let files = try? fileManager.contentsOfDirectory(atPath: payloadDirectory)
150 | var isDirectory: ObjCBool = true
151 |
152 | for file in files! {
153 | fileManager.fileExists(atPath: payloadDirectory.appendPathComponent(file), isDirectory: &isDirectory)
154 | if !isDirectory.boolValue { continue }
155 |
156 | //MARK: Bundle variables setup
157 | let appFilePath = payloadDirectory.appendPathComponent(file)
158 | let infoPlistPath = appFilePath.appendPathComponent("Info.plist")
159 | let provisioningPath = appFilePath.appendPathComponent("embedded.mobileprovision")
160 |
161 | let currInfoPlist = PlistHelper(plistPath: infoPlistPath)
162 |
163 | //MARK: Delete CFBundleResourceSpecification from Info.plist
164 | currInfoPlist.delete(key: "CFBundleResourceSpecification")
165 |
166 |
167 | let profilePath = checkProfilePath(provisioningFile, provisioningPath)
168 | guard let profile = Profile(filePath: profilePath!) else {
169 | delegate?.codeSignError(errDes: "Creat Profile fail", tempDir: tempFolder)
170 | continue
171 | }
172 |
173 | var entitleDic = profile.entitlements.fullDictionary
174 | let xcentPath = appFilePath.appendPathComponent("archived-expanded-entitlements.xcent")
175 | NSDictionary(contentsOfFile: xcentPath)?.forEach {
176 | let key = $0 as! String
177 | let value = $1 as AnyObject
178 | entitleDic?.updateValue(value, forKey: key)
179 | }
180 | (entitleDic! as NSDictionary).write(toFile: entitlementsPlist, atomically: true)
181 |
182 |
183 | //MARK: Make sure that the executable is well... executable.
184 | if let bundleExecutable = currInfoPlist.bundleExecutable {
185 | _ = Process().execute(chmodPath, workingDirectory: nil, arguments: ["755", appFilePath.appendPathComponent(bundleExecutable)])
186 | }
187 |
188 | //MARK: Change Application ID
189 | if newBundleID != "" {
190 | if let oldAppID = currInfoPlist.bundleIdentifier {
191 | // recursive func
192 | func changeAppexID(_ appexFile: String) {
193 | let appexPlist = PlistHelper(plistPath: appexFile + "/Info.plist")
194 | if let appexBundleID = appexPlist.bundleIdentifier {
195 | let newAppexID = "\(newBundleID)\(appexBundleID.substring(from: oldAppID.endIndex))"
196 | delegate?.codeSignLogRecord(logDes: "Changing \(appexFile) bundleId to \(newAppexID)")
197 | appexPlist.bundleIdentifier = newAppexID
198 | }
199 | if let _ = appexPlist.wkAppBundleIdentifier {
200 | appexPlist.wkAppBundleIdentifier = newBundleID
201 | }
202 | recursiveDirectorySearch(appexFile, extensions: ["app"], found: changeAppexID)
203 | }
204 | // Search appex in current app file to changeAppID
205 | recursiveDirectorySearch(appFilePath, extensions: ["appex"], found: changeAppexID)
206 | }
207 | currInfoPlist.bundleIdentifier = newBundleID
208 | }
209 |
210 | //MARK: Change Display Name
211 | if newDisplayName != "" {
212 | currInfoPlist.bundleDisplayName = newDisplayName
213 | }
214 |
215 | //MARK: Change Version
216 | if newVersion != "" {
217 | currInfoPlist.bundleVersion = newVersion
218 | }
219 |
220 | //MARK: Change Short Version
221 | if newShortVersion != "" {
222 | currInfoPlist.shortBundleVersion = newShortVersion
223 | }
224 |
225 |
226 | //MARK: Codesigning - App
227 | let signableExts = ["dylib","so","0","vis","pvr","framework","appex","app"]
228 | recursiveDirectorySearch(appFilePath, extensions: signableExts) { file in
229 | codeSign(file, certificate: signingCertificate, entitlements: entitlementsPlist)
230 | }
231 | codeSign(appFilePath, certificate: signingCertificate, entitlements: entitlementsPlist)
232 |
233 | //MARK: Codesigning - Verification
234 | let verificationTask = Process().execute(codesignPath, workingDirectory: nil, arguments: ["-v", appFilePath])
235 | if verificationTask.status != 0 {
236 | //MARK: alert if certificate expired
237 | self.delegate?.codeSignError(errDes: "verifying code sign fail:\(verificationTask.output)", tempDir: tempFolder)
238 | DispatchQueue.main.async(execute: {
239 | let alert = NSAlert()
240 | alert.addButton(withTitle: "OK")
241 | alert.messageText = "Error verifying code signature!"
242 | alert.informativeText = verificationTask.output
243 | alert.alertStyle = .critical
244 | alert.runModal()
245 | })
246 | return
247 | }
248 | }
249 |
250 | //MARK: Packaging
251 | //Check if output already exists and delete if so
252 | if fileManager.fileExists(atPath: outputFile) {
253 | do {
254 | try fileManager.removeItem(atPath: outputFile)
255 | } catch {
256 | delegate?.codeSignError(errDes: "Delete exists output file fail", tempDir: tempFolder)
257 | return
258 | }
259 | }
260 |
261 | delegate?.codeSignLogRecord(logDes: "Packaging IPA: \(outputFile)")
262 |
263 | Compress.shared.zip(workingDirectory, outputFile: outputFile)
264 |
265 | delegate?.codeSigneEndSuccessed(outPutPath: outputFile, tempDir: tempFolder)
266 |
267 | }
268 |
269 |
270 | //MARK: Codesigning
271 | func codeSign(_ file: String, certificate: String, entitlements: String?) {
272 | delegate?.codeSignLogRecord(logDes: "codeSign:\(file)")
273 | var arguments = ["-vvv","-fs",certificate,"--no-strict"]
274 | if fileManager.fileExists(atPath: entitlements!) {
275 | arguments.append("--entitlements=\(entitlements!)")
276 | }
277 | arguments.append(file)
278 | let codesignTask = Process().execute(codesignPath, workingDirectory: nil, arguments: arguments)
279 |
280 | if codesignTask.status != 0 {
281 | //MARK: alert if certificate expired
282 | delegate?.codeSignLogRecord(logDes: "Error codesigning \(file) error:\(codesignTask.output)")
283 | }
284 | }
285 |
286 |
287 | func recursiveDirectorySearch(_ path: String, extensions: [String], found: ((_ file: String) -> Void)) {
288 | if let files = try? fileManager.contentsOfDirectory(atPath: path) {
289 | var isDirectory: ObjCBool = true
290 | for file in files {
291 | let currentFile = path.appendPathComponent(file)
292 | fileManager.fileExists(atPath: currentFile, isDirectory: &isDirectory)
293 | if isDirectory.boolValue {
294 | recursiveDirectorySearch(currentFile, extensions: extensions, found: found)
295 | }
296 | if extensions.contains(file.pathExtension) {
297 | found(currentFile)
298 | }
299 | }
300 | }
301 | }
302 |
303 | }
304 |
--------------------------------------------------------------------------------
/ResignForiOS.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8A4B79862016D5E2007ACEA4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */; };
11 | 8A4B79882016D5E2007ACEA4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79872016D5E2007ACEA4 /* ViewController.swift */; };
12 | 8A4B798A2016D5E2007ACEA4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */; };
13 | 8A4B798D2016D5E2007ACEA4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */; };
14 | 8A4B799B2016D6F5007ACEA4 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */; };
15 | 8A4B799C2016D6F5007ACEA4 /* CodeSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */; };
16 | 8A4B799D2016D6F5007ACEA4 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */; };
17 | 8A4B799E2016D6F5007ACEA4 /* PlistHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */; };
18 | 8A4B799F2016D6F5007ACEA4 /* ProcessTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */; };
19 | 8A4B79A02016D6F5007ACEA4 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B799A2016D6F5007ACEA4 /* Tools.swift */; };
20 | 8A4B79A22016D71E007ACEA4 /* UpdateAppleCer.sh in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */; };
21 | 8A4B79A42016DBB6007ACEA4 /* MainSignView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */; };
22 | 8A66D1F8203ECAE200A6D27A /* HProgregressHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */; };
23 | 8AF9995F20AD85EB00661F05 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF9995E20AD85EB00661F05 /* main.swift */; };
24 | 8AF9996320AD868F00661F05 /* CodeSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */; };
25 | 8AF9996420AD869100661F05 /* PlistHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */; };
26 | 8AF9996520AD869800661F05 /* ProcessTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */; };
27 | 8AF9996620AD869D00661F05 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */; };
28 | 8AF9996720AD86A000661F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */; };
29 | 8AF9996820AD86A900661F05 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B799A2016D6F5007ACEA4 /* Tools.swift */; };
30 | 8AF9996A20AD8E7000661F05 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF9996920AD8E7000661F05 /* Core.swift */; };
31 | /* End PBXBuildFile section */
32 |
33 | /* Begin PBXCopyFilesBuildPhase section */
34 | 8AF9995A20AD85EB00661F05 /* CopyFiles */ = {
35 | isa = PBXCopyFilesBuildPhase;
36 | buildActionMask = 2147483647;
37 | dstPath = /usr/share/man/man1/;
38 | dstSubfolderSpec = 0;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 1;
42 | };
43 | /* End PBXCopyFilesBuildPhase section */
44 |
45 | /* Begin PBXFileReference section */
46 | 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResignForiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
48 | 8A4B79872016D5E2007ACEA4 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
49 | 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 8A4B798C2016D5E2007ACEA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
51 | 8A4B798E2016D5E2007ACEA4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | 8A4B798F2016D5E2007ACEA4 /* ResignForiOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ResignForiOS.entitlements; sourceTree = ""; };
53 | 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileManager.swift; sourceTree = ""; };
54 | 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeSigner.swift; sourceTree = ""; };
55 | 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; };
56 | 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistHelper.swift; sourceTree = ""; };
57 | 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessTask.swift; sourceTree = ""; };
58 | 8A4B799A2016D6F5007ACEA4 /* Tools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tools.swift; sourceTree = ""; };
59 | 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = UpdateAppleCer.sh; sourceTree = ""; };
60 | 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainSignView.swift; sourceTree = ""; };
61 | 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HProgregressHUD.swift; sourceTree = ""; };
62 | 8AF9995C20AD85EB00661F05 /* CoolResign */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CoolResign; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 8AF9995E20AD85EB00661F05 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
64 | 8AF9996920AD8E7000661F05 /* Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = ""; };
65 | /* End PBXFileReference section */
66 |
67 | /* Begin PBXFrameworksBuildPhase section */
68 | 8A4B797F2016D5E2007ACEA4 /* Frameworks */ = {
69 | isa = PBXFrameworksBuildPhase;
70 | buildActionMask = 2147483647;
71 | files = (
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | 8AF9995920AD85EB00661F05 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | /* End PBXFrameworksBuildPhase section */
83 |
84 | /* Begin PBXGroup section */
85 | 8A4B79792016D5E2007ACEA4 = {
86 | isa = PBXGroup;
87 | children = (
88 | 8A4B79842016D5E2007ACEA4 /* ResignForiOS */,
89 | 8AF9995D20AD85EB00661F05 /* CoolResign */,
90 | 8A4B79832016D5E2007ACEA4 /* Products */,
91 | );
92 | sourceTree = "";
93 | };
94 | 8A4B79832016D5E2007ACEA4 /* Products */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */,
98 | 8AF9995C20AD85EB00661F05 /* CoolResign */,
99 | );
100 | name = Products;
101 | sourceTree = "";
102 | };
103 | 8A4B79842016D5E2007ACEA4 /* ResignForiOS */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */,
107 | 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */,
108 | 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */,
109 | 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */,
110 | 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */,
111 | 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */,
112 | 8A4B799A2016D6F5007ACEA4 /* Tools.swift */,
113 | 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */,
114 | 8A4B79872016D5E2007ACEA4 /* ViewController.swift */,
115 | 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */,
116 | 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */,
117 | 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */,
118 | 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */,
119 | 8A4B798E2016D5E2007ACEA4 /* Info.plist */,
120 | 8A4B798F2016D5E2007ACEA4 /* ResignForiOS.entitlements */,
121 | );
122 | path = ResignForiOS;
123 | sourceTree = "";
124 | };
125 | 8AF9995D20AD85EB00661F05 /* CoolResign */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 8AF9996920AD8E7000661F05 /* Core.swift */,
129 | 8AF9995E20AD85EB00661F05 /* main.swift */,
130 | );
131 | path = CoolResign;
132 | sourceTree = "";
133 | };
134 | /* End PBXGroup section */
135 |
136 | /* Begin PBXNativeTarget section */
137 | 8A4B79812016D5E2007ACEA4 /* ResignForiOS */ = {
138 | isa = PBXNativeTarget;
139 | buildConfigurationList = 8A4B79922016D5E2007ACEA4 /* Build configuration list for PBXNativeTarget "ResignForiOS" */;
140 | buildPhases = (
141 | 8A4B797E2016D5E2007ACEA4 /* Sources */,
142 | 8A4B797F2016D5E2007ACEA4 /* Frameworks */,
143 | 8A4B79802016D5E2007ACEA4 /* Resources */,
144 | );
145 | buildRules = (
146 | );
147 | dependencies = (
148 | );
149 | name = ResignForiOS;
150 | productName = ResignForiOS;
151 | productReference = 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */;
152 | productType = "com.apple.product-type.application";
153 | };
154 | 8AF9995B20AD85EB00661F05 /* CoolResign */ = {
155 | isa = PBXNativeTarget;
156 | buildConfigurationList = 8AF9996220AD85EB00661F05 /* Build configuration list for PBXNativeTarget "CoolResign" */;
157 | buildPhases = (
158 | 8AF9995820AD85EB00661F05 /* Sources */,
159 | 8AF9995920AD85EB00661F05 /* Frameworks */,
160 | 8AF9995A20AD85EB00661F05 /* CopyFiles */,
161 | );
162 | buildRules = (
163 | );
164 | dependencies = (
165 | );
166 | name = CoolResign;
167 | productName = CoolResign;
168 | productReference = 8AF9995C20AD85EB00661F05 /* CoolResign */;
169 | productType = "com.apple.product-type.tool";
170 | };
171 | /* End PBXNativeTarget section */
172 |
173 | /* Begin PBXProject section */
174 | 8A4B797A2016D5E2007ACEA4 /* Project object */ = {
175 | isa = PBXProject;
176 | attributes = {
177 | LastSwiftUpdateCheck = 0930;
178 | LastUpgradeCheck = 0920;
179 | ORGANIZATIONNAME = cheng;
180 | TargetAttributes = {
181 | 8A4B79812016D5E2007ACEA4 = {
182 | CreatedOnToolsVersion = 9.2;
183 | ProvisioningStyle = Manual;
184 | SystemCapabilities = {
185 | com.apple.Sandbox = {
186 | enabled = 0;
187 | };
188 | };
189 | };
190 | 8AF9995B20AD85EB00661F05 = {
191 | CreatedOnToolsVersion = 9.3;
192 | ProvisioningStyle = Automatic;
193 | };
194 | };
195 | };
196 | buildConfigurationList = 8A4B797D2016D5E2007ACEA4 /* Build configuration list for PBXProject "ResignForiOS" */;
197 | compatibilityVersion = "Xcode 8.0";
198 | developmentRegion = en;
199 | hasScannedForEncodings = 0;
200 | knownRegions = (
201 | en,
202 | Base,
203 | );
204 | mainGroup = 8A4B79792016D5E2007ACEA4;
205 | productRefGroup = 8A4B79832016D5E2007ACEA4 /* Products */;
206 | projectDirPath = "";
207 | projectRoot = "";
208 | targets = (
209 | 8A4B79812016D5E2007ACEA4 /* ResignForiOS */,
210 | 8AF9995B20AD85EB00661F05 /* CoolResign */,
211 | );
212 | };
213 | /* End PBXProject section */
214 |
215 | /* Begin PBXResourcesBuildPhase section */
216 | 8A4B79802016D5E2007ACEA4 /* Resources */ = {
217 | isa = PBXResourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 8A4B798A2016D5E2007ACEA4 /* Assets.xcassets in Resources */,
221 | 8A4B79A22016D71E007ACEA4 /* UpdateAppleCer.sh in Resources */,
222 | 8A4B798D2016D5E2007ACEA4 /* Main.storyboard in Resources */,
223 | );
224 | runOnlyForDeploymentPostprocessing = 0;
225 | };
226 | /* End PBXResourcesBuildPhase section */
227 |
228 | /* Begin PBXSourcesBuildPhase section */
229 | 8A4B797E2016D5E2007ACEA4 /* Sources */ = {
230 | isa = PBXSourcesBuildPhase;
231 | buildActionMask = 2147483647;
232 | files = (
233 | 8A4B79882016D5E2007ACEA4 /* ViewController.swift in Sources */,
234 | 8A4B79A42016DBB6007ACEA4 /* MainSignView.swift in Sources */,
235 | 8A4B799F2016D6F5007ACEA4 /* ProcessTask.swift in Sources */,
236 | 8A4B799B2016D6F5007ACEA4 /* ProfileManager.swift in Sources */,
237 | 8A66D1F8203ECAE200A6D27A /* HProgregressHUD.swift in Sources */,
238 | 8A4B799D2016D6F5007ACEA4 /* StringExtension.swift in Sources */,
239 | 8A4B799C2016D6F5007ACEA4 /* CodeSigner.swift in Sources */,
240 | 8A4B799E2016D6F5007ACEA4 /* PlistHelper.swift in Sources */,
241 | 8A4B79862016D5E2007ACEA4 /* AppDelegate.swift in Sources */,
242 | 8A4B79A02016D6F5007ACEA4 /* Tools.swift in Sources */,
243 | );
244 | runOnlyForDeploymentPostprocessing = 0;
245 | };
246 | 8AF9995820AD85EB00661F05 /* Sources */ = {
247 | isa = PBXSourcesBuildPhase;
248 | buildActionMask = 2147483647;
249 | files = (
250 | 8AF9996320AD868F00661F05 /* CodeSigner.swift in Sources */,
251 | 8AF9996620AD869D00661F05 /* ProfileManager.swift in Sources */,
252 | 8AF9996720AD86A000661F05 /* StringExtension.swift in Sources */,
253 | 8AF9996420AD869100661F05 /* PlistHelper.swift in Sources */,
254 | 8AF9996820AD86A900661F05 /* Tools.swift in Sources */,
255 | 8AF9996520AD869800661F05 /* ProcessTask.swift in Sources */,
256 | 8AF9996A20AD8E7000661F05 /* Core.swift in Sources */,
257 | 8AF9995F20AD85EB00661F05 /* main.swift in Sources */,
258 | );
259 | runOnlyForDeploymentPostprocessing = 0;
260 | };
261 | /* End PBXSourcesBuildPhase section */
262 |
263 | /* Begin PBXVariantGroup section */
264 | 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */ = {
265 | isa = PBXVariantGroup;
266 | children = (
267 | 8A4B798C2016D5E2007ACEA4 /* Base */,
268 | );
269 | name = Main.storyboard;
270 | sourceTree = "";
271 | };
272 | /* End PBXVariantGroup section */
273 |
274 | /* Begin XCBuildConfiguration section */
275 | 8A4B79902016D5E2007ACEA4 /* Debug */ = {
276 | isa = XCBuildConfiguration;
277 | buildSettings = {
278 | ALWAYS_SEARCH_USER_PATHS = NO;
279 | CLANG_ANALYZER_NONNULL = YES;
280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
282 | CLANG_CXX_LIBRARY = "libc++";
283 | CLANG_ENABLE_MODULES = YES;
284 | CLANG_ENABLE_OBJC_ARC = YES;
285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
286 | CLANG_WARN_BOOL_CONVERSION = YES;
287 | CLANG_WARN_COMMA = YES;
288 | CLANG_WARN_CONSTANT_CONVERSION = YES;
289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
291 | CLANG_WARN_EMPTY_BODY = YES;
292 | CLANG_WARN_ENUM_CONVERSION = YES;
293 | CLANG_WARN_INFINITE_RECURSION = YES;
294 | CLANG_WARN_INT_CONVERSION = YES;
295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
299 | CLANG_WARN_STRICT_PROTOTYPES = YES;
300 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
302 | CLANG_WARN_UNREACHABLE_CODE = YES;
303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
304 | CODE_SIGN_IDENTITY = "-";
305 | COPY_PHASE_STRIP = NO;
306 | DEBUG_INFORMATION_FORMAT = dwarf;
307 | ENABLE_STRICT_OBJC_MSGSEND = YES;
308 | ENABLE_TESTABILITY = YES;
309 | GCC_C_LANGUAGE_STANDARD = gnu11;
310 | GCC_DYNAMIC_NO_PIC = NO;
311 | GCC_NO_COMMON_BLOCKS = YES;
312 | GCC_OPTIMIZATION_LEVEL = 0;
313 | GCC_PREPROCESSOR_DEFINITIONS = (
314 | "DEBUG=1",
315 | "$(inherited)",
316 | );
317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
319 | GCC_WARN_UNDECLARED_SELECTOR = YES;
320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
321 | GCC_WARN_UNUSED_FUNCTION = YES;
322 | GCC_WARN_UNUSED_VARIABLE = YES;
323 | MACOSX_DEPLOYMENT_TARGET = 10.10;
324 | MTL_ENABLE_DEBUG_INFO = YES;
325 | ONLY_ACTIVE_ARCH = YES;
326 | SDKROOT = macosx;
327 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
328 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
329 | };
330 | name = Debug;
331 | };
332 | 8A4B79912016D5E2007ACEA4 /* Release */ = {
333 | isa = XCBuildConfiguration;
334 | buildSettings = {
335 | ALWAYS_SEARCH_USER_PATHS = NO;
336 | CLANG_ANALYZER_NONNULL = YES;
337 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
339 | CLANG_CXX_LIBRARY = "libc++";
340 | CLANG_ENABLE_MODULES = YES;
341 | CLANG_ENABLE_OBJC_ARC = YES;
342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
343 | CLANG_WARN_BOOL_CONVERSION = YES;
344 | CLANG_WARN_COMMA = YES;
345 | CLANG_WARN_CONSTANT_CONVERSION = YES;
346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
347 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
348 | CLANG_WARN_EMPTY_BODY = YES;
349 | CLANG_WARN_ENUM_CONVERSION = YES;
350 | CLANG_WARN_INFINITE_RECURSION = YES;
351 | CLANG_WARN_INT_CONVERSION = YES;
352 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
356 | CLANG_WARN_STRICT_PROTOTYPES = YES;
357 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
358 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
359 | CLANG_WARN_UNREACHABLE_CODE = YES;
360 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
361 | CODE_SIGN_IDENTITY = "-";
362 | COPY_PHASE_STRIP = NO;
363 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
364 | ENABLE_NS_ASSERTIONS = NO;
365 | ENABLE_STRICT_OBJC_MSGSEND = YES;
366 | GCC_C_LANGUAGE_STANDARD = gnu11;
367 | GCC_NO_COMMON_BLOCKS = YES;
368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
370 | GCC_WARN_UNDECLARED_SELECTOR = YES;
371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
372 | GCC_WARN_UNUSED_FUNCTION = YES;
373 | GCC_WARN_UNUSED_VARIABLE = YES;
374 | MACOSX_DEPLOYMENT_TARGET = 10.10;
375 | MTL_ENABLE_DEBUG_INFO = NO;
376 | SDKROOT = macosx;
377 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
378 | };
379 | name = Release;
380 | };
381 | 8A4B79932016D5E2007ACEA4 /* Debug */ = {
382 | isa = XCBuildConfiguration;
383 | buildSettings = {
384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
385 | CODE_SIGN_STYLE = Manual;
386 | COMBINE_HIDPI_IMAGES = YES;
387 | DEVELOPMENT_TEAM = "";
388 | INFOPLIST_FILE = ResignForiOS/Info.plist;
389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
390 | PRODUCT_BUNDLE_IDENTIFIER = com.chengcheng.ResignForiOS;
391 | PRODUCT_NAME = "$(TARGET_NAME)";
392 | PROVISIONING_PROFILE_SPECIFIER = "";
393 | SWIFT_VERSION = 4.0;
394 | };
395 | name = Debug;
396 | };
397 | 8A4B79942016D5E2007ACEA4 /* Release */ = {
398 | isa = XCBuildConfiguration;
399 | buildSettings = {
400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
401 | CODE_SIGN_STYLE = Manual;
402 | COMBINE_HIDPI_IMAGES = YES;
403 | DEVELOPMENT_TEAM = "";
404 | INFOPLIST_FILE = ResignForiOS/Info.plist;
405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
406 | PRODUCT_BUNDLE_IDENTIFIER = com.chengcheng.ResignForiOS;
407 | PRODUCT_NAME = "$(TARGET_NAME)";
408 | PROVISIONING_PROFILE_SPECIFIER = "";
409 | SWIFT_VERSION = 4.0;
410 | };
411 | name = Release;
412 | };
413 | 8AF9996020AD85EB00661F05 /* Debug */ = {
414 | isa = XCBuildConfiguration;
415 | buildSettings = {
416 | CLANG_ENABLE_OBJC_WEAK = YES;
417 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
418 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
419 | CODE_SIGN_STYLE = Automatic;
420 | MACOSX_DEPLOYMENT_TARGET = 10.13;
421 | PRODUCT_NAME = "$(TARGET_NAME)";
422 | SWIFT_VERSION = 4.2;
423 | };
424 | name = Debug;
425 | };
426 | 8AF9996120AD85EB00661F05 /* Release */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | CLANG_ENABLE_OBJC_WEAK = YES;
430 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
431 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
432 | CODE_SIGN_STYLE = Automatic;
433 | MACOSX_DEPLOYMENT_TARGET = 10.13;
434 | PRODUCT_NAME = "$(TARGET_NAME)";
435 | SWIFT_VERSION = 4.2;
436 | };
437 | name = Release;
438 | };
439 | /* End XCBuildConfiguration section */
440 |
441 | /* Begin XCConfigurationList section */
442 | 8A4B797D2016D5E2007ACEA4 /* Build configuration list for PBXProject "ResignForiOS" */ = {
443 | isa = XCConfigurationList;
444 | buildConfigurations = (
445 | 8A4B79902016D5E2007ACEA4 /* Debug */,
446 | 8A4B79912016D5E2007ACEA4 /* Release */,
447 | );
448 | defaultConfigurationIsVisible = 0;
449 | defaultConfigurationName = Release;
450 | };
451 | 8A4B79922016D5E2007ACEA4 /* Build configuration list for PBXNativeTarget "ResignForiOS" */ = {
452 | isa = XCConfigurationList;
453 | buildConfigurations = (
454 | 8A4B79932016D5E2007ACEA4 /* Debug */,
455 | 8A4B79942016D5E2007ACEA4 /* Release */,
456 | );
457 | defaultConfigurationIsVisible = 0;
458 | defaultConfigurationName = Release;
459 | };
460 | 8AF9996220AD85EB00661F05 /* Build configuration list for PBXNativeTarget "CoolResign" */ = {
461 | isa = XCConfigurationList;
462 | buildConfigurations = (
463 | 8AF9996020AD85EB00661F05 /* Debug */,
464 | 8AF9996120AD85EB00661F05 /* Release */,
465 | );
466 | defaultConfigurationIsVisible = 0;
467 | defaultConfigurationName = Release;
468 | };
469 | /* End XCConfigurationList section */
470 | };
471 | rootObject = 8A4B797A2016D5E2007ACEA4 /* Project object */;
472 | }
473 |
--------------------------------------------------------------------------------
/ResignForiOS/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
873 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
--------------------------------------------------------------------------------