Repository: Lakr233/ActionBee Branch: main Commit: b9807306513d Files: 286 Total size: 1.3 MB Directory structure: gitextract_cxsqbwhp/ ├── .gitignore ├── .gitmodules ├── .root ├── App/ │ └── Action/ │ ├── Action/ │ │ ├── Action.entitlements │ │ ├── Application/ │ │ │ ├── Action.entitlements │ │ │ ├── ActionApp.swift │ │ │ ├── AppDelegate.swift │ │ │ ├── AppSetup.swift │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AccentColor.colorset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Avatar.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── License.txt │ │ │ ├── en.lproj/ │ │ │ │ └── Localizable.strings │ │ │ └── zh-Hans.lproj/ │ │ │ └── Localizable.strings │ │ ├── Backend/ │ │ │ ├── Action/ │ │ │ │ ├── ActionManager+Artifact.swift │ │ │ │ ├── ActionManager+Event.swift │ │ │ │ ├── ActionManager+History.swift │ │ │ │ ├── ActionManager+Module.swift │ │ │ │ ├── ActionManager.swift │ │ │ │ ├── ActionModuleTemplates/ │ │ │ │ │ ├── ActionManager+Template.swift │ │ │ │ │ ├── Template+Executable.swift │ │ │ │ │ ├── Template+Node.swift │ │ │ │ │ ├── Template+Python.swift │ │ │ │ │ └── Template+Swift.swift │ │ │ │ └── ActionTemplates/ │ │ │ │ └── .templates │ │ │ ├── Config/ │ │ │ │ └── Config.swift │ │ │ ├── Executor/ │ │ │ │ └── Executor.swift │ │ │ ├── PasteboardManager/ │ │ │ │ ├── PasteboardManager+Event.swift │ │ │ │ └── PasteboardManager.swift │ │ │ └── StatusBarManager/ │ │ │ └── StatusBarManager.swift │ │ ├── Extension/ │ │ │ ├── AES.swift │ │ │ ├── Data.swift │ │ │ ├── DispatchQueue.swift │ │ │ ├── Notification.swift │ │ │ ├── Result.swift │ │ │ ├── Throttle.swift │ │ │ ├── UserDefault.swift │ │ │ └── View.swift │ │ └── Interface/ │ │ ├── ActionModule/ │ │ │ ├── ModuleCreateView.swift │ │ │ ├── ModuleEditView.swift │ │ │ ├── ModuleElementView.swift │ │ │ ├── ModuleImportView.swift │ │ │ └── ModuleManageView.swift │ │ ├── Effect/ │ │ │ └── RandomCodeTextView.swift │ │ ├── Generic/ │ │ │ ├── ApplicationView.swift │ │ │ ├── DiagnosticLogView.swift │ │ │ ├── HistoryView.swift │ │ │ ├── LicenseView.swift │ │ │ ├── MainView.swift │ │ │ ├── SettingView.swift │ │ │ ├── SidebarView.swift │ │ │ └── WelcomeView.swift │ │ ├── Menubar/ │ │ │ ├── Menubar.swift │ │ │ └── MenubarView.swift │ │ └── Toast/ │ │ └── Toast.swift │ └── Action.xcodeproj/ │ └── project.pbxproj ├── App.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── External/ │ ├── AuxiliaryExecute/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Package.swift │ │ ├── README.md │ │ ├── Sources/ │ │ │ └── AuxiliaryExecute/ │ │ │ ├── AuxiliaryExecute+Async.swift │ │ │ ├── AuxiliaryExecute+Shell.swift │ │ │ ├── AuxiliaryExecute+Spawn.swift │ │ │ └── AuxiliaryExecute.swift │ │ └── Tests/ │ │ └── AuxiliaryExecuteTests/ │ │ └── AuxiliaryExecuteTests.swift │ ├── Colorful/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Package.swift │ │ ├── README.md │ │ └── Sources/ │ │ └── Colorful/ │ │ ├── Colorful.swift │ │ ├── ColorfulView.swift │ │ └── PointRandomization.swift │ ├── KeychainAccess/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── Examples/ │ │ │ └── Example-iOS/ │ │ │ ├── Example-iOS/ │ │ │ │ ├── AccountsViewController.swift │ │ │ │ ├── AppDelegate.swift │ │ │ │ ├── Base.lproj/ │ │ │ │ │ ├── LaunchScreen.xib │ │ │ │ │ └── Main.storyboard │ │ │ │ ├── Example-iOS.entitlements │ │ │ │ ├── Images.xcassets/ │ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── InputViewController.swift │ │ │ └── Example-iOS.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ └── Example-iOS.xcscheme │ │ ├── KeychainAccess.podspec │ │ ├── KeychainAccess.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── LICENSE │ │ ├── Lib/ │ │ │ ├── Certificates/ │ │ │ │ ├── KeychainAccess_Tests.provisionprofile.enc │ │ │ │ ├── apple.cer │ │ │ │ ├── developer_id_app.p12.enc │ │ │ │ ├── iOS_Development.mobileprovision.enc │ │ │ │ ├── ios_developer.p12.enc │ │ │ │ └── tvOS_Development.mobileprovision.enc │ │ │ ├── Configurations/ │ │ │ │ ├── Base.xcconfig │ │ │ │ ├── Debug.xcconfig │ │ │ │ ├── KeychainAccess.xcconfig │ │ │ │ ├── Release.xcconfig │ │ │ │ ├── TestHost.xcconfig │ │ │ │ └── Tests.xcconfig │ │ │ ├── Gemfile │ │ │ ├── KeychainAccess/ │ │ │ │ ├── Info.plist │ │ │ │ ├── Keychain.swift │ │ │ │ └── KeychainAccess.h │ │ │ ├── KeychainAccess.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ ├── KeychainAccess.xcscheme │ │ │ │ └── TestHost.xcscheme │ │ │ ├── KeychainAccessTests/ │ │ │ │ ├── EnumTests.swift │ │ │ │ ├── ErrorTypeTests.swift │ │ │ │ ├── Info.plist │ │ │ │ ├── KeychainAccessTests.swift │ │ │ │ └── SharedCredentialTests.swift │ │ │ ├── Rakefile │ │ │ ├── Scripts/ │ │ │ │ ├── add_key.sh │ │ │ │ └── decode_cert.sh │ │ │ ├── TestHost/ │ │ │ │ ├── AppDelegate.swift │ │ │ │ ├── Assets.xcassets/ │ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Info.plist │ │ │ │ └── TestHost.entitlements │ │ │ └── TestHost-MacCatalyst/ │ │ │ ├── KeychainAccessTests-MacCatalyst/ │ │ │ │ ├── EnumTests.swift │ │ │ │ ├── ErrorTypeTests.swift │ │ │ │ ├── Info.plist │ │ │ │ └── KeychainAccessTests.swift │ │ │ ├── TestHost-MacCatalyst/ │ │ │ │ ├── AppDelegate.swift │ │ │ │ ├── Assets.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── Base.lproj/ │ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ │ └── Main.storyboard │ │ │ │ ├── Info.plist │ │ │ │ ├── SceneDelegate.swift │ │ │ │ ├── TestHost-MacCatalyst.entitlements │ │ │ │ └── ViewController.swift │ │ │ └── TestHost-MacCatalyst.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ └── TestHost-MacCatalyst.xcscheme │ │ ├── Package.swift │ │ ├── Package@swift-5.3.swift │ │ ├── README.md │ │ └── Sources/ │ │ └── Keychain.swift │ └── SymbolPicker/ │ ├── .gitignore │ ├── LICENSE │ ├── Package.swift │ ├── README.md │ └── Sources/ │ └── SymbolPicker/ │ ├── Resources/ │ │ ├── en.lproj/ │ │ │ └── Localizable.strings │ │ ├── sfsymbols.txt │ │ └── zh_CN.lproj/ │ │ └── Localizable.strings │ └── SymbolPicker.swift ├── LICENSE ├── README.md └── Resources/ ├── ModuleSample/ │ ├── Module Export - Link Cleaner/ │ │ ├── .ActionManifest.plist │ │ ├── .gitignore │ │ ├── .supplement/ │ │ │ ├── Binary/ │ │ │ │ └── CommandLineBridge/ │ │ │ │ ├── CommandLineBridge/ │ │ │ │ │ └── main.swift │ │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── CommandLineBridge.xcscheme │ │ │ ├── Communicator/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Communicator/ │ │ │ │ ├── Communicator.h │ │ │ │ ├── Communicator.m │ │ │ │ └── include/ │ │ │ │ └── Communicator.h │ │ │ ├── Definition/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Definition/ │ │ │ │ └── Definition.swift │ │ │ ├── compile.sh │ │ │ └── launch.sh │ │ ├── App.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Source/ │ │ ├── .gitignore │ │ ├── Package.swift │ │ └── Sources/ │ │ └── Source/ │ │ ├── CleanerRule/ │ │ │ ├── CleanerRule.swift │ │ │ ├── Rule+BiliBili.swift │ │ │ └── Rule+Twitter.swift │ │ └── Source.swift │ ├── Module Export - Multiline Init Formatter/ │ │ ├── .ActionManifest.plist │ │ ├── .gitignore │ │ ├── .supplement/ │ │ │ ├── Binary/ │ │ │ │ └── CommandLineBridge/ │ │ │ │ ├── CommandLineBridge/ │ │ │ │ │ └── main.swift │ │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── CommandLineBridge.xcscheme │ │ │ ├── Communicator/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Communicator/ │ │ │ │ ├── Communicator.h │ │ │ │ ├── Communicator.m │ │ │ │ └── include/ │ │ │ │ └── Communicator.h │ │ │ ├── Definition/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Definition/ │ │ │ │ └── Definition.swift │ │ │ ├── compile.sh │ │ │ └── launch.sh │ │ ├── App.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Source/ │ │ ├── .gitignore │ │ ├── Package.swift │ │ └── Sources/ │ │ └── Source/ │ │ └── Source.swift │ ├── Module Export - Quick SFImage/ │ │ ├── .ActionManifest.plist │ │ ├── .gitignore │ │ ├── .supplement/ │ │ │ ├── Binary/ │ │ │ │ └── CommandLineBridge/ │ │ │ │ ├── CommandLineBridge/ │ │ │ │ │ └── main.swift │ │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── CommandLineBridge.xcscheme │ │ │ ├── Communicator/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Communicator/ │ │ │ │ ├── Communicator.h │ │ │ │ ├── Communicator.m │ │ │ │ └── include/ │ │ │ │ └── Communicator.h │ │ │ ├── Definition/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Definition/ │ │ │ │ └── Definition.swift │ │ │ ├── compile.sh │ │ │ └── launch.sh │ │ ├── App.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── Source/ │ │ ├── .gitignore │ │ ├── Package.swift │ │ └── Sources/ │ │ └── Source/ │ │ └── Source.swift │ └── Module Export - Speak Dictionary/ │ ├── .ActionManifest.plist │ ├── .gitignore │ ├── .supplement/ │ │ ├── Binary/ │ │ │ └── CommandLineBridge/ │ │ │ ├── CommandLineBridge/ │ │ │ │ └── main.swift │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ └── CommandLineBridge.xcscheme │ │ ├── Communicator/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Communicator/ │ │ │ ├── Communicator.h │ │ │ ├── Communicator.m │ │ │ └── include/ │ │ │ └── Communicator.h │ │ ├── Definition/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Definition/ │ │ │ └── Definition.swift │ │ ├── compile.sh │ │ └── launch.sh │ ├── App.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── Source/ │ ├── .gitignore │ ├── Package.swift │ └── Sources/ │ └── Source/ │ └── Source.swift ├── ModuleTemplate/ │ ├── .templates │ ├── Executable/ │ │ ├── ActionBeeModule.exec │ │ └── Put your binary here │ ├── ExecutableSwift/ │ │ ├── .gitignore │ │ ├── .supplement/ │ │ │ ├── Binary/ │ │ │ │ └── CommandLineBridge/ │ │ │ │ ├── CommandLineBridge/ │ │ │ │ │ └── main.swift │ │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── CommandLineBridge.xcscheme │ │ │ ├── Communicator/ │ │ │ │ ├── .gitignore │ │ │ │ ├── Package.swift │ │ │ │ └── Sources/ │ │ │ │ └── Communicator/ │ │ │ │ ├── Communicator.h │ │ │ │ ├── Communicator.m │ │ │ │ └── include/ │ │ │ │ └── Communicator.h │ │ │ └── Definition/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Definition/ │ │ │ └── Definition.swift │ │ ├── App.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── Source/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Source/ │ │ │ └── Source.swift │ │ └── compile.command │ ├── SourceNode/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── .supplement/ │ │ │ └── compile.sh │ │ ├── package.json │ │ ├── src/ │ │ │ ├── global.d.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── SourcePython/ │ │ └── main.py │ └── SourceSwift/ │ ├── .gitignore │ ├── .supplement/ │ │ ├── Binary/ │ │ │ └── CommandLineBridge/ │ │ │ ├── CommandLineBridge/ │ │ │ │ └── main.swift │ │ │ └── CommandLineBridge.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ └── CommandLineBridge.xcscheme │ │ ├── Communicator/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Communicator/ │ │ │ ├── Communicator.h │ │ │ ├── Communicator.m │ │ │ └── include/ │ │ │ └── Communicator.h │ │ ├── Definition/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ └── Sources/ │ │ │ └── Definition/ │ │ │ └── Definition.swift │ │ ├── compile.sh │ │ └── launch.sh │ ├── App.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── Source/ │ ├── .gitignore │ ├── Package.swift │ └── Sources/ │ └── Source/ │ └── Source.swift └── Scripts/ ├── NewlineDeduplicate.swift ├── PackTemplates.sh ├── UpdateGitHub.sh └── UpdateLicenses.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* *.ActionTemplatePackage ================================================ FILE: .gitmodules ================================================ [submodule "External/Colorful"] path = External/Colorful url = https://github.com/Lakr233/Colorful [submodule "External/AuxiliaryExecute"] path = External/AuxiliaryExecute url = https://github.com/Lakr233/AuxiliaryExecute [submodule "External/SymbolPicker"] path = External/SymbolPicker url = https://github.com/xnth97/SymbolPicker [submodule "External/KeychainAccess"] path = External/KeychainAccess url = https://github.com/kishikawakatsumi/KeychainAccess ================================================ FILE: .root ================================================ ================================================ FILE: App/Action/Action/Action.entitlements ================================================ ================================================ FILE: App/Action/Action/Application/Action.entitlements ================================================ ================================================ FILE: App/Action/Action/Application/ActionApp.swift ================================================ // // ActionApp.swift // Action // // Created by Lakr Aream on 2022/7/25. // import SwiftUI @main struct ActionApp: App { static let bootTime: Date = .init() @AppStorage("wiki.qaq.agreeToLicense") static var agreeToLicense: Bool = false static let documentDirectory: URL = { let availableDirectories = FileManager .default .urls(for: .documentDirectory, in: .userDomainMask) #if DEBUG return availableDirectories[0] .appendingPathComponent("ActionBee.Debug") #else return availableDirectories[0] .appendingPathComponent("ActionBee") #endif }() init() { _ = ActionApp.bootTime applicationSetup() } @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { MainView() } .windowToolbarStyle(.unifiedCompact) .commands { SidebarCommands() } .commands { CommandGroup(replacing: CommandGroupPlacement.newItem) {} } } } ================================================ FILE: App/Action/Action/Application/AppDelegate.swift ================================================ // // AppDelegate.swift // Action // // Created by Lakr Aream on 2022/7/25. // import AppKit import SwiftUI class AppDelegate: NSObject, NSApplicationDelegate { func switchDockIconMode() { let windowCount = NSApp .windows .filter { self.filteringSpecialWindow($0) } .count if !StatusBarManager.shared.hasWindowOpened, windowCount == 0, !Menubar.shared.popover.isShown { NSApp.setActivationPolicy(.accessory) } else { NSApp.setActivationPolicy(.regular) } } private func filteringSpecialWindow(_ window: NSWindow) -> Bool { let list = ["NSStatusBarWindow", "_NSPopoverWindow"] for item in list { guard let clz = NSClassFromString(item) else { continue } if window.isKind(of: clz.self) { return false } } return true } } ================================================ FILE: App/Action/Action/Application/AppSetup.swift ================================================ // // ApplicationSetup.swift // Action // // Created by Lakr Aream on 2022/7/25. // import AppKit extension ActionApp { static let appBundleIdentifier: String = Bundle .main .bundleIdentifier ?? "wiki.qaq.unknown" static let appVersion: String = Bundle .main .infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown" func applicationSetup() { assert(Thread.isMainThread) assert(getuid() != 0) print( """ \(ActionApp.appBundleIdentifier) - \(ActionApp.appVersion) Location: [*] \(Bundle.main.bundleURL.path) [*] \(Self.documentDirectory.path) Environment: uid \(getuid()) gid \(getgid()) """ ) disableDebuggerIfNeeded() _ = AXUIElementCreateSystemWide() _ = Executor.shared _ = PasteboardManager.shared _ = StatusBarManager.shared let timer = Timer(timeInterval: 0.5, repeats: true) { _ in appDelegate.switchDockIconMode() } RunLoop.current.add(timer, forMode: .common) #if DEBUG let debuggerTimer = Timer(timeInterval: 1, repeats: true) { _ in _ = 0 } RunLoop.current.add(debuggerTimer, forMode: .common) #endif } private func disableDebuggerIfNeeded() { #if !DEBUG do { typealias ptrace = @convention(c) (_ request: Int, _ pid: Int, _ addr: Int, _ data: Int) -> AnyObject let open = dlopen("/usr/lib/system/libsystem_kernel.dylib", RTLD_NOW) if unsafeBitCast(open, to: Int.self) > 0x1024 { let result = dlsym(open, "ptrace") if let result = result { let target = unsafeBitCast(result, to: ptrace.self) _ = target(0x1F, 0, 0, 0) } } } #endif } } ================================================ FILE: App/Action/Action/Application/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0.227", "green" : "0.533", "red" : "0.910" } }, "idiom" : "universal" }, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "0.455", "green" : "0.659", "red" : "0.910" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: App/Action/Action/Application/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "icon_16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "filename" : "icon_16x16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "filename" : "icon_32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "filename" : "icon_32x32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "filename" : "icon_128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "filename" : "icon_128x128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "filename" : "icon_256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "filename" : "icon_256x256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "filename" : "icon_512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "icon_512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: App/Action/Action/Application/Assets.xcassets/Avatar.imageset/Contents.json ================================================ { "images" : [ { "filename" : "Avatar.png", "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: App/Action/Action/Application/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: App/Action/Action/Application/License.txt ================================================ MIT License Copyright (c) 2022 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========== AuxiliaryExecute MIT License Copyright (c) 2021 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========== Colorful MIT License Copyright (c) 2021 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========== KeychainAccess The MIT License (MIT) Copyright (c) 2014 kishikawa katsumi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========== SymbolPicker MIT License Copyright (c) 2022 Yubo Qin & Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========== Updated: 2022-08-30 ================================================ FILE: App/Action/Action/Application/en.lproj/Localizable.strings ================================================ /* Localizable.strings Action Created by 维安雨轩 on 2022/08/19. */ ================================================ FILE: App/Action/Action/Application/zh-Hans.lproj/Localizable.strings ================================================ /* Localizable.strings Action Created by 维安雨轩 on 2022/08/19. */ //ModuleManageView.swift "Create an action by click plus button on toolbar to process your pasteboard event. Format text, clean up links, speak when copy from special app, send to your device, etc etc. Choose an language you are familiar with to get start." = "通过点击工具栏上的加号按钮创建一个自动化来处理你的剪贴板事件。\n如格式化文本、清理链接或从设定应用中复制时,发送至你的设备等。\n现在快选择一种你熟悉的语言来开始吧。"; "Add Action" = "添加自动化模块"; "Import Action" = "导入自动化模块"; "Module" = "模块"; "You are about to import an Action Module which is untrusted with no signature. Importing malicious modules can pose unknown risks and there is no sandbox nor container dealing with it." = "你正在导入一个没有签名的不受信任的自动化模块。导入恶意模块可能会带来未知风险,在运行这个模块时并不会通过沙盒或容器来处理它。"; "Trust And Import" = "信任并导入"; "Cancel" = "取消"; "Failed to import this module" = "导入模块失败"; // ModuleImportView.swift "You are about to import an untrusted module" = "您正在导入一个不受信任的模块"; "Importing malicious module may damage your system, you are in charge to review this module." = "导入恶意模块可能会损坏你的系统,您需要负责审查这个模块。"; "Trust & Import" = "信任并导入"; "OK" = "成功"; // ModuleElementView.swift "Error" = "出错了"; // ModuleCreateView.swift "Create Action" = "创建自动化模块"; "Module Name" = "模块名称"; "Language" = "语言"; "Next" = "继续"; // ModuleEditView.swift "Edit Action" = "编辑自动化模块"; "Save" = "保存"; "Compiling Source" = "正在编译模块..."; "Compile Finished" = "编译完成"; "Broken Module" = "损坏的模块"; "Delete" = "删除"; "Close" = "关闭"; "Enabled" = "启用"; "Name" = "名称"; "Timeout: " = "超时:"; "Enable In App" = "在应用程序中启用"; "Enabled for All Apps" = "为所有应用程序启用"; "This pasteboard action will only run if copying from these apps" = "此自动化只有在从这些应用程序复制时才会运行"; "Coding" = "编辑"; "Edit Code" = "编辑代码"; "Show in Finder" = "在 Finder 中显示"; "Export" = "导出"; "Module Template: %@" = "模板: %@"; "Unknown" = "未知"; "No Build Hint" = "没有构建提示"; "You should recompile, click save, this module each time you edit it" = "在每次编辑该模块时,点击保存将会重新编译。"; "Unable to compile this action: %@" = "无法编译这个自动化模块: %@"; "Are you sure you want to delete this module? This operation can not be undone." = "您确定要删除这个模块吗?\n确定后将无法撤销。"; "Select Application" = "选择应用程序"; "Failed to load this module" = "加载此模块失败"; "Module Export - %@" = "导出模块 - %@"; "Unnamed" = "未命名"; // ContentView.swift "Toggle Sidebar" = "切换侧边栏"; // SidebarView.swift "App" = "应用"; "Welcome" = "欢迎"; "Action" = "自动化"; "Module" = "模块"; "History" = "历史"; "Misc" = "其他"; "Setting" = "设置"; "Diagnostic" = "诊断"; // WelcomeView.swift "Welcome to Action Bee" = "欢迎使用 Action Bee"; "A programmable pasteboard action trigger." = "一个可编程的剪贴板触发器"; // HistoryView.swift "No History Was Found" = "暂无历史记录"; "Clear History" = "清除历史记录"; "Are you sure you want to delete all history records? This operation can not be undone." = "你确定要删除所有历史记录吗?\n确定后将无法撤销。"; "Pasteboard Event" = "剪贴板事件"; "Length: %lld Action Candidates: %lld" = "剪贴长度: %lld 共执行模块: %lld"; "Deleted Action" = "被删除的动作"; " Post Action: %@" = " 动作: %@"; " Content: %@" = " 内容: %@"; // SettingView.swift "Pasteboard Deduplicate" = "阻拦重复执行"; "Pasteboard content matches previous will not generate event if on" = "如剪贴板的内容与之前的相匹配,开启后,将不会执行事件。"; "Silent Mode" = "静默模式"; "Do not show popover after action triggered" = "动作触发后不弹出窗口"; "Toast Mode" = "Toast 模式"; "Use toast instead of popover on menubar" = "在屏幕上显示简易提示框而不是弹出式菜单"; "Reduced UI Effects" = "减少 UI 视觉效果"; "Turning off visual effects does not affect app's core functionality" = "关闭视觉效果并不会影响应用程序的核心功能"; "Application" = "应用"; "Get Source Code" = "获取源代码"; "License" = "许可证"; // MenubarView.swift "ActionBee is ready to accept pasteboard events." = "ActionBee 已准备好处理剪贴板事件。"; // LicenseView.swift "Software License" = "软件许可证"; "I understand and agree to this license." = "我理解并同意此许可。"; "Done" = "同意"; ================================================ FILE: App/Action/Action/Backend/Action/ActionManager+Artifact.swift ================================================ // // ActionManager+Artifact.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Foundation private let artifactPathExtension = "ActionBeeArtifact" extension ActionManager { struct ModuleArtifact: Codable { let id: Action.ID let signature: String init?(id: Action.ID, copyingArtifactAt atUrl: URL) { self.id = id let targetDir = Self.obtainArtifactUrlForAction(withId: id) do { try? FileManager.default.removeItem(at: targetDir) var isDir = ObjCBool(false) guard FileManager.default.fileExists(atPath: atUrl.path, isDirectory: &isDir) else { return nil } if isDir.boolValue { try FileManager.default.copyItem(at: atUrl, to: targetDir) } else { try FileManager.default.createDirectory(at: targetDir, withIntermediateDirectories: true) let name = atUrl.lastPathComponent let target = targetDir.appendingPathComponent(name) try FileManager.default.copyItem(at: atUrl, to: target) } signature = try Self.generatePackageSignature(at: targetDir) } catch { print("[E] \(error.localizedDescription)") return nil } } static func deletingDotFiles(at atUrl: URL) throws { let enumerator = FileManager.default.enumerator(atPath: atUrl.path) while let subPath = enumerator?.nextObject() as? String { let url = atUrl.appendingPathComponent(subPath) var shouldClean = false if subPath == ".DS_Store" { shouldClean = true } if subPath.hasPrefix("._") { shouldClean = true } if shouldClean { print("[*] deleting dot files inside artifact \(url.path)") try FileManager.default.removeItem(at: url) } } } static func generatePackageSignature(at atUrl: URL) throws -> String { try deletingDotFiles(at: atUrl) var signatureDic = [String: String]() let enumerator = FileManager.default.enumerator(atPath: atUrl.path) while let subPath = enumerator?.nextObject() as? String { let path = atUrl.appendingPathComponent(subPath) var isDir = ObjCBool(false) guard FileManager .default .fileExists(atPath: path.path, isDirectory: &isDir) else { throw GenericActionError.brokenResources } let data = try Data(contentsOf: path) let hash = data.sha256() signatureDic[subPath] = hash } var hasher = String() let valueArray = signatureDic.sorted { $0.key < $1.key } for (key, value) in valueArray { hasher += key hasher += value hasher += "\n" } guard let signature = hasher.data(using: .utf8)?.sha256() else { throw GenericActionError.unknown } return signature } static func obtainArtifactUrlForAction(withId: Action.ID) -> URL { ActionManager.shared .actionArtifactBaseUrl .appendingPathComponent(withId.uuidString) .appendingPathExtension(artifactPathExtension) } func obtainArtifactUrl() -> URL { Self.obtainArtifactUrlForAction(withId: id) } func validateSignature() -> Bool { do { let testSignature = try Self.generatePackageSignature(at: obtainArtifactUrl()) return signature == testSignature } catch { print("[E] \(error.localizedDescription)") return false } } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionManager+Event.swift ================================================ // // ActionManager+Event.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Cocoa import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() extension ActionManager { struct ActionRecipeData: Codable { let postAction: PostAction let postContent: String let continueQueue: Bool enum PostAction: String, Codable { case overwrite case speak case none func humanReadableDescription() -> String { switch self { case .overwrite: return "Overwrite Pasteboard" case .speak: return "Speak Message" case .none: return "None" } } } init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } func handle(pasteboardEvent event: PasteboardManager.PEvent) { assert(!Thread.isMainThread) var actionCandidates: [Action] = [] DispatchQueue.withMainAndWait { actionCandidates = self.actions } actionCandidates = actionCandidates .filter { enabledActions.contains($0.id) } .filter { !initialingAciton.contains($0.id) } .filter { if $0.enabledAppList.isEmpty { return true } guard let appBundleIdentifier = event.app?.bundleIdentifier else { return false } return $0.enabledAppList.contains(appBundleIdentifier) } .sorted { a, b in guard a.priority == b.priority else { return a.priority < b.priority } return a.name < b.name } guard !actionCandidates.isEmpty else { print("[-] no action candidate found, ignoring pasteboard event") return } print("[*] pasteboard event resolved \(actionCandidates.count) candidates") DispatchQueue.withMainAndWait { self.actionRunning = true self.actionRunningHint = "Resolved \(actionCandidates.count) Action Candidates" Menubar.shared.switchTitle(status: .running) } var successAction: [HistoryElement.SuccessRecord] = [] var failedAction: [HistoryElement.FailureRecord] = [] var shouldShowResultWindow = false defer { let history = HistoryElement( event: event, actionCandidates: actionCandidates.map(\.id), succeedAction: successAction, failedAction: failedAction ) DispatchQueue.withMainAndWait { self.histories.append(history) self.actionRunning = false self.actionRunningHint = "" Menubar.shared.switchTitle(status: .ready) if shouldShowResultWindow, !Config.shared.silentMode { if Config.shared.toastMode { var image = "checkmark.circle.fill" if !failedAction.isEmpty { image = "checkmark.circle.trianglebadge.exclamationmark" } Toast.post(systemIcon: image, message: "ActionBee Completed") } else { Menubar.shared.showPopover() } } } } for action in actionCandidates { DispatchQueue.withMainAndWait { self.actionRunningHint = "Executing Action - \(action.name)" } let recipe = action.template .obtainTemplateDetails() .executeModule( id: action.id, withPasteboardEvent: event ) { print($0) } switch recipe { case let .success(recipe): successAction.append(.init( action: action.id, recipeAction: recipe.postAction.humanReadableDescription(), recipeContent: recipe.postContent )) resolvePostAction(recipe) if recipe.postAction != .none { shouldShowResultWindow = true } guard recipe.continueQueue else { return } case let .failure(failure): shouldShowResultWindow = true failedAction.append(.init(action: action.id, errorHint: failure.message)) print("[E] error executing aciton: \(failure.message)") continue } } } func resolvePostAction(_ object: ActionRecipeData) { assert(!Thread.isMainThread) switch object.postAction { case .none: break case .speak: DispatchQueue.global().async { Executor.shared.speak(object.postContent) } case .overwrite: NSPasteboard.general.prepareForNewContents() NSPasteboard.general.setString(object.postContent, forType: .string) } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionManager+History.swift ================================================ // // ActionManager+History.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Foundation extension ActionManager { struct HistoryElement: Codable, Identifiable, Hashable, Equatable { var id: UUID = .init() let date: Date let event: PasteboardManager.PEvent let actionCandidates: [Action.ID] struct SuccessRecord: Codable, Identifiable, Hashable, Equatable { var id: UUID = .init() let action: Action.ID let recipeAction: String let recipeContent: String } let succeedAction: [SuccessRecord] struct FailureRecord: Codable, Identifiable, Hashable, Equatable { var id: UUID = .init() let action: Action.ID let errorHint: String } let failedAction: [FailureRecord] init( id: UUID = .init(), date: Date = .init(), event: PasteboardManager.PEvent, actionCandidates: [ActionManager.Action.ID] = [], succeedAction: [ActionManager.HistoryElement.SuccessRecord] = [], failedAction: [ActionManager.HistoryElement.FailureRecord] = [] ) { self.id = id self.date = date self.event = event self.actionCandidates = actionCandidates self.succeedAction = succeedAction self.failedAction = failedAction } func search(with key: String) -> Bool { if event.app?.name.lowercased().contains(key) ?? false { return true } if event.app?.bundleIdentifier.lowercased().contains(key) ?? false { return true } if event.content.lowercased().contains(key) { return true } if date.formatted(date: .complete, time: .complete).lowercased().contains(key) { return true } return false } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionManager+Module.swift ================================================ // // ActionManager+Module.swift // Action // // Created by Lakr Aream on 2022/7/26. // import Foundation /* Action will be compiled into ActionManifest.plist into project root dir so the project dir looks like: eg swift . ├── .build │   └── cli <- binary ├── App.xcworkspace └── compile.sh <- emit binary, called each time when compile // user editable ├── Source │   ├── Package.swift │   └── Sources │   └── Source │   └── Source.swift <- user code // program granted ├── .ActionManifest.plist <- exported definition of this action */ extension ActionManager { struct Action: Codable, Equatable, Hashable, Identifiable { var id: UUID var name: String var icon: String var priority: Int // lower first-er P0~ var timeout: Int var template: ActionManager.ModuleTemplateIdentifier // will only run if copy from these apps var enabledAppList: [String] = [] // keep for future usage var attachment: [String: String] init( id: UUID = .init(), name: String, icon: String = "text.append", priority: Int = 100, timeout: Int = 5, template: ActionManager.ModuleTemplateIdentifier, enabledAppList: [String] = [], attachment: [String: String] = [:] ) { self.id = id self.name = name self.icon = icon self.priority = priority self.timeout = timeout self.template = template self.enabledAppList = enabledAppList self.attachment = attachment } } func updateActionModuleManifest(onActionId: Action.ID? = nil) { if let id = onActionId { guard let action = self[id] else { return } try? compileManifestAndSave(forAction: action) } for action in actions { try? compileManifestAndSave(forAction: action) } } func compileManifestAndSave(forAction action: Action) throws { let actionModuleDir = actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) let manifestUrl = actionModuleDir .appendingPathComponent(actionManifestFileName) .appendingPathExtension(actionManifestExtension) guard FileManager.default.fileExists(atPath: actionModuleBaseUrl.path) else { return } let data = try actionManifestEncoder.encode(action) try data.write(to: manifestUrl, options: .atomic) } func deleteModule(withId id: Action.ID) { print("[*] deleting module \(id)") let moduleUrl = actionModuleBaseUrl .appendingPathComponent(id.uuidString) try? FileManager.default.removeItem(at: moduleUrl) actions = actions.filter { $0.id != id } enabledActions = enabledActions.filter { $0 != id } invalidateArtifactCache(forAction: id) } func issueCompile(forAction actionId: Action.ID, output: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = self[actionId] else { return .failure(.brokenResources) } let result = action.template .obtainTemplateDetails() .compileModule(id: actionId, output: output) return result } func registerArtifact(forAction actionId: Action.ID, artifact: URL) { print("[*] copying artifact at path \(artifact.path) for action \(actionId)") try? FileManager.default.createDirectory( at: ActionManager.shared.actionArtifactBaseUrl, withIntermediateDirectories: true ) guard let object = ModuleArtifact(id: actionId, copyingArtifactAt: artifact) else { return } print("[*] registering artifact \(actionId) with signature \(object.signature)") guard Thread.isMainThread else { DispatchQueue.withMainAndWait { self.artifacts[object.id] = object } return } artifacts[object.id] = object } func invalidateArtifactCache(forAction actionId: Action.ID) { print("[*] invalidating artifact cache for \(actionId)") if let value = artifacts[actionId] { try? FileManager.default.removeItem(at: value.obtainArtifactUrl()) } if Thread.isMainThread { artifacts.removeValue(forKey: actionId) } else { DispatchQueue.withMainAndWait { self.artifacts.removeValue(forKey: actionId) } } } func importModule(at: URL) -> Result { print("[*] importing module at \(at.path)") let manifest = at .appendingPathComponent(actionManifestFileName) .appendingPathExtension(actionManifestExtension) do { let data = try Data(contentsOf: manifest) let action = try actionManifestDecoder.decode(Action.self, from: data) let target = actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) print("[*] manifest returns id \(action.id)") try? FileManager.default.removeItem(at: target) try FileManager.default.copyItem(at: at, to: target) invalidateArtifactCache(forAction: action.id) DispatchQueue.withMainAndWait { self[action.id] = action self.enabledActions.append(action.id) } updateActionModuleManifest(onActionId: action.id) return .success(action.id) } catch { return .failure(error) } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionManager.swift ================================================ // // ActionManager.swift // Action // // Created by Lakr Aream on 2022/7/26. // import Combine import Foundation final class ActionManager: ObservableObject { static let shared = ActionManager() private init() { #if DEBUG for item in ModuleTemplateIdentifier.allCases { _ = item.obtainTemplateDetails().getTemplateBundleURL() } #endif var buildAction: [Action.ID: Action] = [:] for action in actionsStore { buildAction[action.id] = action } // now clean the items inside the dir if not exists in plist do { let actionIds = buildAction.keys.map(\.uuidString) let list = ( try? FileManager .default .contentsOfDirectory(atPath: actionModuleBaseUrl.path) ) ?? [] var deleteList = [String]() deleteList += list.filter { !actionIds.contains($0) } deleteList += actionIds.filter { !list.contains($0) } for element in deleteList { let url = actionModuleBaseUrl.appendingPathComponent(element) try? FileManager.default.removeItem(at: url) if let id = UUID(uuidString: element) { buildAction.removeValue(forKey: id) } print("[-] removing unregistered action module file at path \(url.path)") } } actions = buildAction.values .sorted { $0.name < $1.name } updateActionModuleManifest() enabledActions = Array(Set(enabledActionsStore)) for action in actions { print("[+] loading module \(action.name) - \(action.id.uuidString)") } artifacts = artifactsStore for artifact in artifacts { let found = actions.contains { $0.id == artifact.key } if found { print("[+] loading artifact \(artifact.key.uuidString)") DispatchQueue.global().async { guard artifact.value.validateSignature() else { print("[E] artifact signature mismatch!") self.invalidateArtifactCache(forAction: artifact.key) return } } } else { DispatchQueue.global().async { self.invalidateArtifactCache(forAction: artifact.key) } } } histories = historiesStore .sorted { $0.date < $1.date } } var actionModuleBaseUrl: URL { let ret = ActionApp .documentDirectory .appendingPathComponent("ActionModules") try? FileManager.default.createDirectory( at: ret, withIntermediateDirectories: true ) return ret } var actionArtifactBaseUrl: URL { let ret = ActionApp .documentDirectory .appendingPathComponent("ActionArtifact") try? FileManager.default.createDirectory( at: ret, withIntermediateDirectories: true ) return ret } let actionManifestFileName = ".ActionManifest" let actionManifestExtension = "plist" let actionManifestEncoder = PropertyListEncoder() let actionManifestDecoder = PropertyListDecoder() @EncryptedCodableDefaultsWrapper(key: "wiki.qaq.ActionManager.actionsStoreKey", defaultValue: [Action]()) private var actionsStore @Published var actions: [Action] = [] { didSet { DispatchQueue.global().async { self.actionsStore = self.actions } } } @Published var initialingAciton: Set = [] subscript(actionId: UUID) -> Action? { get { actions.first { $0.id == actionId } } set { assert(Thread.isMainThread) guard let newValue = newValue else { deleteModule(withId: actionId) return } let idx = actions.firstIndex { $0.id == newValue.id } if let idx = idx { actions[idx] = newValue } else { actions = ( actions + [newValue] ) .sorted { $0.name < $1.name } } } } @EncryptedCodableDefaultsWrapper(key: "wiki.qaq.ActionManager.enabledActions", defaultValue: []) private var enabledActionsStore: [Action.ID] @Published var enabledActions = [Action.ID]() { didSet { DispatchQueue.global().async { self.enabledActionsStore = self.enabledActions } } } @EncryptedCodableDefaultsWrapper(key: "wiki.qaq.ActionManager.artifactsStore", defaultValue: [Action.ID: ModuleArtifact]()) private var artifactsStore @Published var artifacts: [Action.ID: ModuleArtifact] = [:] { didSet { DispatchQueue.global().async { self.artifactsStore = self.artifacts } } } @EncryptedCodableDefaultsWrapper(key: "wiki.qaq.ActionManager.historiesStore", defaultValue: [HistoryElement]()) private var historiesStore @Published var histories: [HistoryElement] = [] { didSet { let historyLimit = 500 guard histories.count < historyLimit else { DispatchQueue.main.async { self.histories.removeFirst(self.histories.count - historyLimit) } return } DispatchQueue.global().async { self.historiesStore = self.histories } } } @Published var actionRunning = false @Published var actionRunningHint = "" } ================================================ FILE: App/Action/Action/Backend/Action/ActionModuleTemplates/ActionManager+Template.swift ================================================ // // ActionManager+ModuleTemplate.swift // Action // // Created by Lakr Aream on 2022/8/15. // import Cocoa import Foundation protocol ModuleTemplate { func getLanguage() -> String func getTemplateBundleName() -> String func getBuildHint() -> String func getTemplateBundleURL() -> URL func openDesignatedEditor(id: ActionManager.Action.ID) -> Result func compileModule( id: ActionManager.Action.ID, output: @escaping (String) -> Void ) -> Result func executeModule( id: ActionManager.Action.ID, withPasteboardEvent event: PasteboardManager.PEvent, output: @escaping (String) -> Void ) -> Result } extension ModuleTemplate { func getTemplateBundleURL() -> URL { Bundle.main.url( forResource: getTemplateBundleName(), withExtension: "ActionTemplatePackage", subdirectory: "ActionTemplates" )! } } extension ActionManager { enum GenericActionError: Error { case permissionDenied case compilerError case brokenResources case designatedEditorMissing case unauthorizedModificationDetected case invalidResponse case unknown var message: String { switch self { case .permissionDenied: return "Permission denied for requires resources." case .compilerError: return "Compiler returned an error, check your source." case .brokenResources: return "Resources for this module was not found." case .designatedEditorMissing: return "The designated editor app for this module was not found, please edit it in Finder yourself." case .unauthorizedModificationDetected: return "The requires resource was modified by unauthorized event." case .invalidResponse: return "Invalid respond." case .unknown: return "Unknown error occurred." } } } enum ModuleTemplateIdentifier: String, Codable, CaseIterable, Hashable, Equatable { case executable case executableSwift case swift case node case python func obtainTemplateDetails() -> ModuleTemplate { switch self { case .executable: return ModuleTemplateExecutable() case .executableSwift: return ModuleTemplateExecutableSwift() case .swift: return ModuleTemplateSwift() case .node: return ModuleTemplateNode() case .python: return ModuleTemplatePython() } } } func createAction(withName name: String, withModuleTemplate template: ModuleTemplateIdentifier) -> UUID? { let actionUUID = UUID() let templateBundleUrl = template .obtainTemplateDetails() .getTemplateBundleURL() let extractTarget = actionModuleBaseUrl .appendingPathComponent(actionUUID.uuidString) let action = Action(id: actionUUID, name: name, template: template) print("[*] creating module at \(extractTarget.path)") do { try FileManager.default.createDirectory(at: extractTarget, withIntermediateDirectories: true) try Executor.shared.unarchiveTar(at: templateBundleUrl, toDest: extractTarget) try compileManifestAndSave(forAction: action) } catch { return nil } DispatchQueue.withMainAndWait { self.actions = ( self.actions + [action] ) .sorted { $0.name < $1.name } self.enabledActions.append(action.id) } return actionUUID } } ================================================ FILE: App/Action/Action/Backend/Action/ActionModuleTemplates/Template+Executable.swift ================================================ // // Template+Executable.swift // Action // // Created by Lakr Aream on 2022/8/17. // import AuxiliaryExecute import Cocoa import Foundation extension ActionManager { class ModuleTemplateExecutable: ModuleTemplate { func getLanguage() -> String { "Binary Executable" } func getTemplateBundleName() -> String { "Executable" } func getBuildHint() -> String { "Compile your binary, name it ActionBeeModule.exec, and put it here." } struct ArgumentData: Codable { let focusAppID: String? let focusAppName: String? let pasteboardContent: String init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } func compileBase64() -> String? { (try? JSONEncoder().encode(self))?.base64EncodedString() } } func openDesignatedEditor(id: ActionManager.Action.ID) -> Result { guard let action = ActionManager.shared[id] else { return .failure(GenericActionError.brokenResources) } let target = ActionManager.shared.actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) guard FileManager.default.fileExists(atPath: target.path) else { return .failure(GenericActionError.brokenResources) } guard NSWorkspace.shared.open(target) else { return .failure(GenericActionError.designatedEditorMissing) } return .success } func compileModule(id: ActionManager.Action.ID, output _: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = ActionManager.shared[id] else { return .failure(.brokenResources) } let target = ActionManager.shared.actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) .appendingPathComponent("ActionBeeModule.exec") guard FileManager.default.fileExists(atPath: target.path) else { return .failure(.brokenResources) } guard FileManager.default.isExecutableFile(atPath: target.path) else { return .failure(.permissionDenied) } ActionManager.shared.registerArtifact(forAction: action.id, artifact: target) return .success } func executeModule(id: ActionManager.Action.ID, withPasteboardEvent event: PasteboardManager.PEvent, output: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = ActionManager.shared[id], let artifact = ActionManager.shared.artifacts[id] else { return .failure(.brokenResources) } guard artifact.validateSignature() else { return .failure(.unauthorizedModificationDetected) } guard let argument = ArgumentData( focusAppID: event.app?.bundleIdentifier, focusAppName: event.app?.name, pasteboardContent: event.content ) .compileBase64() else { return .failure(.brokenResources) } print("[*] executing action \(id.uuidString)") var resultData: ActionRecipeData? let binary = artifact .obtainArtifactUrl() .appendingPathComponent("cli") .path let recipe = AuxiliaryExecute.spawn( command: binary, environment: ["Communicator_Message": argument], timeout: Double(action.timeout), output: output ) var lastLine = recipe.stderr .trimmingCharacters(in: .whitespacesAndNewlines) .components(separatedBy: "\n") .last? .trimmingCharacters(in: .whitespaces) ?? "" let prefix = "ActionBee-Result-Recipe://" if lastLine.hasPrefix(prefix) { lastLine.removeFirst(prefix.count) } if let base64 = Data(base64Encoded: lastLine) { resultData = ActionRecipeData.retrieve(withData: base64) } guard let result = resultData else { return .failure(.invalidResponse) } return .success(result) } } class ModuleTemplateExecutableSwift: ModuleTemplateExecutable { override func getLanguage() -> String { "Binary Executable - Swift" } override func getTemplateBundleName() -> String { "ExecutableSwift" } override func getBuildHint() -> String { "Compile your binary, name it ActionBeeModule.exec, and put it here." } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionModuleTemplates/Template+Node.swift ================================================ // // Template+Node.swift // Action // // Created by Innei on 2022/8/21. // import AuxiliaryExecute import Foundation extension ActionManager { class ModuleTemplateNode: ModuleTemplateExecutable { override func getLanguage() -> String { "Source - Node" } override func getTemplateBundleName() -> String { "SourceNode" } override func getBuildHint() -> String { "To build Node Module, node and it's tool is required. Install them yourself." } override func openDesignatedEditor(id: ActionManager.Action.ID) -> Result { let url = ActionManager.shared .actionModuleBaseUrl .appendingPathComponent(id.uuidString) let recipe = AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", "code \(url.path)"] ) guard recipe.exitCode == 0 else { return .failure(.designatedEditorMissing) } return .success } override func compileModule(id: ActionManager.Action.ID, output: @escaping (String) -> Void) -> Result { guard let action = ActionManager.shared[id] else { return .failure(.brokenResources) } let temporaryDir = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent(UUID().uuidString) try? FileManager.default.removeItem(at: temporaryDir) defer { try? FileManager.default.removeItem(at: temporaryDir) } do { output("[*] starting compiler at \(temporaryDir.path)\n") let userSrc = ActionManager.shared .actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) let targetSrc = temporaryDir output("[*] copying user source from \(userSrc.path) to \(targetSrc.path)\n") try FileManager.default.copyItem(at: userSrc, to: targetSrc) FileManager.default.createFile( atPath: temporaryDir.appendingPathComponent(".action").path, contents: nil ) } catch { output("[E] \(error.localizedDescription)") return .failure(.permissionDenied) } output("[*] calling compiler script\n") let compileScript = temporaryDir .appendingPathComponent(".supplement") .appendingPathComponent("compile.sh") let recipe = executeZshScript(atLocation: compileScript, output: output) guard recipe.exitCode == 0 else { return .failure(.compilerError) } let artifactLocation = temporaryDir .appendingPathComponent("dist") // .appendingPathComponent("index.js") guard FileManager.default.fileExists(atPath: artifactLocation.path) else { return .failure(.permissionDenied) } output("[*] compiled binary at \(artifactLocation.path)\n") ActionManager.shared.registerArtifact(forAction: action.id, artifact: artifactLocation) return .success } override func executeModule(id: ActionManager.Action.ID, withPasteboardEvent event: PasteboardManager.PEvent, output: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = ActionManager.shared[id], let artifact = ActionManager.shared.artifacts[id] else { return .failure(.brokenResources) } guard artifact.validateSignature() else { return .failure(.unauthorizedModificationDetected) } let script = artifact .obtainArtifactUrl() .appendingPathComponent("index.js") guard let argument = ArgumentData( focusAppID: event.app?.bundleIdentifier, focusAppName: event.app?.name, pasteboardContent: event.content ) .compileBase64() else { return .failure(.brokenResources) } print("[*] executing action \(id.uuidString)") var resultData: ActionRecipeData? let recipe = AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", "node \(script.path)"], environment: ["Communicator_Message": argument], timeout: Double(action.timeout), output: output ) var lastLine = recipe.stderr .trimmingCharacters(in: .whitespacesAndNewlines) .components(separatedBy: "\n") .last? .trimmingCharacters(in: .whitespaces) ?? "" let prefix = "ActionBee-Result-Recipe://" if lastLine.hasPrefix(prefix) { lastLine.removeFirst(prefix.count) } if let base64 = Data(base64Encoded: lastLine) { resultData = ActionRecipeData.retrieve(withData: base64) } guard let result = resultData else { return .failure(.invalidResponse) } return .success(result) } func executeZshScript(atLocation: URL, output: @escaping (String) -> Void = { _ in }) -> AuxiliaryExecute.ExecuteRecipe { AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", atLocation.path], output: output ) } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionModuleTemplates/Template+Python.swift ================================================ // // Template+Python.swift // Action // // Created by Lakr Aream on 2022/8/30. // import AuxiliaryExecute import Foundation extension ActionManager { class ModuleTemplatePython: ModuleTemplateExecutable { override func getLanguage() -> String { "Source - Python" } override func getTemplateBundleName() -> String { "SourcePython" } override func getBuildHint() -> String { "To build Python Module, python 3 and it's tool is required. Install them yourself." } override func openDesignatedEditor(id: ActionManager.Action.ID) -> Result { let url = ActionManager.shared .actionModuleBaseUrl .appendingPathComponent(id.uuidString) let recipe = AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", "code \(url.path)"] ) guard recipe.exitCode == 0 else { return .failure(.designatedEditorMissing) } return .success } override func compileModule(id: ActionManager.Action.ID, output _: @escaping (String) -> Void) -> Result { guard let action = ActionManager.shared[id] else { return .failure(.brokenResources) } let actionUrl = ActionManager.shared .actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) ActionManager.shared.registerArtifact(forAction: action.id, artifact: actionUrl) return .success } override func executeModule(id: ActionManager.Action.ID, withPasteboardEvent event: PasteboardManager.PEvent, output: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = ActionManager.shared[id], let artifact = ActionManager.shared.artifacts[id] else { return .failure(.brokenResources) } guard artifact.validateSignature() else { return .failure(.unauthorizedModificationDetected) } let script = artifact .obtainArtifactUrl() .appendingPathComponent("main.py") guard let argument = ArgumentData( focusAppID: event.app?.bundleIdentifier, focusAppName: event.app?.name, pasteboardContent: event.content ) .compileBase64() else { return .failure(.brokenResources) } print("[*] executing action \(id.uuidString)") var resultData: ActionRecipeData? let recipe = AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", "python3 \(script.path)"], environment: ["Communicator_Message": argument], timeout: Double(action.timeout), output: output ) var lastLine = recipe.stderr .trimmingCharacters(in: .whitespacesAndNewlines) .components(separatedBy: "\n") .last? .trimmingCharacters(in: .whitespaces) ?? "" let prefix = "ActionBee-Result-Recipe://" if lastLine.hasPrefix(prefix) { lastLine.removeFirst(prefix.count) } if let base64 = Data(base64Encoded: lastLine) { resultData = ActionRecipeData.retrieve(withData: base64) } guard let result = resultData else { return .failure(.invalidResponse) } return .success(result) } func executeZshScript(atLocation: URL, output: @escaping (String) -> Void = { _ in }) -> AuxiliaryExecute.ExecuteRecipe { AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", atLocation.path], output: output ) } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionModuleTemplates/Template+Swift.swift ================================================ // // ModuleTemplate+Swift.swift // Action // // Created by Lakr Aream on 2022/8/16. // import AuxiliaryExecute import Cocoa extension ActionManager { class ModuleTemplateSwift: ModuleTemplateExecutable { override func getLanguage() -> String { "Source - Swift" } override func getTemplateBundleName() -> String { "SourceSwift" } override func getBuildHint() -> String { "To build Swift Module, Xcode and it's tool xcode-build is required. Install Xcode yourself." } override func openDesignatedEditor(id: ActionManager.Action.ID) -> Result { guard let action = ActionManager.shared[id] else { return .failure(GenericActionError.brokenResources) } let target = ActionManager.shared.actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) .appendingPathComponent("App.xcworkspace") guard FileManager.default.fileExists(atPath: target.path) else { return .failure(GenericActionError.brokenResources) } guard NSWorkspace.shared.open(target) else { return .failure(GenericActionError.designatedEditorMissing) } return .success } override func compileModule(id: ActionManager.Action.ID, output: @escaping (String) -> Void) -> Result { assert(!Thread.isMainThread) guard let action = ActionManager.shared[id] else { return .failure(.brokenResources) } let temporaryDir = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent(UUID().uuidString) try? FileManager.default.removeItem(at: temporaryDir) defer { try? FileManager.default.removeItem(at: temporaryDir) } do { output("[*] starting compiler at \(temporaryDir.path)\n") try FileManager.default.createDirectory(at: temporaryDir, withIntermediateDirectories: true) try Executor.shared.unarchiveTar(at: getTemplateBundleURL(), toDest: temporaryDir) let validatedSourcePathComponents = "Source" let userSrc = ActionManager.shared .actionModuleBaseUrl .appendingPathComponent(action.id.uuidString) .appendingPathComponent(validatedSourcePathComponents) let targetSrc = temporaryDir .appendingPathComponent(validatedSourcePathComponents) output("[*] copying user source from \(userSrc.path) to \(targetSrc.path)\n") try FileManager.default.removeItem(at: targetSrc) try FileManager.default.copyItem(at: userSrc, to: targetSrc) FileManager.default.createFile( atPath: temporaryDir.appendingPathComponent(".action").path, contents: nil ) } catch { output("[E] \(error.localizedDescription)") return .failure(.permissionDenied) } output("[*] calling compiler script\n") let compileScript = temporaryDir .appendingPathComponent(".supplement") .appendingPathComponent("compile.sh") let recipe = executeZshScript(atLocation: compileScript, output: output) guard recipe.exitCode == 0 else { return .failure(.compilerError) } let binaryLocation = temporaryDir .appendingPathComponent(".build") .appendingPathComponent("cli") guard FileManager.default.fileExists(atPath: binaryLocation.path) else { return .failure(.permissionDenied) } output("[*] compiled binary at \(binaryLocation.path)\n") ActionManager.shared.registerArtifact(forAction: action.id, artifact: binaryLocation) return .success } func executeZshScript(atLocation: URL, output: @escaping (String) -> Void = { _ in }) -> AuxiliaryExecute.ExecuteRecipe { AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", atLocation.path], output: output ) } } } ================================================ FILE: App/Action/Action/Backend/Action/ActionTemplates/.templates ================================================ ================================================ FILE: App/Action/Action/Backend/Config/Config.swift ================================================ // // Config.swift // Action // // Created by Lakr Aream on 2022/8/17. // import Combine import Foundation class Config: ObservableObject { static let shared = Config() private init() { reducedEffects = reducedEffectsStore pasteboardDeduplicate = pasteboardDeduplicateStore silentMode = silentModeStore toastMode = toastModeStore } @UserDefaultsWrapper(key: "wiki.qaq.config.reducedEffects", defaultValue: false) private var reducedEffectsStore @Published var reducedEffects: Bool = false { didSet { reducedEffectsStore = reducedEffects } } @UserDefaultsWrapper(key: "wiki.qaq.config.pasteboardDeduplicate", defaultValue: true) private var pasteboardDeduplicateStore @Published var pasteboardDeduplicate: Bool = true { didSet { pasteboardDeduplicateStore = pasteboardDeduplicate } } @UserDefaultsWrapper(key: "wiki.qaq.config.silentMode", defaultValue: false) private var silentModeStore @Published var silentMode: Bool = true { didSet { silentModeStore = silentMode } } @UserDefaultsWrapper(key: "wiki.qaq.config.toastMode", defaultValue: false) private var toastModeStore @Published var toastMode: Bool = true { didSet { toastModeStore = toastMode } } } ================================================ FILE: App/Action/Action/Backend/Executor/Executor.swift ================================================ // // Executor.swift // Action // // Created by Lakr Aream on 2022/7/26. // import AuxiliaryExecute import Foundation final class Executor { static let shared = Executor() let executorDir = ActionApp .documentDirectory .appendingPathComponent("Executor") private init() { let whoami = "/usr/bin/whoami" let receipt = AuxiliaryExecute.spawn(command: whoami) let username = receipt.stdout.trimmingCharacters(in: .whitespacesAndNewlines) guard receipt.exitCode == 0, !username.isEmpty /* , username != "root" */ else { fatalError("Malformed application permission") } print("[*] whoami \(username)") do { let findEnv = AuxiliaryExecute.spawn( command: "/bin/zsh", args: ["-c", "source ~/.zshrc 1>/dev/null 2>/dev/null && echo $PATH"] ) let env = findEnv.stdout.trimmingCharacters(in: .whitespacesAndNewlines) print("[*] setting up env PATH value \(findEnv.stdout)") let envPathBuilder = env.components(separatedBy: ":") .filter { FileManager.default.fileExists(atPath: $0) } let origPathBuilder = ( ProcessInfo .processInfo .environment["PATH"] ?? "" ) .components(separatedBy: ":") .filter { FileManager.default.fileExists(atPath: $0) } let newPath = Array(Set(envPathBuilder + origPathBuilder)) .joined(separator: ":") setenv("PATH", newPath, 1) } try? FileManager.default.createDirectory(at: executorDir, withIntermediateDirectories: true) } enum ExecutorError: Error { case unknown } func obtainXcodeCommandLineToolLocation() -> URL? { let receipt = AuxiliaryExecute.spawn( command: "/usr/bin/xcode-select", args: ["--print-path"] ) let path = receipt .stdout .trimmingCharacters(in: .whitespacesAndNewlines) guard path != "/", path.hasPrefix("/"), FileManager.default.fileExists(atPath: path) else { return nil } return URL(fileURLWithPath: path) } func unarchiveTar(at: URL, toDest: URL) throws { let receipt = AuxiliaryExecute.spawn( command: "/usr/bin/tar", args: ["-xf", at.path, "--directory", toDest.path] ) guard receipt.exitCode == 0 else { throw ExecutorError.unknown } } func speak(_ str: String) { AuxiliaryExecute.spawn( command: "/usr/bin/say", args: [str] ) } } ================================================ FILE: App/Action/Action/Backend/PasteboardManager/PasteboardManager+Event.swift ================================================ // // PasteboardManager+Event.swift // Action // // Created by Lakr Aream on 2022/7/26. // import Foundation extension PasteboardManager { struct PEvent: Codable, Equatable, Hashable { let content: String let app: AppInfo? init(content: String, app: PasteboardManager.AppInfo?) { self.content = content self.app = app } } struct AppInfo: Codable, Equatable, Hashable { let name: String let bundleIdentifier: String init(name: String, bundleIdentifier: String) { self.name = name self.bundleIdentifier = bundleIdentifier } } } ================================================ FILE: App/Action/Action/Backend/PasteboardManager/PasteboardManager.swift ================================================ // // PasteboardManager.swift // Action // // Created by Lakr Aream on 2022/7/25. // import AppKit final class PasteboardManager { static let shared = PasteboardManager() private init() { print("[*] setting up Pasteboard Manager") Thread( target: self, selector: #selector(startMonitorThread), object: nil ) .start() } let systemPasteboard = NSPasteboard.general private var monitorThread: Thread? private var monitorRunLoop: RunLoop? private var accessQueue = DispatchQueue(label: "wiki.qaq.PasteboardManager.accessQueue") private var executeQueue = DispatchQueue(label: "wiki.qaq.PasteboardManager.executeQueue") private var previousPasteboardChangeCount: Int? private var previousPasteboardEvent: PEvent? var eventBlockaded: Bool = false @objc private func startMonitorThread() { monitorThread = Thread.current monitorRunLoop = RunLoop.current defer { self.monitorThread = nil self.monitorRunLoop = nil } let timer = Timer(timeInterval: 0.25, repeats: true) { _ in self.accessQueue.async { self.checkPasteboard() } } RunLoop.current.add(timer, forMode: .common) CFRunLoopRun() } func clearLastEvent() { print("[*] clearing previous pasteboard event") accessQueue.async { self.previousPasteboardEvent = nil } } func requestCheckPasteboard() { accessQueue.async { self.checkPasteboard() } } private func checkPasteboard() { guard systemPasteboard.changeCount != previousPasteboardChangeCount else { return } let newChangeCount = systemPasteboard.changeCount print("[*] NSPasteboard has changeCount \(newChangeCount) previous at \(previousPasteboardChangeCount ?? -1)") previousPasteboardChangeCount = newChangeCount guard let copied = systemPasteboard.string(forType: .string) else { print("[?] system pasteboard does not returns as string > ignoring") return } let app: AppInfo? = obtainRunningApplication() let event = PEvent(content: copied, app: app) print("[*] PasteboardEvent content len \(copied.count) from \(app?.name ?? "nil")") var shouldDispatchEvent = false if let previousEvent = previousPasteboardEvent, previousEvent != event || !Config.shared.pasteboardDeduplicate { shouldDispatchEvent = true } else { print("[*] event content did not change, ignore dispatch") } previousPasteboardEvent = event if shouldDispatchEvent { prepareWorkflow(forPasteboardEvent: event) } } private func obtainRunningApplication() -> AppInfo? { if let axApp = obtainRunningApplicationUsingAXElement() { return axApp } if let currentApplication = NSWorkspace.shared.menuBarOwningApplication, let name = currentApplication.localizedName, let bundleIdentifier = currentApplication.bundleIdentifier { return AppInfo(name: name, bundleIdentifier: bundleIdentifier) } if let currentApplication = NSWorkspace.shared.frontmostApplication, let name = currentApplication.localizedName, let bundleIdentifier = currentApplication.bundleIdentifier { return AppInfo(name: name, bundleIdentifier: bundleIdentifier) } return nil } private func obtainRunningApplicationUsingAXElement() -> AppInfo? { let systemWideElement: AXUIElement = AXUIElementCreateSystemWide() var focusedElement: AnyObject? AXUIElementCopyAttributeValue( systemWideElement, kAXFocusedUIElementAttribute as CFString, &focusedElement ) guard let element = focusedElement else { return nil } var pid: pid_t = 0 AXUIElementGetPid(element as! AXUIElement, &pid) guard pid > 0 else { return nil } let runningApp = NSRunningApplication(processIdentifier: pid) if let name = runningApp?.localizedName, let bundleIdentifier = runningApp?.bundleIdentifier { return AppInfo(name: name, bundleIdentifier: bundleIdentifier) } return nil } private func prepareWorkflow(forPasteboardEvent pasteboardEvent: PEvent) { assert(!Thread.isMainThread) guard !eventBlockaded else { print("[*] this pasteboard has been blockaded due to other process exists") return } PasteboardManager.shared.eventBlockaded = true defer { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { PasteboardManager.shared.eventBlockaded = false } } print("[*] calling workflow manager to resolve event") print(" content length \(pasteboardEvent.content.count)") print(" from \(pasteboardEvent.app?.bundleIdentifier ?? "unknown") (\(pasteboardEvent.app?.name ?? "nope"))") ActionManager.shared.handle(pasteboardEvent: pasteboardEvent) } } ================================================ FILE: App/Action/Action/Backend/StatusBarManager/StatusBarManager.swift ================================================ // // StatusBarManager.swift // Action // // Created by Lakr Aream on 2022/7/26. // import Foundation final class StatusBarManager { var hasWindowOpened: Bool = false static let shared = StatusBarManager() private init() {} } ================================================ FILE: App/Action/Action/Extension/AES.swift ================================================ // // AES.swift // PTFoundation // // Created by Lakr Aream on 12/15/20. // import CommonCrypto import Foundation import KeychainAccess private var bundleId: String { guard let id = Bundle.main.bundleIdentifier, !id.isEmpty else { fatalError("AES Engine requires bundle identifier to work") } return id } private let keychainServiceID = "wiki.qaq.ActionBee.kcAccess" private let keychainMainKeyID = "wiki.qaq.ActionBee.keychainMainKeyID" private let keychainLabel = "ActionBee Main Crypto Key" private let keychainComment = "ActionBee requires this crypto key to access your encrypted data and sign sensitive information." public struct AES { private let key: Data private let iv: Data public static let shared: AES = { #if DEBUG var keyBuilder = "" #if os(macOS) let platformExpert = IOServiceGetMatchingService( kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice") ) guard platformExpert > 0 else { fatalError() } guard let serialNumber = ( IORegistryEntryCreateCFProperty( platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0 ) .takeUnretainedValue() as? String ) else { fatalError() } IOObjectRelease(platformExpert) keyBuilder = serialNumber #else keyBuilder = "0xdeadbeef & 0xbadf00d & 0xdeadbeef & 0xbadf00d & 0xdeadbeef & 0xbadf00d" #endif let key = keyBuilder + keyBuilder + keyBuilder guard let aes = AES(key: key, iv: key) else { fatalError("failed to initialize crypto engine") } return aes #else let keychain = Keychain(service: keychainServiceID) var retry = 3 var key: String? repeat { defer { retry -= 1 } do { let main = try keychain.getString(keychainMainKeyID) if let main = main, main.count > 2 { key = main break } else { try keychain.remove(keychainMainKeyID) let new = UUID().uuidString key = new try keychain .label(keychainLabel) .comment(keychainComment) .set(new, key: keychainMainKeyID) break } } catch { continue } } while retry > 0 guard let key = key else { fatalError("failed to load crypto keys") } guard let aes = AES(key: key, iv: key) else { fatalError("failed to initialize crypto engine") } return aes #endif }() internal init?(key initKey: String, iv initIV: String) { if initKey.count < kCCKeySizeAES128 || initIV.count < kCCBlockSizeAES128 { return nil } var initKey = initKey while initKey.count < 32 { initKey += initKey } while initKey.count > 32 { initKey.removeLast() } guard initKey.count == kCCKeySizeAES128 || initKey.count == kCCKeySizeAES256, let keyData = initKey.data(using: .utf8) else { return nil } var initIV = initIV while initIV.count < kCCBlockSizeAES128 { initIV += initIV } while initIV.count > kCCBlockSizeAES128 { initIV.removeLast() } guard initIV.count == kCCBlockSizeAES128, let ivData = initIV.data(using: .utf8) else { return nil } key = keyData iv = ivData } // MARK: - API public func encrypt(data: Data) -> Data? { crypt(data: data, option: CCOperation(kCCEncrypt)) } public func decrypt(data: Data) -> Data? { crypt(data: data, option: CCOperation(kCCDecrypt)) } // MARK: - INTERNAL private func crypt(data: Data?, option: CCOperation) -> Data? { guard let data = data else { return nil } let cryptLength = data.count + kCCBlockSizeAES128 var cryptData = Data(count: cryptLength) let keyLength = key.count let options = CCOptions(kCCOptionPKCS7Padding) var bytesLength = Int(0) let status = cryptData.withUnsafeMutableBytes { cryptBytes in data.withUnsafeBytes { dataBytes in iv.withUnsafeBytes { ivBytes in key.withUnsafeBytes { keyBytes in CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), options, keyBytes.baseAddress, keyLength, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength) } } } } guard UInt32(status) == UInt32(kCCSuccess) else { assertionFailure() return nil } cryptData.removeSubrange(bytesLength ..< cryptData.count) return cryptData } } ================================================ FILE: App/Action/Action/Extension/Data.swift ================================================ // // Data.swift // Action // // Created by Lakr Aream on 2022/8/16. // import CommonCrypto import Foundation extension Data { func sha256() -> String { var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(count), &digest) } let hexBytes = digest.map { String(format: "%02hhx", $0) } let sha256Hex = hexBytes.joined() return sha256Hex } } ================================================ FILE: App/Action/Action/Extension/DispatchQueue.swift ================================================ // // DispatchQueue.swift // Action // // Created by Lakr Aream on 2022/8/15. // import Foundation extension DispatchQueue { static func withMainAndWait(block: @escaping () -> Void) { assert(!Thread.isMainThread) guard !Thread.isMainThread else { block() return } let sem = DispatchSemaphore(value: 0) DispatchQueue.main.async { block() sem.signal() } sem.wait() } } ================================================ FILE: App/Action/Action/Extension/Notification.swift ================================================ // // Notification.swift // Action // // Created by Lakr Aream on 2022/8/15. // import Foundation extension Notification.Name { static let editAction = Notification.Name("wiki.qaq.editAction") } ================================================ FILE: App/Action/Action/Extension/Result.swift ================================================ // // Result.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Foundation public extension Result where Success == Void { static var success: Result { .success(()) } } ================================================ FILE: App/Action/Action/Extension/Throttle.swift ================================================ // SwiftThrottle.swift // Twitter @Lakr233 // // Created by Lakr Aream on 12/12/20. // import Foundation /* This throttle is intended to prevent the program from crashing with too many requests or is used for saving computer resources. ** Swift Throttle is not designed for operations that require high time accuracy ** */ // MARK: - THROTTLE public class Throttle { // MARK: - PROPERTY /// Setup with these values to control the throttle behave /// - minimumDelay >= 0.5 second is suggested public private(set) var minimumDelay: TimeInterval public private(set) var workingQueue: DispatchQueue /// lock when dispatch job to execution private var executeLock = NSLock() /// These value controls throttle behavior public private(set) var lastExecute: Date? public private(set) var lastRequestWasCanceled: Bool = false public private(set) var scheduled: Bool = false /// Lock when setting jobs, required by thread safe design private var _assignmentLock = NSLock() private var _assignment: (() -> Void)? public private(set) var assignment: (() -> Void)? { set { _assignmentLock.lock() defer { _assignmentLock.unlock() } _assignment = newValue } get { _assignmentLock.lock() defer { _assignmentLock.unlock() } return _assignment } } // MARK: - INIT /// Create a throttle /// - Parameters: /// - minimumDelay: in second /// - queue: the queue that job will executed on, default to main public init(minimumDelay delay: TimeInterval, queue: DispatchQueue = DispatchQueue.main) { minimumDelay = delay workingQueue = queue #if DEBUG if minimumDelay < 0.5 { // we suggest minimumDelay to be at least 0.5 second debugPrint("[SwiftThrottle] " + "minimumDelay(\(minimumDelay) less then 0.5s will be inaccurate" + ", last callback not guaranteed") } #endif } // MARK: - API /// Update property minimumDelay /// - Parameter interval: in second public func updateMinimumDelay(interval: Double) { executeLock.lock() minimumDelay = interval executeLock.unlock() } /// Assign job to throttle /// - Parameter job: call block public func throttle(job: (() -> Void)?) { realThrottle(job: job, useAssignment: false) } // MARK: - BACKEND /// Check nothing but execute /// - Parameter capturedJob: block to execute private func releaseExec(capturedJob: @escaping (() -> Void)) { lastExecute = Date() workingQueue.async { capturedJob() } } /// Throttle is working here /// - Parameters: /// - job: block that was required to execute /// - useAssignment: shall we overwrite assigned job? private func realThrottle(job: (() -> Void)?, useAssignment: Bool) { // lock down every thing when resigning job executeLock.lock() defer { self.executeLock.unlock() } // if called from rescheduled job, cancel job overwrite var capturedJobDecision: (() -> Void)? if !useAssignment { // resign job every time calling from user assignment = job capturedJobDecision = job } else { capturedJobDecision = assignment } guard let capturedJob = capturedJobDecision else { return } // MARK: LOCK BEGIN if let lastExec = lastExecute { // executed before, value negative let timeBetween = -lastExec.timeIntervalSinceNow if timeBetween < minimumDelay { // The throttle will be reprogrammed once for future execution lastRequestWasCanceled = true if !scheduled { scheduled = true let dispatchTime = Double(minimumDelay - timeBetween + 0.01) // Preventing trigger failures // This is where the inaccuracy comes from workingQueue.asyncAfter(deadline: .now() + dispatchTime) { self.realThrottle(job: nil, useAssignment: true) self.scheduled = false } } } else { // Throttle release to execution releaseExec(capturedJob: capturedJob) } } else // never called before, release to execution { releaseExec(capturedJob: capturedJob) } // MARK: LOCK END } } ================================================ FILE: App/Action/Action/Extension/UserDefault.swift ================================================ // // UserDefault.swift // Action // // Created by Lakr Aream on 2022/8/15. // import Foundation #if DEBUG private let keyPrefix = "debug." #else private let keyPrefix = "" #endif @propertyWrapper struct UserDefaultsWrapper { let key: String let defaultValue: Value var storage: UserDefaults = .standard init(key: String, defaultValue: Value, storage: UserDefaults = .standard) { self.key = keyPrefix + key self.defaultValue = defaultValue self.storage = storage } var wrappedValue: Value { get { let value = storage.value(forKey: key) as? Value return value ?? defaultValue } set { storage.setValue(newValue, forKey: key) } } } extension UserDefaultsWrapper where Value: ExpressibleByNilLiteral { init(key: String, storage: UserDefaults = .standard) { self.init(key: key, defaultValue: nil, storage: storage) } } private let documentEncoder = PropertyListEncoder() private let documentDecoder = PropertyListDecoder() @propertyWrapper struct CodableDefaultsWrapper { let key: String let defaultValue: Value var storage: UserDefaults = .standard init(key: String, defaultValue: Value, storage: UserDefaults = .standard) { self.key = keyPrefix + key self.defaultValue = defaultValue self.storage = storage } var wrappedValue: Value { get { guard let data = storage.value(forKey: key) as? Data, let value = try? documentDecoder.decode(Value.self, from: data) else { return defaultValue } return value } set { guard let data = try? documentEncoder.encode(newValue) else { return } storage.setValue(data, forKey: key) } } } @propertyWrapper struct EncryptedCodableDefaultsWrapper { let key: String let defaultValue: Value var storage: UserDefaults = .standard init(key: String, defaultValue: Value, storage: UserDefaults = .standard) { self.key = keyPrefix + key self.defaultValue = defaultValue self.storage = storage } var wrappedValue: Value { get { guard let data = storage.value(forKey: key) as? Data, let decrypted = AES.shared.decrypt(data: data), let value = try? documentDecoder.decode(Value.self, from: decrypted) else { return defaultValue } return value } set { guard let data = try? documentEncoder.encode(newValue), let encrypt = AES.shared.encrypt(data: data) else { return } storage.setValue(encrypt, forKey: key) } } } extension EncryptedCodableDefaultsWrapper where Value: ExpressibleByNilLiteral { init(key: String, storage: UserDefaults = .standard) { self.init(key: key, defaultValue: nil, storage: storage) } } ================================================ FILE: App/Action/Action/Extension/View.swift ================================================ // // View.swift // Action // // Created by Lakr Aream on 2022/7/25. // import SwiftUI extension View { func usePreferredContentSize() -> some View { frame( minWidth: 400, idealWidth: 500, maxWidth: .infinity, minHeight: 300, idealHeight: 350, maxHeight: .infinity, alignment: .center ) } } ================================================ FILE: App/Action/Action/Interface/ActionModule/ModuleCreateView.swift ================================================ // // ModuleCreateView.swift // Action // // Created by Lakr Aream on 2022/7/26. // import SwiftUI struct ModuleCreateSheet: View { @Environment(\.presentationMode) var presentationMode @State var moduleName: String = "Module - 0x\(Int.random(in: 100_000 ... 999_999))" @State var selectedTemplate: ActionManager.ModuleTemplateIdentifier = .swift @State var showProgress: Bool = false var body: some View { VStack(alignment: .leading, spacing: 6) { if showProgress { ProgressView() .frame(maxWidth: .infinity, maxHeight: .infinity) } else { Label("Create Action", systemImage: "doc.badge.gearshape.fill") .font(.system(.headline, design: .rounded)) Divider() TextField("Module Name", text: $moduleName) Picker("Language", selection: $selectedTemplate) { ForEach(ActionManager.ModuleTemplateIdentifier.allCases, id: \.self) { template in Text(template.obtainTemplateDetails().getLanguage()) } } Divider() HStack { Button("Cancel") { presentationMode.wrappedValue.dismiss() } .keyboardShortcut(.cancelAction) Spacer() Button("Next") { callCreate() } .buttonStyle(.borderedProminent) .keyboardShortcut(.defaultAction) } } } .padding() .frame(width: 300, alignment: .center) } func callCreate() { showProgress = true DispatchQueue.global().async { var actionId: UUID? defer { DispatchQueue.main.async { presentationMode.wrappedValue.dismiss() } DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { if let id = actionId { ActionManager.shared.initialingAciton.insert(id) NotificationCenter.default.post(name: .editAction, object: id) } } } print("[*] \(moduleName) \(selectedTemplate)") actionId = ActionManager.shared.createAction( withName: moduleName, withModuleTemplate: selectedTemplate ) } } } ================================================ FILE: App/Action/Action/Interface/ActionModule/ModuleEditView.swift ================================================ // // ModuleEditView.swift // Action // // Created by Lakr Aream on 2022/8/15. // import SwiftUI import SymbolPicker struct ModuleEditView: View { let id: UUID @Environment(\.presentationMode) var presentationMode @State var editingAction: ActionManager.Action? = nil @State var actionEnabled: Bool = true @State var openSymbolPicker: Bool = false @State var openCompileView: Bool = false @State var hoverApplication: String? = nil @State var compileLog: String = "" @State var compilerFinished: Bool = false var body: some View { VStack(alignment: .leading, spacing: 8) { if editingAction == nil { brokenModule } else { HStack { Label("Edit Action", systemImage: "slider.horizontal.3") .font(.system(.headline, design: .rounded)) Spacer() Button { delete() } label: { Image(systemName: "trash") .font(.system(.headline, design: .rounded)) .foregroundColor(.accentColor) } .buttonStyle(.plain) } Divider() basicMetaBlock enableForAppsBlock editCodeBlock Divider() HStack { Button("Cancel") { presentationMode.wrappedValue.dismiss() } .keyboardShortcut(.cancelAction) Spacer() Button("Save") { finalizeEdit() } .buttonStyle(.borderedProminent) .keyboardShortcut(.defaultAction) } } } .onAppear { editingAction = ActionManager.shared[id] actionEnabled = ActionManager.shared.enabledActions.contains(id) } .opacity(openCompileView ? 0 : 1) .overlay(compileOverlay) .padding() .frame(width: 500, alignment: .center) } var compileOverlay: some View { VStack(alignment: .leading) { if !compilerFinished { ProgressView().padding(.top, 20) } Spacer().frame(height: 20) Text(compilerFinished ? "Compile Finished" : "Compiling Source") .font(.headline) Spacer().frame(height: 6) if !compilerFinished { RandomCodeTextView() } Divider() ScrollView(.vertical, showsIndicators: true) { VStack(alignment: .leading, spacing: 4) { ForEach(Array( compileLog .components(separatedBy: "\n") .reversed() .enumerated() ), id: \.offset) { _, log in Text(log).textSelection(.enabled) } .font(.system(.footnote, design: .monospaced)) .opacity(0.5) } } Button("Cancel") { compileLog = "" openCompileView = false } .opacity(compilerFinished ? 1 : 0) } .opacity(openCompileView ? 1 : 0) } var brokenModule: some View { VStack { Image(systemName: "xmark.seal.fill") .font(.system(size: 36, weight: .semibold, design: .rounded)) .foregroundColor(.pink) .frame(width: 80, height: 80) Text("Broken Module") .font(.headline) Divider() HStack { Button("Delete") { ActionManager.shared.deleteModule(withId: id) } Button("Close") { presentationMode.wrappedValue.dismiss() } .buttonStyle(.borderedProminent) } } .padding() } var actionIcon: String { guard let icon = editingAction?.icon, !icon.isEmpty else { return "text.append" } return icon } var basicMetaBlock: some View { Group { Toggle("Enabled", isOn: $actionEnabled) .font(.system(.headline, design: .rounded)) HStack { Button { openSymbolPicker = true } label: { Image(systemName: actionIcon) } TextField("Name", text: Binding( get: { editingAction?.name ?? "" }, set: { newValue in editingAction?.name = newValue } )) Text("Timeout: ") TextField("Name", text: Binding( get: { String(editingAction?.timeout ?? 5) }, set: { newValue in editingAction?.timeout = Int(newValue) ?? 5 } )) .frame(width: 26) Text("s") } Text("ID: \(editingAction?.id.uuidString ?? "0x4422DEADBEEF")") .textSelection(.enabled) .font(.system(.footnote, design: .monospaced)) } .sheet(isPresented: $openSymbolPicker) { SymbolPicker(symbol: Binding( get: { actionIcon }, set: { newValue in editingAction?.icon = newValue } )) } } var enableForAppsBlock: some View { Group { HStack { Text("Enable In App") .font(.system(.headline, design: .rounded)) Spacer() Button { addApp() } label: { Image(systemName: "plus") .foregroundColor(.accentColor) .font(.system(.headline, design: .rounded)) } .buttonStyle(.plain) } if let appList = editingAction?.enabledAppList, !appList.isEmpty { ScrollView(.horizontal, showsIndicators: false) { HStack { ForEach(appList, id: \.self) { appId in ApplicationView(appId: appId) .blur(radius: hoverApplication == appId ? 6 : 0) .overlay { Button { editingAction?.enabledAppList = editingAction? .enabledAppList .filter { $0 != appId } ?? [] } label: { Image(systemName: "xmark") .foregroundColor(.white) .font(.system(.headline, design: .rounded)) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.black.opacity(0.5)) .cornerRadius(4) } .buttonStyle(.plain) .opacity(hoverApplication == appId ? 1 : 0) } .animation(.interactiveSpring(), value: hoverApplication) .onHover { hover in if hover { hoverApplication = appId } else { hoverApplication = nil } } } } } .frame(height: 26) } else { Button { addApp() } label: { Label("Enabled for All Apps", systemImage: "app.badge.checkmark") .font(.system(.subheadline, design: .rounded)) .frame(height: 26) .frame(maxWidth: .infinity) .background(Color.accentColor.opacity(0.1)) .cornerRadius(4) } .buttonStyle(.plain) } Text("This pasteboard action will only run if copying from these apps") .textSelection(.enabled) .font(.system(.footnote)) } } var editCodeBlock: some View { Group { Text("Coding") .font(.system(.headline, design: .rounded)) HStack(spacing: 8) { Button("Edit Code") { editModule() } .buttonStyle(.borderedProminent) Button("Show in Finder") { showInFinder() } Button("Export") { exportModule() } } VStack(alignment: .leading, spacing: 2) { Text("Module Template: \(editingAction?.template.obtainTemplateDetails().getLanguage() ?? "Unknown")") Text(editingAction?.template.obtainTemplateDetails().getBuildHint() ?? "No Build Hint") .underline() Text("You should recompile, click save, this module each time you edit it") } .textSelection(.enabled) .font(.system(.footnote)) } } func finalizeEdit() { guard ActionManager.shared[id] != nil else { presentationMode.wrappedValue.dismiss() return } guard let action = editingAction else { presentationMode.wrappedValue.dismiss() return } ActionManager.shared.invalidateArtifactCache(forAction: id) ActionManager.shared.enabledActions = ActionManager.shared .enabledActions .filter { $0 != id } guard actionEnabled else { presentationMode.wrappedValue.dismiss() return } ActionManager.shared.enabledActions += [id] ActionManager.shared.initialingAciton.remove(id) compile { result in switch result { case .success: ActionManager.shared[id] = action presentationMode.wrappedValue.dismiss() case let .failure(failure): compilerFinished = true let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "Unable to compile this action: \(failure.message)" if let window = NSApp.keyWindow { alert.beginSheetModal(for: window) } else { alert.runModal() } } } } func delete() { let alert = NSAlert() alert.messageText = NSLocalizedString("Are you sure you want to delete this module? This operation can not be undone.", comment: "") alert.alertStyle = .critical alert.addButton(withTitle: NSLocalizedString("Delete", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) guard let window = NSApp.keyWindow else { return } alert.beginSheetModal(for: window) { resp in guard resp == .alertFirstButtonReturn else { return } presentationMode.wrappedValue.dismiss() DispatchQueue.main.async { ActionManager.shared[id] = nil } } } func addApp() { let openPanel = NSOpenPanel() openPanel.prompt = NSLocalizedString("Select Application", comment: "") openPanel.allowedContentTypes = [.application] openPanel.allowsMultipleSelection = true openPanel.canChooseDirectories = true openPanel.treatsFilePackagesAsDirectories = true openPanel.directoryURL = URL(fileURLWithPath: "/Applications/") guard let window = NSApp.keyWindow else { return } openPanel.beginSheetModal(for: window) { resp in guard resp == .OK else { return } var buildId: Set = [] for id in editingAction?.enabledAppList ?? [] { buildId.insert(id) } for url in openPanel.urls { guard let bundle = Bundle(path: url.path), let id = bundle.bundleIdentifier else { continue } buildId.insert(id) } editingAction?.enabledAppList = Array(buildId).sorted() } } func showInFinder() { let url = ActionManager .shared .actionModuleBaseUrl .appendingPathComponent(editingAction?.id.uuidString ?? "") guard NSWorkspace.shared.open(url) else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Failed to load this module", comment: "") if let window = NSApp.keyWindow { alert.beginSheetModal(for: window) } return } } func editModule() { let result = editingAction?.template.obtainTemplateDetails() .openDesignatedEditor(id: id) if case let .failure(failure) = result { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = failure.message if let window = NSApp.keyWindow { alert.beginSheetModal(for: window) } } } func exportModule() { let panel = NSSavePanel() panel.nameFieldStringValue = "Module Export - \(editingAction?.name ?? "Unnamed")" guard let window = NSApp.keyWindow else { return } panel.beginSheetModal(for: window) { resp in guard resp == .OK, let url = panel.url else { return } ActionManager.shared.updateActionModuleManifest(onActionId: id) try? FileManager.default.removeItem(at: url) try? FileManager.default.copyItem( at: ActionManager.shared.actionModuleBaseUrl.appendingPathComponent(id.uuidString), to: url ) } } func compile(completion: @escaping (Result) -> Void = { _ in }) { openCompileView = true guard let action = editingAction else { openCompileView = false completion(.failure(.brokenResources)) return } DispatchQueue.global().async { let result = ActionManager.shared.issueCompile(forAction: action.id) { str in DispatchQueue.withMainAndWait { compileLog.append(str) } } DispatchQueue.main.async { completion(result) } } } } ================================================ FILE: App/Action/Action/Interface/ActionModule/ModuleElementView.swift ================================================ // // ModuleElementView.swift // Action // // Created by Lakr Aream on 2022/8/15. // import SwiftUI struct ModuleElementView: View { let id: UUID let notificationPublisher = NotificationCenter .default .publisher(for: .editAction) .receive(on: RunLoop.main) @StateObject var actionManager = ActionManager.shared @State var openEdit: Bool = false var gradientColor: Gradient { if actionManager.enabledActions.contains(id) { if actionManager.artifacts[id] == nil { return Gradient(colors: [.pink, .red]) } else { return Gradient(colors: [.yellow, .orange]) } } else { return Gradient(colors: [.gray, .black.opacity(0.8)]) } } var body: some View { Button { openEdit = true } label: { LinearGradient( gradient: gradientColor, startPoint: .topTrailing, endPoint: .bottomTrailing ) .overlay { Color.orange.opacity(0.5) } .overlay { content } .cornerRadius(8) .clipped() .frame(width: 140, height: 80) } .overlay { if actionManager.artifacts[id] == nil { Image(systemName: "xmark.octagon.fill") .foregroundColor(.white) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) .padding(4) } } .buttonStyle(.plain) .onHover { hover in if hover { NSCursor.pointingHand.push() } else { NSCursor.pop() } } .onReceive(notificationPublisher) { notification in guard let notificationId = notification.object as? UUID, notificationId == id else { return } openEdit = true } .sheet(isPresented: $openEdit) { ModuleEditView(id: id) } } var content: some View { Group { if let action = actionManager[id] { VStack(spacing: 4) { Image(systemName: action.icon) .font(.system(.headline, design: .rounded)) .foregroundColor(.white) .frame(maxWidth: .infinity, alignment: .leading) Spacer() Text(action.name) .font(.system(.headline, design: .rounded)) .lineLimit(3) .foregroundColor(.white) .frame(maxWidth: .infinity, alignment: .leading) } .background( Image(systemName: action.icon) .font(.system(size: 48, weight: .semibold, design: .rounded)) .foregroundColor(.white) .opacity(0.1) .offset(x: 50, y: 10) ) .padding(8) } else { Text("Error").font(.headline) } } } } ================================================ FILE: App/Action/Action/Interface/ActionModule/ModuleImportView.swift ================================================ // // ModuleImportView.swift // Action // // Created by Lakr Aream on 2022/8/18. // import SwiftUI struct ModuleImportView: View { let url: URL @Environment(\.presentationMode) var presentationMode @State var openEdit: Bool = false @State var editingAction: ActionManager.Action.ID? = nil var body: some View { VStack(alignment: .leading, spacing: 12) { Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 42, weight: .semibold, design: .rounded)) .foregroundColor(.pink) Text("You are about to import an untrusted module") .font(.system(.headline)) .foregroundColor(.pink) Text("Importing malicious module may damage your system, you are in charge to review this module.") .font(.system(.footnote)) .foregroundColor(.pink) HStack { Button("Trust & Import") { startImport() } .keyboardShortcut(.defaultAction) .tint(.pink) .buttonStyle(.borderedProminent) Button("Cancel") { presentationMode.wrappedValue.dismiss() } .keyboardShortcut(.cancelAction) } Divider() Text(url.path) .underline() .font(.system(.footnote, design: .monospaced)) .opacity(0.5) .onHover { if $0 { NSCursor.pointingHand.push() } else { NSCursor.pop() }} .onTapGesture { NSWorkspace.shared.open(url) } } .padding() .sheet(isPresented: $openEdit) { ModuleEditView(id: editingAction ?? .init()) } .onChange(of: openEdit) { newValue in if newValue == false, editingAction != nil { presentationMode.wrappedValue.dismiss() } } .frame(width: 400) } func startImport() { DispatchQueue.global().async { let result = ActionManager.shared.importModule(at: url) DispatchQueue.main.async { switch result { case let .success(action): openEdit = true editingAction = action case let .failure(failure): guard let window = NSApp.keyWindow else { presentationMode.wrappedValue.dismiss() return } let alert = NSAlert() alert.alertStyle = .critical alert.messageText = failure.localizedDescription alert.addButton(withTitle: "OK") alert.beginSheetModal(for: window) { _ in presentationMode.wrappedValue.dismiss() } } } } } } ================================================ FILE: App/Action/Action/Interface/ActionModule/ModuleManageView.swift ================================================ // // ModuleManageView.swift // Action // // Created by Lakr Aream on 2022/7/26. // import SwiftUI struct ModuleManageView: View { @ObservedObject var actionManager = ActionManager.shared @State var searchKey: String = "" @State var openCreate: Bool = false @State var importQueue: [URL]? = nil @State var importingItem: URL? = nil var actions: [ActionManager.Action] { if searchKey.isEmpty { return actionManager.actions } else { let key = searchKey.lowercased() return actionManager .actions .filter { $0.name.lowercased().contains(key) } } } var body: some View { GeometryReader { r in if actionManager.actions.isEmpty { VStack(spacing: 12) { Image(systemName: "arrow.up") .font(.system(size: 26, weight: .regular, design: .rounded)) Text("Create an action by click plus button on toolbar to process your pasteboard event. Format text, clean up links, speak when copy from special app, send to your device, etc etc. Choose an language you are familiar with to get start.") .font(.system(.subheadline)) } .padding() .frame(maxWidth: .infinity, maxHeight: .infinity) } else { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 140, maximum: 140))], alignment: .leading, spacing: 8) { ForEach(actions, id: \.hashValue) { action in ModuleElementView(id: action.id) } } .padding(10) .animation(.interactiveSpring(), value: r.size) .animation(.interactiveSpring(), value: searchKey) } } } .sheet(isPresented: $openCreate) { ModuleCreateSheet() } .sheet( isPresented: Binding( get: { importingItem != nil }, set: { opened in importingItem = nil if !opened { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { if !(importQueue?.isEmpty ?? true) { importQueue?.removeFirst() } checkImportQueue() } } } ) ) { ModuleImportView(url: importingItem ?? URL(fileURLWithPath: "/bad/")) } .searchable(text: $searchKey) .toolbar { ToolbarItem { Button { openCreate = true } label: { Label("Add Action", systemImage: "plus") } .keyboardShortcut("n", modifiers: .command) } ToolbarItem { Button { importActions() } label: { Label("Import Action", systemImage: "square.and.arrow.down") } } } .navigationTitle("Module") .usePreferredContentSize() } func checkImportQueue() { guard let newValue = importQueue else { return } guard !newValue.isEmpty else { importQueue = nil importingItem = nil return } importingItem = newValue.first } func importActions() { let panel = NSOpenPanel() panel.canChooseDirectories = true panel.canChooseFiles = false panel.resolvesAliases = true panel.treatsFilePackagesAsDirectories = true panel.allowsMultipleSelection = true guard let window = NSApp.keyWindow else { return } panel.beginSheetModal(for: window) { resp in guard resp == .OK, !panel.urls.isEmpty else { return } self.importQueue = panel.urls self.checkImportQueue() } } func importModule(at: URL) { assert(!Thread.isMainThread) let sem = DispatchSemaphore(value: 0) DispatchQueue.main.async { guard let window = NSApp.keyWindow else { sem.signal() return } let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "You are about to import an Action Module which is untrusted with no signature. Importing malicious modules can pose unknown risks and there is no sandbox nor container dealing with it." alert.informativeText = at.path alert.addButton(withTitle: "Trust And Import") alert.addButton(withTitle: "Cancel") alert.beginSheetModal(for: window) { resp in guard resp == .alertFirstButtonReturn else { sem.signal() return } DispatchQueue.global().async { let result = actionManager.importModule(at: at) if case let .failure(failure) = result { print(failure.localizedDescription) let sem2 = DispatchSemaphore(value: 0) DispatchQueue.main.async { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "Failed to import this module" alert.informativeText = failure.localizedDescription alert.beginSheetModal(for: window) { _ in sem2.signal() } } sem2.wait() } sem.signal() } } } sem.wait() } } ================================================ FILE: App/Action/Action/Interface/Effect/RandomCodeTextView.swift ================================================ // // RandomCodeTextView.swift // Action // // Created by Lakr Aream on 2022/8/16. // import SwiftUI struct RandomCodeTextView: View { @State var code = "Made with love by @Lakr233 " let timer = Timer .publish(every: 0.1, on: .main, in: .common) .autoconnect() var body: some View { Text(code) .font(.system(.footnote, design: .monospaced)) .lineLimit(1) .onReceive(timer) { _ in if code.count > 50 { code = "" } for _ in 0 ... Int.random(in: 1 ... 3) { if let c = "`1234567890-=qwertyuiop[]asdfghjkl;'\\zxcvbnm,./".randomElement() { code += String(c) } } } } } ================================================ FILE: App/Action/Action/Interface/Generic/ApplicationView.swift ================================================ // // ApplicationView.swift // Action // // Created by Lakr Aream on 2022/8/16. // import SwiftUI struct ApplicationView: View { let appId: String var body: some View { Group { if let url = NSWorkspace .shared .urlForApplication(withBundleIdentifier: appId), let bundle = Bundle(url: url) { HStack(spacing: 4) { Image(nsImage: NSWorkspace.shared.icon(forFile: url.path)) .resizable() .antialiased(true) .frame(width: 24, height: 24) .cornerRadius(4) .clipped() VStack(alignment: .leading, spacing: 2) { Text(bundle.infoDictionary?[kCFBundleNameKey as String] as? String ?? "Unknown Name") .font(.system(size: 10, weight: .semibold, design: .rounded)) .lineLimit(1) Text(appId) .font(.system(size: 6, weight: .semibold, design: .monospaced)) .lineLimit(1) } } } else { HStack(spacing: 4) { Image(systemName: "questionmark.app.dashed") .font(.system(size: 16, weight: .regular, design: .rounded)) Text(appId) .font(.system(size: 10, weight: .semibold, design: .rounded)) } } } .frame(height: 26) } } ================================================ FILE: App/Action/Action/Interface/Generic/DiagnosticLogView.swift ================================================ // // DiagnosticLogView.swift // Action // // Created by Lakr Aream on 2022/8/15. // import SwiftUI class Logger: ObservableObject { fileprivate static let shared = Logger() private let logCountLimitation = 4096 private init() { logs.reserveCapacity(logCountLimitation + 1) } struct Log: Identifiable, Equatable { var id: UUID = .init() var message: String } @Published var logs: [Log] = [] private var logsLock = NSLock() fileprivate func append(_ str: String) { DispatchQueue.global().async { [self] in logsLock.lock() var read = logs read.append(.init(message: str)) if read.count > logCountLimitation { read.removeFirst(read.count - logCountLimitation) } DispatchQueue.withMainAndWait { self.logs = read } logsLock.unlock() } } } // overwrite print function func print(_ str: String) { let str = str.trimmingCharacters(in: .newlines) Swift.print(str) Logger.shared.append(str) } struct DiagnosticLogView: View { @StateObject var logger = Logger.shared @State var highlight: Logger.Log.ID? @State var searchKey: String = "" var logs: [Logger.Log] { if searchKey.count > 0 { let key = searchKey.lowercased() return logger .logs .filter { $0.message.lowercased().contains(key) } } else { return logger.logs } } var body: some View { ScrollView(.vertical, showsIndicators: true) { ScrollViewReader { reader in LazyVStack(alignment: .leading, spacing: 0) { ForEach(logs) { log in ScrollView(.horizontal, showsIndicators: false) { Text(log.message) .textSelection(.enabled) } .frame(maxWidth: .infinity) .padding(2) .background( RoundedRectangle(cornerRadius: 4) .foregroundColor(.accentColor) .opacity(highlight == log.id ? 0.1 : 0) .animation(.interactiveSpring(), value: highlight) ) .tag(log.id) .onHover { hover in if hover { highlight = log.id } else { highlight = nil } } } .font(.system(size: 10, weight: .regular, design: .monospaced)) } .padding(10) .onChange(of: logger.logs) { newValue in guard let id = newValue.last?.id else { return } withAnimation(.interactiveSpring()) { reader.scrollTo(id) } } } } .searchable(text: $searchKey) .toolbar { ToolbarItem { Button { let panel = NSSavePanel() panel.nameFieldStringValue = "ActionBee Diagnostic \(Int(Date().timeIntervalSince1970)).log" guard let window = NSApp.keyWindow else { return } panel.beginSheetModal(for: window) { resp in guard resp == .OK, let url = panel.url else { return } let logs = logger.logs.map(\.message).joined(separator: "\n") try? logs.write(toFile: url.path, atomically: true, encoding: .utf8) } } label: { Label("Share", systemImage: "square.and.arrow.up") } } } .navigationTitle("Diagnostic") .usePreferredContentSize() } } ================================================ FILE: App/Action/Action/Interface/Generic/HistoryView.swift ================================================ // // HistoryView.swift // Action // // Created by Lakr Aream on 2022/8/16. // import SwiftUI struct HistoryView: View { @StateObject var actionManager = ActionManager.shared @State var searchKey = "" @State var hoverId: ActionManager.HistoryElement.ID? = nil var histories: [ActionManager.HistoryElement] { if searchKey.isEmpty { return actionManager.histories } let key = searchKey.lowercased() return actionManager .histories .filter { $0.search(with: key) } } var body: some View { Group { if histories.isEmpty { VStack(spacing: 12) { Image(systemName: "rectangle.dashed.badge.record") .font(.system(size: 26, weight: .regular, design: .rounded)) Text("No History Was Found") .font(.system(.footnote)) } .padding() .frame(maxWidth: .infinity, maxHeight: .infinity) } else { ScrollView { LazyVStack(spacing: 2) { ForEach(histories.reversed()) { record in HStack(alignment: .top, spacing: 6) { Text("> ") .font(.system(.subheadline, design: .rounded)) Divider() HistoryRecordView(record: record) } .frame(maxWidth: .infinity) .padding(2) .background(Color.accentColor.opacity(record.id == hoverId ? 0.1 : 0)) .cornerRadius(8) .onHover { hover in hoverId = hover ? record.id : nil } } } .padding(10) } .animation(.interactiveSpring(), value: hoverId) .animation(.interactiveSpring(), value: searchKey) } } .toolbar { ToolbarItem { Button { clearHistory() } label: { Label("Clear History", systemImage: "xmark.circle") } } } .searchable(text: $searchKey) .navigationTitle("History") .usePreferredContentSize() } func clearHistory() { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Are you sure you want to delete all history records? This operation can not be undone.", comment: "") alert.addButton(withTitle: NSLocalizedString("Delete", comment: "")) alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) guard let window = NSApp.keyWindow else { return } alert.beginSheetModal(for: window) { resp in guard resp == .alertFirstButtonReturn else { return } actionManager.histories = [] } } } struct HistoryRecordView: View { let record: ActionManager.HistoryElement var body: some View { VStack(alignment: .leading, spacing: 4) { Text("Pasteboard Event") .font(.system(.subheadline, design: .monospaced)) .bold() Text("Length: \(record.event.content.count) Action Candidates: \(record.actionCandidates.count)") if !record.succeedAction.isEmpty { Divider() ForEach(record.succeedAction) { item in Text("+ [\(ActionManager.shared[item.action]?.name ?? "Deleted Action")]") Text(" Post Action: \(item.recipeAction)") ScrollView(.horizontal, showsIndicators: false) { Text(" Content: \(item.recipeContent)").lineLimit(1) } } .foregroundColor(.blue) } if !record.failedAction.isEmpty { Divider() ForEach(record.failedAction) { item in Text("- [\(ActionManager.shared[item.action]?.name ?? "Deleted Action")]") Text(" \(item.errorHint)") } .foregroundColor(.pink) } Divider() Text(record.date.formatted(date: .complete, time: .complete)) .opacity(0.5) } .font(.system(.footnote, design: .monospaced)) .frame(maxWidth: .infinity, alignment: .leading) } } ================================================ FILE: App/Action/Action/Interface/Generic/LicenseView.swift ================================================ // // LicenseView.swift // Action // // Created by Lakr Aream on 2022/8/17. // import SwiftUI struct LicenseView: View { @State var agreed = false @Environment(\.presentationMode) var presentationMode var licenseText: String { guard let url = Bundle.main.url(forResource: "License", withExtension: "txt"), let text = try? String(contentsOfFile: url.path) else { return "This app's bundle is broken, do not use it." } return text } var body: some View { VStack(alignment: .leading, spacing: 8) { Label("Software License", systemImage: "flag.2.crossed") .font(.system(.headline, design: .rounded)) Divider() ScrollView { Text(licenseText) .font(.system(.subheadline, design: .rounded)) } .frame(maxHeight: 250) Divider() HStack { Toggle("I understand and agree to this license.", isOn: $agreed) Spacer() Button("Done") { ActionApp.agreeToLicense = agreed presentationMode.wrappedValue.dismiss() } .disabled(!agreed) .buttonStyle(.borderedProminent) .keyboardShortcut(.defaultAction) } } .padding() .task { agreed = ActionApp.agreeToLicense } .onChange(of: agreed) { newValue in if !newValue { ActionApp.agreeToLicense = false } } .frame(width: 450, alignment: .center) } } ================================================ FILE: App/Action/Action/Interface/Generic/MainView.swift ================================================ // // ContentView.swift // Action // // Created by Lakr Aream on 2022/7/25. // import SwiftUI struct MainView: View { @State var openArgumentsSeet: Bool = false var body: some View { NavigationView { SidebarView() WelcomeView() } .navigationTitle("Action Bee") .toolbar { ToolbarItem(placement: .navigation) { Button { NSApp.keyWindow?.firstResponder?.tryToPerform( #selector(NSSplitViewController.toggleSidebar(_:)), with: nil ) } label: { Label("Toggle Sidebar", systemImage: "sidebar.leading") } } } .sheet(isPresented: $openArgumentsSeet) { LicenseView() } .task { _ = Menubar.shared } .task { checkRequirements() } .onChange(of: openArgumentsSeet) { newValue in if !newValue { checkRequirements() } } } func checkRequirements() { guard ActionApp.agreeToLicense else { openArgumentsSeet = true return } } } ================================================ FILE: App/Action/Action/Interface/Generic/SettingView.swift ================================================ // // SettingView.swift // Action // // Created by Lakr Aream on 2022/8/17. // import SwiftUI struct SettingView: View { @StateObject var config = Config.shared @State var showLicense = false var body: some View { ScrollView { VStack(alignment: .leading, spacing: 10) { Section { Toggle("Pasteboard Deduplicate", isOn: $config.pasteboardDeduplicate) .font(.subheadline) Text("Pasteboard content matches previous will not generate event if on") .font(.footnote) Toggle("Silent Mode", isOn: $config.silentMode) .font(.subheadline) Text("Do not show popover after action triggered") .font(.footnote) Toggle("Toast Mode", isOn: $config.toastMode) .font(.subheadline) .disabled(config.silentMode) Text("Use toast instead of popover on menubar") .font(.footnote) .opacity(config.silentMode ? 0.25 : 1) Toggle("Reduced UI Effects", isOn: $config.reducedEffects) .font(.subheadline) Text("Turning off visual effects does not affect app's core functionality") .font(.footnote) } header: { Text("Application") .font(.system(.headline, design: .rounded)) } footer: { Divider() } Label("EOF", systemImage: "text.append") .font(.system(.caption2, design: .rounded)) } .padding(10) } .toolbar { ToolbarItem { Button { NSWorkspace.shared.open(URL(string: "https://github.com/Lakr233/ActionBee")!) } label: { Label("Get Source Code", systemImage: "chevron.left.forwardslash.chevron.right") } } ToolbarItem { Button { showLicense = true } label: { Label("License", systemImage: "flag.2.crossed") } .sheet(isPresented: $showLicense) { LicenseView() } } } .navigationTitle("Setting") .usePreferredContentSize() } } ================================================ FILE: App/Action/Action/Interface/Generic/SidebarView.swift ================================================ // // SidebarView.swift // Action // // Created by Lakr Aream on 2022/7/25. // import SwiftUI #if DEBUG private let stubNavigationTarget: some View = Text("Hello World") .usePreferredContentSize() #endif struct SidebarView: View { var body: some View { List { Section("App") { NavigationLink { WelcomeView() } label: { Label("Welcome", systemImage: "sun.min.fill") } } Section("Action") { NavigationLink { ModuleManageView() } label: { Label("Module", systemImage: "tray.full") } NavigationLink { HistoryView() } label: { Label("History", systemImage: "clock") } } Section("Misc") { NavigationLink { SettingView() } label: { Label("Setting", systemImage: "gear") } NavigationLink { DiagnosticLogView() } label: { Label("Diagnostic", systemImage: "heart.text.square") } } } .listStyle(SidebarListStyle()) } } ================================================ FILE: App/Action/Action/Interface/Generic/WelcomeView.swift ================================================ // // WelcomeView.swift // Action // // Created by Lakr Aream on 2022/7/25. // import Colorful import SwiftUI struct WelcomeView: View { @State var config = Config.shared var version: String { var ret = "Version: " + (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown") + " Build: " + (Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown") #if DEBUG ret = "👾 \(ret) 👾" #endif return ret } var body: some View { ZStack { if !config.reducedEffects { ColorfulView(colors: [.accentColor], colorCount: 4) .ignoresSafeArea() } VStack(spacing: 4) { Image("Avatar") .antialiased(true) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 128, height: 128) Spacer().frame(height: 16) Text("Welcome to Action Bee") .font(.system(.headline, design: .rounded)) Text("A programmable pasteboard action trigger.") .font(.system(.body, design: .rounded)) Spacer().frame(height: 24) } VStack { Spacer() Text(version) .font(.system(size: 12, weight: .semibold, design: .rounded)) .opacity(0.5) } } .padding() .navigationTitle("Action Bee") .usePreferredContentSize() } } ================================================ FILE: App/Action/Action/Interface/Menubar/Menubar.swift ================================================ // // Menubar.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Cocoa import SwiftUI class Menubar: ObservableObject { static let shared = Menubar() var popover: NSPopover var statusItem: NSStatusItem? var eventMonitor: EventMonitor? private init() { let statusItem = NSStatusBar .system .statusItem(withLength: NSStatusItem.variableLength) statusItem.button?.action = #selector(togglePopover(sender:)) self.statusItem = statusItem let buildPopover = NSPopover() popover = buildPopover let view = MenubarView() buildPopover.contentViewController = NSHostingController(rootView: view) eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown], handler: mouseEventHandler) statusItem.button?.title = "🎉" statusItem.button?.target = self } func showPopover(_: AnyObject? = nil) { if let statusBarButton = statusItem?.button { popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY) eventMonitor?.start() } } func hidePopover(_ sender: AnyObject? = nil) { popover.performClose(sender) eventMonitor?.stop() } func mouseEventHandler(_ event: NSEvent?) { if popover.isShown, let event = event { hidePopover(event) } } @objc func togglePopover(sender: AnyObject) { if popover.isShown { hidePopover(sender) } else { showPopover(sender) } } enum TitleType: String { case ready = "🎉" case running = "💨" } private let titleThrottle = Throttle(minimumDelay: 0.5, queue: .main) func switchTitle(status: TitleType) { titleThrottle.throttle { self.statusItem?.button?.title = status.rawValue } } } extension Menubar { class EventMonitor { private var monitor: Any? private let mask: NSEvent.EventTypeMask private let handler: (NSEvent?) -> Void public init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) { self.mask = mask self.handler = handler } deinit { stop() } public func start() { monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler) as! NSObject } public func stop() { if monitor != nil { NSEvent.removeMonitor(monitor!) monitor = nil } } } } ================================================ FILE: App/Action/Action/Interface/Menubar/MenubarView.swift ================================================ // // MenubarView.swift // Action // // Created by Lakr Aream on 2022/8/16. // import Colorful import SwiftUI struct MenubarView: View { @StateObject var menubar = Menubar.shared @StateObject var actionManager = ActionManager.shared var lastHistory: ActionManager.HistoryElement? { if let last = actionManager.histories.last, last.date > ActionApp.bootTime { return last } return nil } var body: some View { ZStack { if actionManager.actionRunning { ZStack { VStack(spacing: 20) { ProgressView() Text(actionManager.actionRunningHint) .font(.system(.subheadline, design: .monospaced)) } } .frame(width: 400, height: 200) } else if let lastHistory = lastHistory { VStack { Spacer().frame(height: 16) Image( systemName: lastHistory.failedAction.isEmpty ? "checkmark.circle.fill" : "checkmark.circle.trianglebadge.exclamationmark" ) .foregroundColor(lastHistory.failedAction.isEmpty ? .green : .orange) .font(.system(size: 36, weight: .semibold, design: .rounded)) Spacer().frame(height: 16) HistoryRecordView(record: lastHistory) Divider().hidden() } .padding() .frame(width: 400) } else { VStack(alignment: .leading, spacing: 8) { Image("Avatar") .antialiased(true) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 50, height: 50) HStack { Image(systemName: "circle.fill") .font(.headline) .foregroundColor(.green) Text("ActionBee is ready to accept pasteboard events.") .font(.headline) } RandomCodeTextView() Divider().hidden() } .padding() .frame(width: 400, height: 200) } } } } ================================================ FILE: App/Action/Action/Interface/Toast/Toast.swift ================================================ // // Toast.swift // Action // // Created by Lakr Aream on 2022/8/17. // import Cocoa import Foundation import SwiftUI private class ToastWindow: NSWindow { init(with screen: NSScreen) { super.init( contentRect: screen.frame, styleMask: [.borderless, .fullSizeContentView], backing: .buffered, defer: false ) isOpaque = false alphaValue = 1 titleVisibility = .hidden titlebarAppearsTransparent = true backgroundColor = .clear ignoresMouseEvents = true isMovable = false isMovableByWindowBackground = false // .fullScreenAuxiliary .stationary .canJoinAllSpaces collectionBehavior = NSWindow.CollectionBehavior(rawValue: 273) styleMask = .borderless // The standard ScreenSaverView class actually sets the window // level to 2002, not the 1000 defined by NSScreenSaverWindowLevel // and kCGScreenSaverWindowLevel /// https://github.com/genekogan/ofxScreenGrab/blob/master/src/macGlutfix.m level = NSWindow.Level(rawValue: 2005) setFrameOrigin(screen.frame.origin) makeKeyAndOrderFront(nil) hasShadow = false } } private class ToastWindowController: NSWindowController { init(with screen: NSScreen, systemIcon: String, message: String) { super.init(window: ToastWindow(with: screen)) contentViewController = NSHostingController( rootView: ToastView(systemIcon: systemIcon, message: message) ) } @available(*, unavailable) required init(coder _: NSCoder) { fatalError() } } struct ToastView: View { let systemIcon: String let message: String @State var opacity: Double = 1 var body: some View { GeometryReader { _ in HStack { Spacer() VStack { Spacer() Spacer() Spacer() Spacer() content Spacer() } Spacer() } } .opacity(opacity) .animation(.interactiveSpring(), value: opacity) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { opacity = 0 } } } var content: some View { VStack(alignment: .center, spacing: 12) { Image(systemName: systemIcon) .font(.system(size: 36, weight: .bold, design: .rounded)) Text(message) .font(.system(.headline, design: .rounded)) } .padding() .background(.regularMaterial) .cornerRadius(8) } } enum Toast { static func post(systemIcon: String, message: String) { guard let screen = NSScreen.main else { return } let windowController = ToastWindowController( with: screen, systemIcon: systemIcon, message: message ) windowController.window?.setFrameOrigin(screen.frame.origin) windowController.window?.setContentSize(screen.frame.size) windowController.window?.makeKeyAndOrderFront(nil) DispatchQueue.main.asyncAfter(deadline: .now() + 3) { windowController.window?.close() } } } ================================================ FILE: App/Action/Action.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 13C6C62128AF1BF0008ADA60 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 13C6C62328AF1BF0008ADA60 /* Localizable.strings */; }; 1F6BCF0628B219B400C2B417 /* Template+Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6BCF0528B219B400C2B417 /* Template+Node.swift */; }; 5001740128AA557100FF9B99 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5001740028AA557100FF9B99 /* UserDefault.swift */; }; 5001740528AA818500FF9B99 /* DiagnosticLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5001740428AA818500FF9B99 /* DiagnosticLogView.swift */; }; 5006F55E28A9663100C8ADBE /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5006F55D28A9663100C8ADBE /* Notification.swift */; }; 5006F56028A9683C00C8ADBE /* DispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5006F55F28A9683C00C8ADBE /* DispatchQueue.swift */; }; 502D1F0A28A8F88D0022CCD3 /* ActionTemplates in Resources */ = {isa = PBXBuildFile; fileRef = 502D1F0928A8F88D0022CCD3 /* ActionTemplates */; }; 502E87A328AA954500CAB5E1 /* ModuleEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502E87A228AA954500CAB5E1 /* ModuleEditView.swift */; }; 502E87A528AA95BF00CAB5E1 /* ModuleElementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502E87A428AA95BF00CAB5E1 /* ModuleElementView.swift */; }; 5030D80828ABDD72001A96B5 /* ActionManager+History.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D80728ABDD72001A96B5 /* ActionManager+History.swift */; }; 5030D80A28ABE953001A96B5 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D80928ABE953001A96B5 /* HistoryView.swift */; }; 5030D80C28ABEF64001A96B5 /* ApplicationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D80B28ABEF64001A96B5 /* ApplicationView.swift */; }; 5030D80E28ABFA82001A96B5 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D80D28ABFA82001A96B5 /* SettingView.swift */; }; 5030D81028AC0045001A96B5 /* LicenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D80F28AC0045001A96B5 /* LicenseView.swift */; }; 5030D81228AC01A7001A96B5 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5030D81128AC01A7001A96B5 /* License.txt */; }; 5030D81628AC090A001A96B5 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5030D81528AC090A001A96B5 /* Config.swift */; }; 503976FA28AE143600588622 /* ModuleImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503976F928AE143600588622 /* ModuleImportView.swift */; }; 5053CF85288F724E00A92822 /* Executor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF84288F724E00A92822 /* Executor.swift */; }; 5053CF87288F764000A92822 /* ModuleManageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF86288F764000A92822 /* ModuleManageView.swift */; }; 5053CF8A288F76E700A92822 /* PasteboardManager+Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF89288F76E700A92822 /* PasteboardManager+Event.swift */; }; 5053CF91288F776000A92822 /* ActionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF90288F776000A92822 /* ActionManager.swift */; }; 5053CF93288F788B00A92822 /* ActionManager+Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF92288F788B00A92822 /* ActionManager+Module.swift */; }; 5053CF9A288F9A5E00A92822 /* ModuleCreateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053CF99288F9A5E00A92822 /* ModuleCreateView.swift */; }; 5053CF9C288F9D8700A92822 /* SymbolPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 5053CF9B288F9D8700A92822 /* SymbolPicker */; }; 505A10A828A95EC200D46DB3 /* ActionManager+Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505A10A728A95EC200D46DB3 /* ActionManager+Template.swift */; }; 5067392628AB786A003A6A9C /* Template+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067392528AB786A003A6A9C /* Template+Swift.swift */; }; 5067392B28AB7A51003A6A9C /* ActionManager+Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067392A28AB7A51003A6A9C /* ActionManager+Event.swift */; }; 5067392D28AB7D6C003A6A9C /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067392C28AB7D6C003A6A9C /* Result.swift */; }; 5067393228AB8E55003A6A9C /* RandomCodeTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067393128AB8E55003A6A9C /* RandomCodeTextView.swift */; }; 5067393428ABA328003A6A9C /* ActionManager+Artifact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067393328ABA328003A6A9C /* ActionManager+Artifact.swift */; }; 5067393628ABA422003A6A9C /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 5067393528ABA422003A6A9C /* KeychainAccess */; }; 5067393828ABA445003A6A9C /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067393728ABA445003A6A9C /* AES.swift */; }; 5067393C28ABA89D003A6A9C /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067393B28ABA89D003A6A9C /* Data.swift */; }; 5067394128ABC9C3003A6A9C /* Menubar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067394028ABC9C3003A6A9C /* Menubar.swift */; }; 5067394328ABCB18003A6A9C /* MenubarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067394228ABCB18003A6A9C /* MenubarView.swift */; }; 507137EC288E934000C23E7F /* ActionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507137EB288E934000C23E7F /* ActionApp.swift */; }; 507137EE288E934000C23E7F /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507137ED288E934000C23E7F /* MainView.swift */; }; 507137F0288E934100C23E7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 507137EF288E934100C23E7F /* Assets.xcassets */; }; 507137FC288E935A00C23E7F /* AuxiliaryExecute in Frameworks */ = {isa = PBXBuildFile; productRef = 507137FB288E935A00C23E7F /* AuxiliaryExecute */; }; 507137FE288E935A00C23E7F /* Colorful in Frameworks */ = {isa = PBXBuildFile; productRef = 507137FD288E935A00C23E7F /* Colorful */; }; 50713803288E976600C23E7F /* AppSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50713802288E976600C23E7F /* AppSetup.swift */; }; 50713808288E9C0C00C23E7F /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50713807288E9C0C00C23E7F /* View.swift */; }; 5071380A288E9C8100C23E7F /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50713809288E9C8100C23E7F /* WelcomeView.swift */; }; 5071380C288E9C8800C23E7F /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5071380B288E9C8800C23E7F /* SidebarView.swift */; }; 5071380E288E9D8A00C23E7F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5071380D288E9D8A00C23E7F /* AppDelegate.swift */; }; 50A7D7FE28ACCB4C004F0B34 /* Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A7D7FD28ACCB4C004F0B34 /* Throttle.swift */; }; 50A7D80128ACCE23004F0B34 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A7D80028ACCE23004F0B34 /* Toast.swift */; }; 50A7D80328ACD14B004F0B34 /* Template+Executable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A7D80228ACD14B004F0B34 /* Template+Executable.swift */; }; 50BCF6AC288EE81000A97B4C /* PasteboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BCF6AB288EE81000A97B4C /* PasteboardManager.swift */; }; 50BCF6AE288F01EF00A97B4C /* StatusBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BCF6AD288F01EF00A97B4C /* StatusBarManager.swift */; }; 50E5204228BE04FC00C3228F /* Template+Python.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E5204128BE04FC00C3228F /* Template+Python.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 13C6C62228AF1BF0008ADA60 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 13C6C62428AF1DEB008ADA60 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 1F6BCF0528B219B400C2B417 /* Template+Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Template+Node.swift"; sourceTree = ""; }; 5001740028AA557100FF9B99 /* UserDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; }; 5001740428AA818500FF9B99 /* DiagnosticLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticLogView.swift; sourceTree = ""; }; 5006F55D28A9663100C8ADBE /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = ""; }; 5006F55F28A9683C00C8ADBE /* DispatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchQueue.swift; sourceTree = ""; }; 502D1F0928A8F88D0022CCD3 /* ActionTemplates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ActionTemplates; sourceTree = ""; }; 502E87A228AA954500CAB5E1 /* ModuleEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleEditView.swift; sourceTree = ""; }; 502E87A428AA95BF00CAB5E1 /* ModuleElementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleElementView.swift; sourceTree = ""; }; 5030D80728ABDD72001A96B5 /* ActionManager+History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionManager+History.swift"; sourceTree = ""; }; 5030D80928ABE953001A96B5 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = ""; }; 5030D80B28ABEF64001A96B5 /* ApplicationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationView.swift; sourceTree = ""; }; 5030D80D28ABFA82001A96B5 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = ""; }; 5030D80F28AC0045001A96B5 /* LicenseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicenseView.swift; sourceTree = ""; }; 5030D81128AC01A7001A96B5 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = License.txt; sourceTree = ""; }; 5030D81528AC090A001A96B5 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 503976F928AE143600588622 /* ModuleImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleImportView.swift; sourceTree = ""; }; 5053CF84288F724E00A92822 /* Executor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Executor.swift; sourceTree = ""; }; 5053CF86288F764000A92822 /* ModuleManageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleManageView.swift; sourceTree = ""; }; 5053CF89288F76E700A92822 /* PasteboardManager+Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PasteboardManager+Event.swift"; sourceTree = ""; }; 5053CF90288F776000A92822 /* ActionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionManager.swift; sourceTree = ""; }; 5053CF92288F788B00A92822 /* ActionManager+Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionManager+Module.swift"; sourceTree = ""; }; 5053CF99288F9A5E00A92822 /* ModuleCreateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleCreateView.swift; sourceTree = ""; }; 505A10A728A95EC200D46DB3 /* ActionManager+Template.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionManager+Template.swift"; sourceTree = ""; }; 5067392528AB786A003A6A9C /* Template+Swift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Template+Swift.swift"; sourceTree = ""; }; 5067392A28AB7A51003A6A9C /* ActionManager+Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionManager+Event.swift"; sourceTree = ""; }; 5067392C28AB7D6C003A6A9C /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 5067393128AB8E55003A6A9C /* RandomCodeTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomCodeTextView.swift; sourceTree = ""; }; 5067393328ABA328003A6A9C /* ActionManager+Artifact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActionManager+Artifact.swift"; sourceTree = ""; }; 5067393728ABA445003A6A9C /* AES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = ""; }; 5067393B28ABA89D003A6A9C /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; 5067394028ABC9C3003A6A9C /* Menubar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menubar.swift; sourceTree = ""; }; 5067394228ABCB18003A6A9C /* MenubarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenubarView.swift; sourceTree = ""; }; 507137E8288E934000C23E7F /* Action.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Action.app; sourceTree = BUILT_PRODUCTS_DIR; }; 507137EB288E934000C23E7F /* ActionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionApp.swift; sourceTree = ""; }; 507137ED288E934000C23E7F /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; 507137EF288E934100C23E7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 507137F4288E934100C23E7F /* Action.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Action.entitlements; sourceTree = ""; }; 50713802288E976600C23E7F /* AppSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSetup.swift; sourceTree = ""; }; 50713807288E9C0C00C23E7F /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; 50713809288E9C8100C23E7F /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; 5071380B288E9C8800C23E7F /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; 5071380D288E9D8A00C23E7F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 50A7D7FD28ACCB4C004F0B34 /* Throttle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Throttle.swift; sourceTree = ""; }; 50A7D80028ACCE23004F0B34 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 50A7D80228ACD14B004F0B34 /* Template+Executable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Template+Executable.swift"; sourceTree = ""; }; 50BCF6AB288EE81000A97B4C /* PasteboardManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardManager.swift; sourceTree = ""; }; 50BCF6AD288F01EF00A97B4C /* StatusBarManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarManager.swift; sourceTree = ""; }; 50E5204128BE04FC00C3228F /* Template+Python.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Template+Python.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 507137E5288E934000C23E7F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 507137FE288E935A00C23E7F /* Colorful in Frameworks */, 507137FC288E935A00C23E7F /* AuxiliaryExecute in Frameworks */, 5053CF9C288F9D8700A92822 /* SymbolPicker in Frameworks */, 5067393628ABA422003A6A9C /* KeychainAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 502E87A628AA95DF00CAB5E1 /* ActionModule */ = { isa = PBXGroup; children = ( 5053CF86288F764000A92822 /* ModuleManageView.swift */, 503976F928AE143600588622 /* ModuleImportView.swift */, 502E87A428AA95BF00CAB5E1 /* ModuleElementView.swift */, 5053CF99288F9A5E00A92822 /* ModuleCreateView.swift */, 502E87A228AA954500CAB5E1 /* ModuleEditView.swift */, ); path = ActionModule; sourceTree = ""; }; 502E87A728AA95E800CAB5E1 /* Generic */ = { isa = PBXGroup; children = ( 507137ED288E934000C23E7F /* MainView.swift */, 5071380B288E9C8800C23E7F /* SidebarView.swift */, 50713809288E9C8100C23E7F /* WelcomeView.swift */, 5030D80928ABE953001A96B5 /* HistoryView.swift */, 5030D80D28ABFA82001A96B5 /* SettingView.swift */, 5001740428AA818500FF9B99 /* DiagnosticLogView.swift */, 5030D80F28AC0045001A96B5 /* LicenseView.swift */, 5030D80B28ABEF64001A96B5 /* ApplicationView.swift */, ); path = Generic; sourceTree = ""; }; 5030D81428AC08FC001A96B5 /* Config */ = { isa = PBXGroup; children = ( 5030D81528AC090A001A96B5 /* Config.swift */, ); path = Config; sourceTree = ""; }; 5053CF88288F76CC00A92822 /* PasteboardManager */ = { isa = PBXGroup; children = ( 50BCF6AB288EE81000A97B4C /* PasteboardManager.swift */, 5053CF89288F76E700A92822 /* PasteboardManager+Event.swift */, ); path = PasteboardManager; sourceTree = ""; }; 5053CF8B288F770B00A92822 /* StatusBarManager */ = { isa = PBXGroup; children = ( 50BCF6AD288F01EF00A97B4C /* StatusBarManager.swift */, ); path = StatusBarManager; sourceTree = ""; }; 5053CF8C288F771500A92822 /* Executor */ = { isa = PBXGroup; children = ( 5053CF84288F724E00A92822 /* Executor.swift */, ); path = Executor; sourceTree = ""; }; 5053CF8D288F771C00A92822 /* Action */ = { isa = PBXGroup; children = ( 5053CF90288F776000A92822 /* ActionManager.swift */, 5030D80728ABDD72001A96B5 /* ActionManager+History.swift */, 5053CF92288F788B00A92822 /* ActionManager+Module.swift */, 5067393328ABA328003A6A9C /* ActionManager+Artifact.swift */, 5067392A28AB7A51003A6A9C /* ActionManager+Event.swift */, 5067392728AB78CD003A6A9C /* ActionModuleTemplates */, 502D1F0928A8F88D0022CCD3 /* ActionTemplates */, ); path = Action; sourceTree = ""; }; 5067392728AB78CD003A6A9C /* ActionModuleTemplates */ = { isa = PBXGroup; children = ( 505A10A728A95EC200D46DB3 /* ActionManager+Template.swift */, 50A7D80228ACD14B004F0B34 /* Template+Executable.swift */, 5067392528AB786A003A6A9C /* Template+Swift.swift */, 1F6BCF0528B219B400C2B417 /* Template+Node.swift */, 50E5204128BE04FC00C3228F /* Template+Python.swift */, ); path = ActionModuleTemplates; sourceTree = ""; }; 5067393028AB8E42003A6A9C /* Effect */ = { isa = PBXGroup; children = ( 5067393128AB8E55003A6A9C /* RandomCodeTextView.swift */, ); path = Effect; sourceTree = ""; }; 5067393F28ABC9B1003A6A9C /* Menubar */ = { isa = PBXGroup; children = ( 5067394028ABC9C3003A6A9C /* Menubar.swift */, 5067394228ABCB18003A6A9C /* MenubarView.swift */, ); path = Menubar; sourceTree = ""; }; 507137DF288E934000C23E7F = { isa = PBXGroup; children = ( 507137EA288E934000C23E7F /* Action */, 507137E9288E934000C23E7F /* Products */, 507137FA288E935A00C23E7F /* Frameworks */, ); sourceTree = ""; }; 507137E9288E934000C23E7F /* Products */ = { isa = PBXGroup; children = ( 507137E8288E934000C23E7F /* Action.app */, ); name = Products; sourceTree = ""; }; 507137EA288E934000C23E7F /* Action */ = { isa = PBXGroup; children = ( 50713801288E975200C23E7F /* Application */, 50BCF6AA288EE7F900A97B4C /* Backend */, 50713805288E9B5200C23E7F /* Interface */, 50713806288E9C0100C23E7F /* Extension */, ); path = Action; sourceTree = ""; }; 507137FA288E935A00C23E7F /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 50713801288E975200C23E7F /* Application */ = { isa = PBXGroup; children = ( 13C6C62328AF1BF0008ADA60 /* Localizable.strings */, 507137EB288E934000C23E7F /* ActionApp.swift */, 507137F4288E934100C23E7F /* Action.entitlements */, 507137EF288E934100C23E7F /* Assets.xcassets */, 5030D81128AC01A7001A96B5 /* License.txt */, 50713802288E976600C23E7F /* AppSetup.swift */, 5071380D288E9D8A00C23E7F /* AppDelegate.swift */, ); path = Application; sourceTree = ""; }; 50713805288E9B5200C23E7F /* Interface */ = { isa = PBXGroup; children = ( 502E87A628AA95DF00CAB5E1 /* ActionModule */, 5067393028AB8E42003A6A9C /* Effect */, 502E87A728AA95E800CAB5E1 /* Generic */, 5067393F28ABC9B1003A6A9C /* Menubar */, 50A7D7FF28ACCE18004F0B34 /* Toast */, ); path = Interface; sourceTree = ""; }; 50713806288E9C0100C23E7F /* Extension */ = { isa = PBXGroup; children = ( 5067393728ABA445003A6A9C /* AES.swift */, 5067393B28ABA89D003A6A9C /* Data.swift */, 5006F55F28A9683C00C8ADBE /* DispatchQueue.swift */, 5006F55D28A9663100C8ADBE /* Notification.swift */, 5067392C28AB7D6C003A6A9C /* Result.swift */, 5001740028AA557100FF9B99 /* UserDefault.swift */, 50713807288E9C0C00C23E7F /* View.swift */, 50A7D7FD28ACCB4C004F0B34 /* Throttle.swift */, ); path = Extension; sourceTree = ""; }; 50A7D7FF28ACCE18004F0B34 /* Toast */ = { isa = PBXGroup; children = ( 50A7D80028ACCE23004F0B34 /* Toast.swift */, ); path = Toast; sourceTree = ""; }; 50BCF6AA288EE7F900A97B4C /* Backend */ = { isa = PBXGroup; children = ( 5053CF8D288F771C00A92822 /* Action */, 5030D81428AC08FC001A96B5 /* Config */, 5053CF8C288F771500A92822 /* Executor */, 5053CF88288F76CC00A92822 /* PasteboardManager */, 5053CF8B288F770B00A92822 /* StatusBarManager */, ); path = Backend; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 507137E7288E934000C23E7F /* Action */ = { isa = PBXNativeTarget; buildConfigurationList = 507137F7288E934100C23E7F /* Build configuration list for PBXNativeTarget "Action" */; buildPhases = ( 507137FF288E93A600C23E7F /* Swift Format */, 502D1F0B28A8FA910022CCD3 /* Update Action Templates */, 5030D81328AC022A001A96B5 /* Scan License */, 507137E4288E934000C23E7F /* Sources */, 507137E5288E934000C23E7F /* Frameworks */, 507137E6288E934000C23E7F /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Action; packageProductDependencies = ( 507137FB288E935A00C23E7F /* AuxiliaryExecute */, 507137FD288E935A00C23E7F /* Colorful */, 5053CF9B288F9D8700A92822 /* SymbolPicker */, 5067393528ABA422003A6A9C /* KeychainAccess */, ); productName = Action; productReference = 507137E8288E934000C23E7F /* Action.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 507137E0288E934000C23E7F /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 507137E7288E934000C23E7F = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 507137E3288E934000C23E7F /* Build configuration list for PBXProject "Action" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, "zh-Hans", ); mainGroup = 507137DF288E934000C23E7F; productRefGroup = 507137E9288E934000C23E7F /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 507137E7288E934000C23E7F /* Action */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 507137E6288E934000C23E7F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 502D1F0A28A8F88D0022CCD3 /* ActionTemplates in Resources */, 13C6C62128AF1BF0008ADA60 /* Localizable.strings in Resources */, 507137F0288E934100C23E7F /* Assets.xcassets in Resources */, 5030D81228AC01A7001A96B5 /* License.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 502D1F0B28A8FA910022CCD3 /* Update Action Templates */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = "Update Action Templates"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "$SOURCE_ROOT/../../Resources/Scripts/PackTemplates.sh\n"; }; 5030D81328AC022A001A96B5 /* Scan License */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = "Scan License"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "$SOURCE_ROOT/../../Resources/Scripts/UpdateLicenses.sh\n"; }; 507137FF288E93A600C23E7F /* Swift Format */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( ); name = "Swift Format"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "cd $SOURCE_ROOT\npwd\n\nif ! command -v \"swiftformat\" &> /dev/null\nthen\n echo \"swiftformat could not be found, skipping\"\n exit 0\nfi\n\nswiftformat . --swiftversion 5.6\n"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 507137E4288E934000C23E7F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50E5204228BE04FC00C3228F /* Template+Python.swift in Sources */, 50BCF6AE288F01EF00A97B4C /* StatusBarManager.swift in Sources */, 503976FA28AE143600588622 /* ModuleImportView.swift in Sources */, 5067393C28ABA89D003A6A9C /* Data.swift in Sources */, 5030D81028AC0045001A96B5 /* LicenseView.swift in Sources */, 50A7D80328ACD14B004F0B34 /* Template+Executable.swift in Sources */, 5053CF8A288F76E700A92822 /* PasteboardManager+Event.swift in Sources */, 5006F56028A9683C00C8ADBE /* DispatchQueue.swift in Sources */, 5030D80E28ABFA82001A96B5 /* SettingView.swift in Sources */, 5001740528AA818500FF9B99 /* DiagnosticLogView.swift in Sources */, 5071380A288E9C8100C23E7F /* WelcomeView.swift in Sources */, 5030D80A28ABE953001A96B5 /* HistoryView.swift in Sources */, 5030D80828ABDD72001A96B5 /* ActionManager+History.swift in Sources */, 502E87A328AA954500CAB5E1 /* ModuleEditView.swift in Sources */, 5067392628AB786A003A6A9C /* Template+Swift.swift in Sources */, 50713808288E9C0C00C23E7F /* View.swift in Sources */, 507137EE288E934000C23E7F /* MainView.swift in Sources */, 5001740128AA557100FF9B99 /* UserDefault.swift in Sources */, 50A7D80128ACCE23004F0B34 /* Toast.swift in Sources */, 50A7D7FE28ACCB4C004F0B34 /* Throttle.swift in Sources */, 5067393828ABA445003A6A9C /* AES.swift in Sources */, 50713803288E976600C23E7F /* AppSetup.swift in Sources */, 5071380E288E9D8A00C23E7F /* AppDelegate.swift in Sources */, 5053CF85288F724E00A92822 /* Executor.swift in Sources */, 5067392D28AB7D6C003A6A9C /* Result.swift in Sources */, 5067394128ABC9C3003A6A9C /* Menubar.swift in Sources */, 5053CF91288F776000A92822 /* ActionManager.swift in Sources */, 5053CF93288F788B00A92822 /* ActionManager+Module.swift in Sources */, 5030D80C28ABEF64001A96B5 /* ApplicationView.swift in Sources */, 5067392B28AB7A51003A6A9C /* ActionManager+Event.swift in Sources */, 507137EC288E934000C23E7F /* ActionApp.swift in Sources */, 5067394328ABCB18003A6A9C /* MenubarView.swift in Sources */, 5006F55E28A9663100C8ADBE /* Notification.swift in Sources */, 5067393228AB8E55003A6A9C /* RandomCodeTextView.swift in Sources */, 5071380C288E9C8800C23E7F /* SidebarView.swift in Sources */, 5030D81628AC090A001A96B5 /* Config.swift in Sources */, 5067393428ABA328003A6A9C /* ActionManager+Artifact.swift in Sources */, 50BCF6AC288EE81000A97B4C /* PasteboardManager.swift in Sources */, 5053CF87288F764000A92822 /* ModuleManageView.swift in Sources */, 502E87A528AA95BF00CAB5E1 /* ModuleElementView.swift in Sources */, 5053CF9A288F9A5E00A92822 /* ModuleCreateView.swift in Sources */, 505A10A828A95EC200D46DB3 /* ActionManager+Template.swift in Sources */, 1F6BCF0628B219B400C2B417 /* Template+Node.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 13C6C62328AF1BF0008ADA60 /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( 13C6C62228AF1BF0008ADA60 /* en */, 13C6C62428AF1DEB008ADA60 /* zh-Hans */, ); name = Localizable.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 507137F5288E934100C23E7F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 507137F6288E934100C23E7F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 507137F8288E934100C23E7F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Action/Action.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = M4Z5DVY94F; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "Action Bee"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Lakr Aream. All Rights Reserved."; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1.5; PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.Action; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Debug; }; 507137F9288E934100C23E7F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Action/Action.entitlements; CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = M4Z5DVY94F; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDisplayName = "Action Bee"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity"; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Lakr Aream. All Rights Reserved."; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 1.5; PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.Action; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 507137E3288E934000C23E7F /* Build configuration list for PBXProject "Action" */ = { isa = XCConfigurationList; buildConfigurations = ( 507137F5288E934100C23E7F /* Debug */, 507137F6288E934100C23E7F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 507137F7288E934100C23E7F /* Build configuration list for PBXNativeTarget "Action" */ = { isa = XCConfigurationList; buildConfigurations = ( 507137F8288E934100C23E7F /* Debug */, 507137F9288E934100C23E7F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 5053CF9B288F9D8700A92822 /* SymbolPicker */ = { isa = XCSwiftPackageProductDependency; productName = SymbolPicker; }; 5067393528ABA422003A6A9C /* KeychainAccess */ = { isa = XCSwiftPackageProductDependency; productName = KeychainAccess; }; 507137FB288E935A00C23E7F /* AuxiliaryExecute */ = { isa = XCSwiftPackageProductDependency; productName = AuxiliaryExecute; }; 507137FD288E935A00C23E7F /* Colorful */ = { isa = XCSwiftPackageProductDependency; productName = Colorful; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 507137E0288E934000C23E7F /* Project object */; } ================================================ FILE: App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: External/AuxiliaryExecute/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: External/AuxiliaryExecute/LICENSE ================================================ MIT License Copyright (c) 2021 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: External/AuxiliaryExecute/Package.swift ================================================ // swift-tools-version:5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "AuxiliaryExecute", products: [ .library( name: "AuxiliaryExecute", targets: ["AuxiliaryExecute"] ), ], targets: [ .target( name: "AuxiliaryExecute", dependencies: [] ), .testTarget( name: "AuxiliaryExecuteTests", dependencies: ["AuxiliaryExecute"] ), ] ) ================================================ FILE: External/AuxiliaryExecute/README.md ================================================ # AuxiliaryExecute A Swift wrapper for system shell over posix_spawn with search path and env support. ## Usage ``` import AuxiliaryExecute AuxiliaryExecute.local.bash(command: "echo nya") ``` ## Customization & Defaults The source for this package is well explained in details along with comments. Feel free looking for them. ``` // automatically search for binary within env PATH let result = AuxiliaryExecute.local.shell( command: "bash", args: ["-c", "echo $mua"], environment: ["mua": "nya"], timeout: 0 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } // or call with binary's full path func spawn( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil ) // for customize option for shell func appendSearchPath(with value: String) func updateExtraSearchPath(with block: (inout [String]) -> Void) func updateOverwriteTable(with block: (inout [String: String?]) -> Void) ``` ## License AuxiliaryExecute is licensed under [MIT](./LICENSE). --- Copyright © 2021 Lakr Aream. All Rights Reserved. ================================================ FILE: External/AuxiliaryExecute/Sources/AuxiliaryExecute/AuxiliaryExecute+Async.swift ================================================ // // AuxiliaryExecute+Spawn.swift // AuxiliaryExecute // // Created by Cyandev on 2022/1/10. // #if swift(>=5.5) import Foundation @available(iOS 15.0, macOS 12.0.0, *) public extension AuxiliaryExecute { /// async/await function for spawn using withCheckedContinuation /// - Parameters: /// - command: full path of the binary file. eg: "/bin/cat" /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read /// - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read /// - Returns: execution recipe, see it's definition for details @discardableResult static func spawnAsync( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil ) async -> ExecuteRecipe { await withCheckedContinuation { cont in self.spawn( command: command, args: args, environment: environment, timeout: timeout, stdoutBlock: stdoutBlock, stderrBlock: stderrBlock ) { recipe in cont.resume(returning: recipe) } } } /// async/await function for spawn using withCheckedContinuation /// - Parameters: /// - command: full path of the binary file. eg: "/bin/cat" /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - output: a block call from pipeControlQueue in background when buffer from stdout or stderr available for read /// - Returns: execution recipe, see it's definition for details @discardableResult static func spawnAsync( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, output: ((String) -> Void)? = nil ) async -> ExecuteRecipe { let lock = NSLock() return await spawnAsync( command: command, args: args, environment: environment, timeout: timeout, stdoutBlock: { str in lock.lock() output?(str) lock.unlock() }, stderrBlock: { str in lock.lock() output?(str) lock.unlock() } ) } } #endif ================================================ FILE: External/AuxiliaryExecute/Sources/AuxiliaryExecute/AuxiliaryExecute+Shell.swift ================================================ // // AuxiliaryExecute+Shell.swift // AuxiliaryExecute // // Created by Lakr Aream on 2021/12/6. // import Foundation public extension AuxiliaryExecute { /// Setup binary table, require lock internal func setupBinaryTable() { lock.lock() let environmentPath = ProcessInfo .processInfo .environment["PATH"]? .components(separatedBy: ":") .compactMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) } ?? [] currentPath = environmentPath binaryTable.removeAll() // now, let's search inside your path and system path for eachPath in environmentPath + extraSearchPath { if let dirElements = try? FileManager .default .contentsOfDirectory(atPath: eachPath) { for node in dirElements { let itemLocation = URL(fileURLWithPath: eachPath) .appendingPathComponent(node) if !isBinaryValid(at: itemLocation) { continue } binaryTable[node] = itemLocation.path } } } lock.unlock() } /// append the search path, thread safe /// - Parameter value: the path func appendSearchPath(with value: String) { lock.lock() extraSearchPath.append(value) lock.unlock() } /// update the customized search path, thread safe /// - Parameter block: update inside this block func updateExtraSearchPath(with block: (inout [String]) -> Void) { lock.lock() block(&extraSearchPath) lock.unlock() } /// update the customized binary table, thread safe /// - Parameter block: update inside this block func updateOverwriteTable(with block: (inout [String: String?]) -> Void) { lock.lock() block(&overwriteTable) lock.unlock() } /// return whether you telling us to not to find anything with this command in shell /// - Parameter command: command name /// - Returns: should skip search internal func commandShouldNotExists(command: String) -> Bool { lock.lock() defer { lock.unlock() } if overwriteTable.keys.contains(command) { if let value = overwriteTable[command] { // it is a String?? :) return value == nil } else { // there must be something wrong assertionFailure("broken memory found with overwriteTable") return false } } return false } /// safely grab the full path for shell command /// - Parameter command: command name /// - Returns: full path if exists, otherwise nil func binaryLocationFor(command: String) -> String? { lock.lock() defer { lock.unlock() } // check if you overwritten the command first if overwriteTable.keys.contains(command) { if let value = overwriteTable[command] { // it is a String?? :) // you telling us to not find anything with this command return value } else { // there must be something wrong assertionFailure("broken memory found with overwriteTable") return nil } } else { return binaryTable[command] } } /// Indicate if a file has permission to execute, check's with permission /// - Parameter at: file url /// - Returns: able to execute or not private func isBinaryValid(at: URL) -> Bool { FileManager .default .isExecutableFile(atPath: at.path) } /// call a binary to execute /// - Parameters: /// - command: the command's name, not full path. eg: bash /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read /// - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read /// - Returns: execution recipe, see it's definition for details @discardableResult func shell( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil ) -> ExecuteRecipe { // the command with full file system path var commandLocation: String? if let location = binaryLocationFor(command: command) { commandLocation = location } else if !commandShouldNotExists(command: command) { // before calling to setup, check if you telling us so // if not so, search for the binary, for two reason: // - table is not yet built // - it may be added to the system after last setup setupBinaryTable() } // if nil, look for the command once again, after another setup if commandLocation == nil { commandLocation = binaryLocationFor(command: command) } // make sure we find the command guard let commandLocation = commandLocation else { return ExecuteRecipe.failure(error: .commandNotFound) } // now, let's validate the command guard isBinaryValid(at: URL(fileURLWithPath: commandLocation)) else { return ExecuteRecipe.failure(error: .commandInvalid) } // finally let‘s call the spawn let recipe = AuxiliaryExecute.spawn( command: commandLocation, args: args, environment: environment, timeout: timeout, stdoutBlock: stdoutBlock, stderrBlock: stderrBlock ) return recipe } /// run script with bash, if bash available /// - Parameters: /// - command: script to be passed to bash. eg: "echo nya" /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read /// - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read /// - Returns: execution recipe, see it's definition for details @discardableResult func bash( command: String, environment: [String: String] = [:], timeout: Double = 0, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil ) -> ExecuteRecipe { let result = shell( command: "bash", args: ["-c", command], environment: environment, timeout: timeout, stdoutBlock: stdoutBlock, stderrBlock: stderrBlock ) return result } } ================================================ FILE: External/AuxiliaryExecute/Sources/AuxiliaryExecute/AuxiliaryExecute+Spawn.swift ================================================ // // AuxiliaryExecute+Spawn.swift // AuxiliaryExecute // // Created by Lakr Aream on 2021/12/6. // import Foundation public extension AuxiliaryExecute { /// call posix spawn to begin execute /// - Parameters: /// - command: full path of the binary file. eg: "/bin/cat" /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - output: a block call from pipeControlQueue in background when buffer from stdout or stderr available for read /// - Returns: execution recipe, see it's definition for details @discardableResult static func spawn( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, setPid: ((pid_t) -> Void)? = nil, output: ((String) -> Void)? = nil ) -> ExecuteRecipe { let outputLock = NSLock() let result = spawn( command: command, args: args, environment: environment, timeout: timeout, setPid: setPid ) { str in outputLock.lock() output?(str) outputLock.unlock() } stderrBlock: { str in outputLock.lock() output?(str) outputLock.unlock() } return result } /// call posix spawn to begin execute and block until the process exits /// - Parameters: /// - command: full path of the binary file. eg: "/bin/cat" /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read /// - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read /// - Returns: execution recipe, see it's definition for details static func spawn( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, setPid: ((pid_t) -> Void)? = nil, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil ) -> ExecuteRecipe { let sema = DispatchSemaphore(value: 0) var recipe: ExecuteRecipe! spawn( command: command, args: args, environment: environment, timeout: timeout, setPid: setPid, stdoutBlock: stdoutBlock, stderrBlock: stderrBlock ) { recipe = $0 sema.signal() } sema.wait() return recipe } /// call posix spawn to begin execute /// - Parameters: /// - command: full path of the binary file. eg: "/bin/cat" /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"] /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"] /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6 /// - setPid: called sync when pid available /// - stdoutBlock: a block call from pipeControlQueue in background when buffer from stdout available for read /// - stderrBlock: a block call from pipeControlQueue in background when buffer from stderr available for read /// - completionBlock: a block called from processControlQueue or current queue when the process is finished or an error occurred static func spawn( command: String, args: [String] = [], environment: [String: String] = [:], timeout: Double = 0, setPid: ((pid_t) -> Void)? = nil, stdoutBlock: ((String) -> Void)? = nil, stderrBlock: ((String) -> Void)? = nil, completionBlock: ((ExecuteRecipe) -> Void)? = nil ) { // MARK: PREPARE FILE PIPE - var pipestdout: [Int32] = [0, 0] var pipestderr: [Int32] = [0, 0] let bufsiz = Int(exactly: BUFSIZ) ?? 65535 pipe(&pipestdout) pipe(&pipestderr) guard fcntl(pipestdout[0], F_SETFL, O_NONBLOCK) != -1 else { let recipe = ExecuteRecipe.failure(error: .openFilePipeFailed) completionBlock?(recipe) return } guard fcntl(pipestderr[0], F_SETFL, O_NONBLOCK) != -1 else { let recipe = ExecuteRecipe.failure(error: .openFilePipeFailed) completionBlock?(recipe) return } // MARK: PREPARE FILE ACTION - var fileActions: posix_spawn_file_actions_t? posix_spawn_file_actions_init(&fileActions) posix_spawn_file_actions_addclose(&fileActions, pipestdout[0]) posix_spawn_file_actions_addclose(&fileActions, pipestderr[0]) posix_spawn_file_actions_adddup2(&fileActions, pipestdout[1], STDOUT_FILENO) posix_spawn_file_actions_adddup2(&fileActions, pipestderr[1], STDERR_FILENO) posix_spawn_file_actions_addclose(&fileActions, pipestdout[1]) posix_spawn_file_actions_addclose(&fileActions, pipestderr[1]) defer { posix_spawn_file_actions_destroy(&fileActions) } // MARK: PREPARE ENV - var realEnvironmentBuilder: [String] = [] // before building the environment, we need to read from the existing environment do { var envBuilder = [String: String]() var currentEnv = environ while let rawStr = currentEnv.pointee { defer { currentEnv += 1 } // get the env let str = String(cString: rawStr) guard let key = str.components(separatedBy: "=").first else { continue } if !(str.count >= "\(key)=".count) { continue } // this is to aviod any problem with mua=nya=nya= that ending with = let value = String(str.dropFirst("\(key)=".count)) envBuilder[key] = value } // now, let's overwrite the environment specified in parameters for (key, value) in environment { envBuilder[key] = value } // now, package those items for (key, value) in envBuilder { realEnvironmentBuilder.append("\(key)=\(value)") } } // making it a c shit let realEnv: [UnsafeMutablePointer?] = realEnvironmentBuilder.map { $0.withCString(strdup) } defer { for case let env? in realEnv { free(env) } } // MARK: PREPARE ARGS - let args = [command] + args let argv: [UnsafeMutablePointer?] = args.map { $0.withCString(strdup) } defer { for case let arg? in argv { free(arg) } } // MARK: NOW POSIX_SPAWN - var pid: pid_t = 0 let spawnStatus = posix_spawn(&pid, command, &fileActions, nil, argv + [nil], realEnv + [nil]) if spawnStatus != 0 { let recipe = ExecuteRecipe.failure(error: .posixSpawnFailed) completionBlock?(recipe) return } setPid?(pid) close(pipestdout[1]) close(pipestderr[1]) var stdoutStr = "" var stderrStr = "" // MARK: OUTPUT BRIDGE - let stdoutSource = DispatchSource.makeReadSource(fileDescriptor: pipestdout[0], queue: pipeControlQueue) let stderrSource = DispatchSource.makeReadSource(fileDescriptor: pipestderr[0], queue: pipeControlQueue) let stdoutSem = DispatchSemaphore(value: 0) let stderrSem = DispatchSemaphore(value: 0) stdoutSource.setCancelHandler { close(pipestdout[0]) stdoutSem.signal() } stderrSource.setCancelHandler { close(pipestderr[0]) stderrSem.signal() } stdoutSource.setEventHandler { let buffer = UnsafeMutablePointer.allocate(capacity: bufsiz) defer { buffer.deallocate() } let bytesRead = read(pipestdout[0], buffer, bufsiz) guard bytesRead > 0 else { if bytesRead == -1, errno == EAGAIN { return } stdoutSource.cancel() return } let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)] array.withUnsafeBufferPointer { ptr in let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer.self)) stdoutStr += str stdoutBlock?(str) } } stderrSource.setEventHandler { let buffer = UnsafeMutablePointer.allocate(capacity: bufsiz) defer { buffer.deallocate() } let bytesRead = read(pipestderr[0], buffer, bufsiz) guard bytesRead > 0 else { if bytesRead == -1, errno == EAGAIN { return } stderrSource.cancel() return } let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)] array.withUnsafeBufferPointer { ptr in let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer.self)) stderrStr += str stderrBlock?(str) } } stdoutSource.resume() stderrSource.resume() // MARK: WAIT + TIMEOUT CONTROL - let realTimeout = timeout > 0 ? timeout : maxTimeoutValue let wallTimeout = DispatchTime.now() + ( TimeInterval(exactly: realTimeout) ?? maxTimeoutValue ) var status: Int32 = 0 var wait: pid_t = 0 var isTimeout = false let timerSource = DispatchSource.makeTimerSource(flags: [], queue: processControlQueue) timerSource.setEventHandler { isTimeout = true kill(pid, SIGKILL) } let processSource = DispatchSource.makeProcessSource(identifier: pid, eventMask: .exit, queue: processControlQueue) processSource.setEventHandler { wait = waitpid(pid, &status, 0) processSource.cancel() timerSource.cancel() stdoutSem.wait() stderrSem.wait() // by using exactly method, we won't crash it! let recipe = ExecuteRecipe( exitCode: Int(exactly: status) ?? -1, pid: Int(exactly: pid) ?? -1, wait: Int(exactly: wait) ?? -1, error: isTimeout ? .timeout : nil, stdout: stdoutStr, stderr: stderrStr ) completionBlock?(recipe) } processSource.resume() // timeout control timerSource.schedule(deadline: wallTimeout) timerSource.resume() } } ================================================ FILE: External/AuxiliaryExecute/Sources/AuxiliaryExecute/AuxiliaryExecute.swift ================================================ // // AuxiliaryExecute.swift // MyYearWithGit // // Created by Lakr Aream on 2021/11/27. // import Foundation /// Execute command or shell with posix, shared with AuxiliaryExecute.local public class AuxiliaryExecute { /// we do not recommend you to subclass this singleton public static let local = AuxiliaryExecute() // if binary not found when you call the shell api // we will take some time to rebuild the bianry table each time // -->>> this is a time-heavy-task // so use binaryLocationFor(command:) to cache it if needed // system path internal var currentPath: [String] = [] // system binary table internal var binaryTable: [String: String] = [:] // for you to put your own search path internal var extraSearchPath: [String] = [] // for you to set your own binary table and will be used firstly // if you set nil here // -> we will return nil even the binary found in system path internal var overwriteTable: [String: String?] = [:] // this value is used when providing 0 or negative timeout paramete internal static let maxTimeoutValue: Double = 2_147_483_647 /// when reading from file pipe, must called from async queue internal static let pipeControlQueue = DispatchQueue( label: "wiki.qaq.AuxiliaryExecute.pipeRead", attributes: .concurrent ) /// when killing process or monitoring events from process, must called from async queue /// we are making this queue serial queue so won't called at the same time when timeout internal static let processControlQueue = DispatchQueue( label: "wiki.qaq.AuxiliaryExecute.processControl", attributes: [] ) /// used for setting binary table, avoid crash internal let lock = NSLock() /// nope! private init() { // no need to setup binary table // we will make call to it when you call the shell api // if you only use the spawn api // we don't need to setup the hole table cause it‘s time-heavy-task } /// Execution Error, do the localization your self public enum ExecuteError: Error, LocalizedError, Codable { // not found in path case commandNotFound // invalid, may be missing, wrong permission or any other reason case commandInvalid // fcntl failed case openFilePipeFailed // posix failed case posixSpawnFailed // waitpid failed case waitPidFailed // timeout when execute case timeout } /// Execution Recipe public struct ExecuteRecipe: Codable { // exit code, usually 0 - 255 by system // -1 means something bad happened, set by us for convince public let exitCode: Int // process pid that was when it is alive // -1 means spawn failed in some situation public let pid: Int // wait result for final waitpid inside block at // processSource - eventMask.exit, usually is pid // -1 for other cases public let wait: Int // any error from us, not the command it self // DOES NOT MEAN THAT THE COMMAND DONE WELL public let error: ExecuteError? // stdout public let stdout: String // stderr public let stderr: String /// General initialization of recipe object /// - Parameters: /// - exitCode: code when process exit /// - pid: pid when process alive /// - wait: wait result on waitpid /// - error: error if any /// - stdout: stdout /// - stderr: stderr internal init( exitCode: Int, pid: Int, wait: Int, error: AuxiliaryExecute.ExecuteError?, stdout: String, stderr: String ) { self.exitCode = exitCode self.pid = pid self.wait = wait self.error = error self.stdout = stdout self.stderr = stderr } /// Template for making failure recipe /// - Parameters: /// - exitCode: default -1 /// - pid: default -1 /// - wait: default -1 /// - error: error /// - stdout: default empty /// - stderr: default empty internal static func failure( exitCode: Int = -1, pid: Int = -1, wait: Int = -1, error: AuxiliaryExecute.ExecuteError?, stdout: String = "", stderr: String = "" ) -> ExecuteRecipe { .init( exitCode: exitCode, pid: pid, wait: wait, error: error, stdout: stdout, stderr: stderr ) } } } ================================================ FILE: External/AuxiliaryExecute/Tests/AuxiliaryExecuteTests/AuxiliaryExecuteTests.swift ================================================ @testable import AuxiliaryExecute import XCTest final class AuxiliaryExecuteTests: XCTestCase { func testMain() throws { XCTAssertNotNil(Int(exactly: AuxiliaryExecute.maxTimeoutValue)) XCTAssertNotNil(Int32(exactly: AuxiliaryExecute.maxTimeoutValue)) XCTAssertNotNil(Double(exactly: AuxiliaryExecute.maxTimeoutValue)) XCTAssertNotNil(TimeInterval(exactly: AuxiliaryExecute.maxTimeoutValue)) do { let result = AuxiliaryExecute.local.bash(command: "printf \"\nnya\n\"") print(result) XCTAssert(result.exitCode == 0) XCTAssertNil(result.error) XCTAssert(result.stdout == "\nnya\n") XCTAssert(result.stderr == "") } do { let result = AuxiliaryExecute.local.shell( command: "bash", args: ["-c", "echo $mua"], environment: ["mua": "nya"], timeout: 0 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } XCTAssert(result.exitCode == 0) XCTAssertNil(result.error) XCTAssert(result.stdout.trimmingCharacters(in: .whitespacesAndNewlines) == "nya") XCTAssert(result.stderr == "") } do { let result = AuxiliaryExecute.local.shell( command: "bash", args: ["-c", "echo $mua"], environment: ["mua": "nya=nya="], timeout: 0 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } XCTAssert(result.exitCode == 0) XCTAssertNil(result.error) XCTAssert(result.stdout.trimmingCharacters(in: .whitespacesAndNewlines) == "nya=nya=") XCTAssert(result.stderr == "") } do { let result = AuxiliaryExecute.local.shell( command: "tail", args: ["-f", "/dev/null"], timeout: 1 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } XCTAssert(result.exitCode == SIGKILL) XCTAssert(result.error == .timeout) } } @available(macOS 12.0.0, *) func testAsync() async throws { do { let result = await AuxiliaryExecute.spawnAsync( command: "/usr/bin/uname", args: ["-a"], timeout: 1 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } XCTAssertEqual(result.exitCode, 0) XCTAssert(result.stdout.contains("Darwin Kernel")) } do { let result = await AuxiliaryExecute.spawnAsync( command: "/usr/bin/tail", args: ["-f", "/dev/null"], timeout: 1 ) { stdout in print(stdout) } stderrBlock: { stderr in print(stderr) } XCTAssertEqual(result.exitCode, Int(SIGKILL)) XCTAssertEqual(result.error, .timeout) } } } ================================================ FILE: External/Colorful/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata ================================================ FILE: External/Colorful/LICENSE ================================================ MIT License Copyright (c) 2021 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: External/Colorful/Package.swift ================================================ // swift-tools-version:5.3 import PackageDescription let package = Package( name: "Colorful", platforms: [ .iOS(.v13), .tvOS(.v13), .macOS(.v10_15), .watchOS(.v6) ], products: [ .library(name: "Colorful", targets: ["Colorful"]), ], targets: [ .target(name: "Colorful", dependencies: []), ] ) ================================================ FILE: External/Colorful/README.md ================================================ # Colorful A SwiftUI implementation of AppleCard's animated colorful blur background. ## Preview ![Preview](./Preview.png) ## Usage ``` import Colorful var body: some View { ColorfulView() } ``` ## Customization & Defaults ``` init( animated: Bool = defaultAnimated, animation: Animation = defaultAnimation, blurRadius: CGFloat = defaultBlurRadius, colors: [Color] = defaultColorList, colorCount: Int = defaultColorCount ) ``` ## License Colorful is licensed under [MIT](./LICENSE). --- Copyright © 2021 Lakr Aream. All Rights Reserved. ================================================ FILE: External/Colorful/Sources/Colorful/Colorful.swift ================================================ // // Colorful.swift // // // Created by Lakr Aream on 2021/9/19. // import SwiftUI private let kDefaultSourceColorList = [#colorLiteral(red: 0.9586862922, green: 0.660125792, blue: 0.8447988033, alpha: 1), #colorLiteral(red: 0.8714533448, green: 0.723166883, blue: 0.9342088699, alpha: 1), #colorLiteral(red: 0.7458761334, green: 0.7851135731, blue: 0.9899476171, alpha: 1), #colorLiteral(red: 0.4398113191, green: 0.8953480721, blue: 0.9796616435, alpha: 1), #colorLiteral(red: 0.3484552801, green: 0.933657825, blue: 0.9058339596, alpha: 1), #colorLiteral(red: 0.5567936897, green: 0.9780793786, blue: 0.6893508434, alpha: 1)] public extension ColorfulView { static let defaultAnimated: Bool = true static let defaultBlurRadius: CGFloat = 64 static let defaultColorCount: Int = 32 static let defaultAnimation: Animation = Animation .interpolatingSpring(stiffness: 50, damping: 1) .speed(0.05) static let defaultColorList: [Color] = kDefaultSourceColorList .map { Color($0) } #if canImport(AppKit) && !targetEnvironment(macCatalyst) static let defaultColorListNSColor: [NSColor] = kDefaultSourceColorList #endif #if canImport(UIKit) static let defaultColorListUIColor: [UIColor] = kDefaultSourceColorList #endif } ================================================ FILE: External/Colorful/Sources/Colorful/ColorfulView.swift ================================================ // // ColorfulView.swift // Colorful // // Created by Lakr Aream on 2021/9/19. // import SwiftUI public struct ColorfulView: View { // MARK: - PROPERTY @State var randomization: [PointRandomization] @State var size: CGSize = .init() private let colorElements: [Color] private let animated: Bool private let animation: Animation private let blurRadius: CGFloat private let timer = Timer .publish(every: 5, on: .main, in: .common) .autoconnect() // MARK: - INIT public init( animated: Bool = defaultAnimated, animation: Animation = defaultAnimation, blurRadius: CGFloat = defaultBlurRadius, colors: [Color] = defaultColorList, colorCount: Int = defaultColorCount ) { assert(colors.count > 0) assert(colorCount > 0) assert(blurRadius > 0) self.animated = animated self.animation = animation self.blurRadius = blurRadius var colorCompiler = [Color]() while colorCompiler.count < colorCount { colorCompiler.append(contentsOf: colors.shuffled()) } if colorCompiler.count > colorCount { colorCompiler.removeLast(colorCompiler.count - colorCount) } assert(colorCompiler.count == colorCount) colorElements = colorCompiler var builder = [PointRandomization]() for _ in 0 ..< colorCount { builder.append(.init()) } _randomization = State(initialValue: builder) } #if canImport(AppKit) && !targetEnvironment(macCatalyst) public init( animated: Bool = defaultAnimated, animation: Animation = defaultAnimation, blurRadius: CGFloat = defaultBlurRadius, nsColors: [NSColor], colorCount: Int = defaultColorCount ) { self.init( animated: animated, animation: animation, blurRadius: blurRadius, colors: nsColors.map { Color($0) }, colorCount: colorCount ) } #endif #if canImport(UIKit) public init( animated: Bool = defaultAnimated, animation: Animation = defaultAnimation, blurRadius: CGFloat = defaultBlurRadius, uiColors: [UIColor], colorCount: Int = defaultColorCount ) { self.init( animated: animated, animation: animation, blurRadius: blurRadius, colors: uiColors.map { Color($0) }, colorCount: colorCount ) } #endif // MARK: - VIEW public var body: some View { GeometryReader { reader in ZStack { ForEach(obtainRangeAndUpdate(size: reader.size)) { configure in Circle() .foregroundColor(configure.color) .opacity(0.5) .frame( width: configure.diameter, height: configure.diameter ) .offset( x: configure.offsetX, y: configure.offsetY ) } } .frame(width: reader.size.width, height: reader.size.height) } .clipped() .blur(radius: blurRadius) .onReceive(timer) { _ in dispatchUpdate() } .onAppear { randomizationStart() } } // MARK: - FUNCTION private func dispatchUpdate() { if !animated { return } withAnimation(animation) { randomizationStart() } } private func randomizationStart() { var randomizationBuilder = [PointRandomization]() for i in 0 ..< randomization.count { let randomizationElement: PointRandomization = { var builder = PointRandomization() builder.randomizeIn(size: size) builder.id = randomization[i].id builder.color = colorElements[i] return builder }() randomizationBuilder.append(randomizationElement) } randomization = randomizationBuilder } private func obtainRangeAndUpdate(size: CGSize) -> [PointRandomization] { issueSizeUpdate(withValue: size) return randomization } private func issueSizeUpdate(withValue size: CGSize) { if self.size == size { return } DispatchQueue.main.async { self.size = size self.dispatchUpdate() } } } ================================================ FILE: External/Colorful/Sources/Colorful/PointRandomization.swift ================================================ // // PointRandomization.swift // Colorful // // Created by Lakr Aream on 2021/9/19. // import SwiftUI extension ColorfulView { struct PointRandomization: Equatable, Hashable, Identifiable { var id = UUID() var diameter: CGFloat = 0 var offsetX: CGFloat = 0 var offsetY: CGFloat = 0 var color: Color = .white.opacity(0) mutating func randomizeIn(size: CGSize) { let decision = (size.width + size.height) / 4 diameter = CGFloat.random(in: (decision * 0.25) ... (decision * 0.75)) offsetX = CGFloat.random(in: -(size.width / 2) ... +(size.width / 2)) offsetY = CGFloat.random(in: -(size.height / 2) ... +(size.height / 2)) } func hash(into hasher: inout Hasher) { hasher.combine(diameter) hasher.combine(offsetX) hasher.combine(offsetY) } static func == (lhs: PointRandomization, rhs: PointRandomization) -> Bool { lhs.diameter == rhs.diameter && lhs.offsetX == rhs.offsetX && lhs.offsetY == rhs.offsetY } } } ================================================ FILE: External/KeychainAccess/.gitignore ================================================ ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Swift.gitignore # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Global/macOS.gitignore # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### https://raw.github.com/github/gitignore/2a4de265d37eca626309d8e115218d18985b5435/Ruby.gitignore *.gem *.rbc /.config /coverage/ /InstalledFiles /pkg/ /spec/reports/ /spec/examples.txt /test/tmp/ /test/version_tmp/ /tmp/ # Used by dotenv library to load environment variables. # .env # Ignore Byebug command history file. .byebug_history ## Specific to RubyMotion: .dat* .repl_history build/ *.bridgesupport build-iPhoneOS/ build-iPhoneSimulator/ ## Specific to RubyMotion (use of CocoaPods): # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # vendor/Pods/ ## Documentation cache and generated files: /.yardoc/ /_yardoc/ /doc/ /rdoc/ ## Environment normalization: /.bundle/ /vendor/bundle /lib/bundler/man/ # for a library or gem, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # Gemfile.lock # .ruby-version # .ruby-gemset # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: .rvmrc # Used by RuboCop. Remote config files pulled in from inherit_from directive. # .rubocop-https?--* ================================================ FILE: External/KeychainAccess/.travis.yml ================================================ language: objective-c cache: directories: - Lib/vendor install: - "(cd Lib && bundle install --path=vendor/bundle --binstubs=vendor/bin)" before_script: - ./Lib/Scripts/decode_cert.sh || true - ./Lib/Scripts/add_key.sh || true script: - "(cd Lib && travis_retry bundle exec rake $ACTION)" matrix: include: - osx_image: xcode11 env: ACTION=build - osx_image: xcode11 env: ACTION='build:carthage' - osx_image: xcode11 env: ACTION='test:iphonesimulator' - osx_image: xcode11 env: ACTION='test:appletvsimulator' - osx_image: xcode11 env: ACTION='test:macosx' # Mac Catalyst requires macOS Catalina. But no CI service supports running tests on Catalina yet. # - osx_image: xcode11 # script: xcodebuild test -workspace KeychainAccess.xcworkspace -scheme KeychainAccess -configuration Debug -enableCodeCoverage YES -derivedDataPath build -hideShellScriptEnvironment -destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst' -only-testing:KeychainAccessTests-MacCatalyst SWIFT_VERSION=5.1 GCC_SYMBOLS_PRIVATE_EXTERN=NO - osx_image: xcode10.3 env: ACTION=build - osx_image: xcode10.3 env: ACTION='build:carthage' - osx_image: xcode10.3 env: ACTION='test:iphonesimulator' - osx_image: xcode10.3 env: ACTION='test:appletvsimulator' - osx_image: xcode10.3 env: ACTION='test:macosx' - osx_image: xcode10.2 env: ACTION=build - osx_image: xcode10.2 env: ACTION='build:carthage' - osx_image: xcode10.2 env: ACTION='test:iphonesimulator' - osx_image: xcode10.2 env: ACTION='test:appletvsimulator' - osx_image: xcode10.2 env: ACTION='test:macosx' - osx_image: xcode10 env: ACTION=build - osx_image: xcode10 env: ACTION='build:carthage' - osx_image: xcode10 env: ACTION='test:iphonesimulator' - osx_image: xcode10 env: ACTION='test:appletvsimulator' - osx_image: xcode10 env: ACTION='test:macosx' - osx_image: xcode9.4 env: ACTION=build - osx_image: xcode9.4 env: ACTION='build:carthage' - osx_image: xcode9.4 env: ACTION='test:iphonesimulator' - osx_image: xcode9.4 env: ACTION='test:appletvsimulator' - osx_image: xcode9.4 env: ACTION='test:macosx' env: global: - LANG=en_US.UTF-8 - LC_ALL=en_US.UTF-8 - secure: KL59HA2XSRa215qVXXnFpx48Tb/k+vNfFAbzyc+0M4mXl1VhSJv3bkTslqDrX822t3iFDFYhXbJ6aUo7szSdcbnCXU7VIhNPRK5QM00eS1AI4V0UwDQZ06g2f4Dmt+cQxACg+0CB0OaPnUCA4rkQQKeBMAWa67Tp2hNgQHwsnio= - secure: B8zXiyX1zEq6uWaCxap5iW1joQBoOjNjSUlcs1t+QKdaFCFtjOI8C1JCClzk7rTnGNDDrhahFlE8yskSNKoBsaW2UJ8TzPIkD4F2pkxTHkaFQl/GBsdbHdOUFL4h0/zGQ6wY1Qhw7C+8+3U+1c9QbnNw6jOQwXTF6gs/XTNzG9Y= branches: only: - master ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/AccountsViewController.swift ================================================ // // AccountsViewController.swift // Example // // Created by kishikawa katsumi on 2014/12/25. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit import KeychainAccess class AccountsViewController: UITableViewController { var itemsGroupedByService: [String: [[String: Any]]]? override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) reloadData() tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // MARK: override func numberOfSections(in tableView: UITableView) -> Int { if itemsGroupedByService != nil { let services = Array(itemsGroupedByService!.keys) return services.count } return 0 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let services = Array(itemsGroupedByService!.keys) let service = services[section] let items = Keychain(service: service).allItems() return items.count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let services = Array(itemsGroupedByService!.keys) return services[section] } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let items = Keychain(service: service).allItems() let item = items[indexPath.row] cell.textLabel?.text = item["key"] as? String cell.detailTextLabel?.text = item["value"] as? String return cell } #if swift(>=4.2) override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let keychain = Keychain(service: service) let items = keychain.allItems() let item = items[indexPath.row] let key = item["key"] as! String keychain[key] = nil if items.count == 1 { reloadData() tableView.deleteSections(IndexSet(integer: indexPath.section), with: .automatic) } else { tableView.deleteRows(at: [indexPath], with: .automatic) } } #else override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { let services = Array(itemsGroupedByService!.keys) let service = services[indexPath.section] let keychain = Keychain(service: service) let items = keychain.allItems() let item = items[indexPath.row] let key = item["key"] as! String keychain[key] = nil if items.count == 1 { reloadData() tableView.deleteSections(IndexSet(integer: indexPath.section), with: .automatic) } else { tableView.deleteRows(at: [indexPath], with: .automatic) } } #endif // MARK: func reloadData() { let items = Keychain.allItems(.genericPassword) itemsGroupedByService = groupBy(items) { item -> String in if let service = item["service"] as? String { return service } return "" } } } private func groupBy(_ xs: C, key: (C.Iterator.Element) -> K) -> [K:[C.Iterator.Element]] { var gs: [K:[C.Iterator.Element]] = [:] for x in xs { let k = key(x) var ys = gs[k] ?? [] ys.append(x) gs.updateValue(ys, forKey: k) } return gs } ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/AppDelegate.swift ================================================ // // AppDelegate.swift // Example // // Created by kishikawa katsumi on 2014/12/25. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? #if swift(>=4.2) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true } #else func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { return true } #endif } ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/Base.lproj/LaunchScreen.xib ================================================ ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/Base.lproj/Main.storyboard ================================================ ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/Example-iOS.entitlements ================================================ keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.Example-iOS ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS/InputViewController.swift ================================================ // // InputViewController.swift // Example // // Created by kishikawa katsumi on 2014/12/26. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit import KeychainAccess class InputViewController: UITableViewController { @IBOutlet weak var saveButton: UIBarButtonItem! @IBOutlet weak var cancelButton: UIBarButtonItem! @IBOutlet weak var usernameField: UITextField! @IBOutlet weak var passwordField: UITextField! @IBOutlet weak var serviceField: UITextField! override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = 44.0 tableView.estimatedRowHeight = 44.0 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // MARK: @IBAction func cancelAction(sender: UIBarButtonItem) { dismiss(animated: true, completion: nil) } @IBAction func saveAction(sender: UIBarButtonItem) { let keychain: Keychain if let service = serviceField.text, !service.isEmpty { keychain = Keychain(service: service) } else { keychain = Keychain() } keychain[usernameField.text!] = passwordField.text dismiss(animated: true, completion: nil) } @IBAction func editingChanged(sender: UITextField) { switch (usernameField.text, passwordField.text) { case let (username?, password?): saveButton.isEnabled = !username.isEmpty && !password.isEmpty case (_?, nil): saveButton.isEnabled = false case (nil, _?): saveButton.isEnabled = false case (nil, nil): saveButton.isEnabled = false } } } ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 14DAEE961A51E1BE0070B77E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */; }; 14DAEE9B1A51E1BE0070B77E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE991A51E1BE0070B77E /* Main.storyboard */; }; 14DAEE9D1A51E1BE0070B77E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */; }; 14DAEEA01A51E1BE0070B77E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */; }; 14DAEEB71A51E2690070B77E /* AccountsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */; }; 14DAEEB81A51E2690070B77E /* InputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14DAEEB61A51E2690070B77E /* InputViewController.swift */; }; 14DAEEC91A51E2D00070B77E /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */; }; 14DAEECB1A51E2E10070B77E /* KeychainAccess.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 1470425D1D6FFA97005A4C6E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 14A630151D3293C700809B3F; remoteInfo = TestHost; }; 14DAEEC01A51E2A60070B77E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 140F195C1A49D79400B0016A; remoteInfo = "KeychainAccess-iOS"; }; 14DAEEC21A51E2A60070B77E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; proxyType = 2; remoteGlobalIDString = 140F19671A49D79500B0016A; remoteInfo = "KeychainAccess-iOSTests"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14DAEECA1A51E2D70070B77E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14DAEECB1A51E2E10070B77E /* KeychainAccess.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 145652E11D898BB9006E8D0E /* Example-iOS.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Example-iOS.entitlements"; sourceTree = ""; }; 14DAEE901A51E1BE0070B77E /* Example-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Example-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEE941A51E1BE0070B77E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14DAEE9A1A51E1BE0070B77E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 14DAEE9F1A51E1BE0070B77E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsViewController.swift; sourceTree = ""; }; 14DAEEB61A51E2690070B77E /* InputViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputViewController.swift; sourceTree = ""; }; 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = KeychainAccess.xcodeproj; path = ../../Lib/KeychainAccess.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 14DAEE8D1A51E1BE0070B77E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14DAEEC91A51E2D00070B77E /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 14DAEE871A51E1BE0070B77E = { isa = PBXGroup; children = ( 14DAEE921A51E1BE0070B77E /* Example-iOS */, 14DAEE911A51E1BE0070B77E /* Products */, 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */, ); sourceTree = ""; }; 14DAEE911A51E1BE0070B77E /* Products */ = { isa = PBXGroup; children = ( 14DAEE901A51E1BE0070B77E /* Example-iOS.app */, ); name = Products; sourceTree = ""; }; 14DAEE921A51E1BE0070B77E /* Example-iOS */ = { isa = PBXGroup; children = ( 14DAEE951A51E1BE0070B77E /* AppDelegate.swift */, 14DAEEB51A51E2690070B77E /* AccountsViewController.swift */, 14DAEEB61A51E2690070B77E /* InputViewController.swift */, 14DAEE991A51E1BE0070B77E /* Main.storyboard */, 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */, 14DAEE9C1A51E1BE0070B77E /* Images.xcassets */, 14DAEE931A51E1BE0070B77E /* Supporting Files */, ); path = "Example-iOS"; sourceTree = ""; }; 14DAEE931A51E1BE0070B77E /* Supporting Files */ = { isa = PBXGroup; children = ( 14DAEE941A51E1BE0070B77E /* Info.plist */, 145652E11D898BB9006E8D0E /* Example-iOS.entitlements */, ); name = "Supporting Files"; sourceTree = ""; }; 14DAEEBA1A51E2A60070B77E /* Products */ = { isa = PBXGroup; children = ( 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */, 14DAEEC31A51E2A60070B77E /* KeychainAccessTests.xctest */, 1470425E1D6FFA97005A4C6E /* TestHost.app */, ); name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 14DAEE8F1A51E1BE0070B77E /* Example-iOS */ = { isa = PBXNativeTarget; buildConfigurationList = 14DAEEAF1A51E1BE0070B77E /* Build configuration list for PBXNativeTarget "Example-iOS" */; buildPhases = ( 14DAEE8C1A51E1BE0070B77E /* Sources */, 14DAEE8D1A51E1BE0070B77E /* Frameworks */, 14DAEE8E1A51E1BE0070B77E /* Resources */, 14DAEECA1A51E2D70070B77E /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = "Example-iOS"; productName = "Example-iOS"; productReference = 14DAEE901A51E1BE0070B77E /* Example-iOS.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 14DAEE881A51E1BE0070B77E /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "kishikawa katsumi"; TargetAttributes = { 14DAEE8F1A51E1BE0070B77E = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 14DAEE8B1A51E1BE0070B77E /* Build configuration list for PBXProject "Example-iOS" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 14DAEE871A51E1BE0070B77E; productRefGroup = 14DAEE911A51E1BE0070B77E /* Products */; projectDirPath = ""; projectReferences = ( { ProductGroup = 14DAEEBA1A51E2A60070B77E /* Products */; ProjectRef = 14DAEEB91A51E2A60070B77E /* KeychainAccess.xcodeproj */; }, ); projectRoot = ""; targets = ( 14DAEE8F1A51E1BE0070B77E /* Example-iOS */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ 1470425E1D6FFA97005A4C6E /* TestHost.app */ = { isa = PBXReferenceProxy; fileType = wrapper.application; path = TestHost.app; remoteRef = 1470425D1D6FFA97005A4C6E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEEC11A51E2A60070B77E /* KeychainAccess.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = KeychainAccess.framework; remoteRef = 14DAEEC01A51E2A60070B77E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 14DAEEC31A51E2A60070B77E /* KeychainAccessTests.xctest */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; path = KeychainAccessTests.xctest; remoteRef = 14DAEEC21A51E2A60070B77E /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ 14DAEE8E1A51E1BE0070B77E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14DAEE9B1A51E1BE0070B77E /* Main.storyboard in Resources */, 14DAEEA01A51E1BE0070B77E /* LaunchScreen.xib in Resources */, 14DAEE9D1A51E1BE0070B77E /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 14DAEE8C1A51E1BE0070B77E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14DAEEB81A51E2690070B77E /* InputViewController.swift in Sources */, 14DAEEB71A51E2690070B77E /* AccountsViewController.swift in Sources */, 14DAEE961A51E1BE0070B77E /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 14DAEE991A51E1BE0070B77E /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 14DAEE9A1A51E1BE0070B77E /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 14DAEE9E1A51E1BE0070B77E /* LaunchScreen.xib */ = { isa = PBXVariantGroup; children = ( 14DAEE9F1A51E1BE0070B77E /* Base */, ); name = LaunchScreen.xib; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 14DAEEAD1A51E1BE0070B77E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = "Example-iOS/Example-iOS.entitlements"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.1; }; name = Debug; }; 14DAEEAE1A51E1BE0070B77E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = "Example-iOS/Example-iOS.entitlements"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.1; VALIDATE_PRODUCT = YES; }; name = Release; }; 14DAEEB01A51E1BE0070B77E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "Example-iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 14DAEEB11A51E1BE0070B77E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "Example-iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 14DAEE8B1A51E1BE0070B77E /* Build configuration list for PBXProject "Example-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 14DAEEAD1A51E1BE0070B77E /* Debug */, 14DAEEAE1A51E1BE0070B77E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14DAEEAF1A51E1BE0070B77E /* Build configuration list for PBXNativeTarget "Example-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 14DAEEB01A51E1BE0070B77E /* Debug */, 14DAEEB11A51E1BE0070B77E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 14DAEE881A51E1BE0070B77E /* Project object */; } ================================================ FILE: External/KeychainAccess/Examples/Example-iOS/Example-iOS.xcodeproj/xcshareddata/xcschemes/Example-iOS.xcscheme ================================================ ================================================ FILE: External/KeychainAccess/KeychainAccess.podspec ================================================ Pod::Spec.new do |s| s.name = 'KeychainAccess' s.version = '4.2.2' s.summary = 'KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and OS X.' s.description = <<-DESC KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and OS X. Makes using Keychain APIs exremely easy and much more palatable to use in Swift. Features - Simple interface - Support access group - Support accessibility - Support iCloud sharing - **Support TouchID and Keychain integration (iOS 8+)** - **Support Shared Web Credentials (iOS 8+)** - Works on both iOS & OS X - watchOS and tvOS are also supported DESC s.homepage = 'https://github.com/kishikawakatsumi/KeychainAccess' s.screenshots = 'https://raw.githubusercontent.com/kishikawakatsumi/KeychainAccess/master/Screenshots/01.png' s.license = 'MIT' s.author = { 'kishikawa katsumi' => 'kishikawakatsumi@mac.com' } s.source = { :git => 'https://github.com/kishikawakatsumi/KeychainAccess.git', :tag => "v#{s.version}" } s.social_media_url = 'https://twitter.com/k_katsumi' s.requires_arc = true s.source_files = 'Lib/KeychainAccess/*.swift' s.swift_version = '5.1' s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.9' s.watchos.deployment_target = '3.0' s.tvos.deployment_target = '9.0' end ================================================ FILE: External/KeychainAccess/KeychainAccess.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: External/KeychainAccess/KeychainAccess.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: External/KeychainAccess/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 kishikawa katsumi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: External/KeychainAccess/Lib/Certificates/KeychainAccess_Tests.provisionprofile.enc ================================================ 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 m1nn1kcLs46WQmS7a7XSHQ== ================================================ FILE: External/KeychainAccess/Lib/Certificates/developer_id_app.p12.enc ================================================ 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 ================================================ FILE: External/KeychainAccess/Lib/Certificates/iOS_Development.mobileprovision.enc ================================================ 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 ================================================ FILE: External/KeychainAccess/Lib/Certificates/ios_developer.p12.enc ================================================ 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 hfJJH1uvIO2vkPfr+fxvTQ== ================================================ FILE: External/KeychainAccess/Lib/Certificates/tvOS_Development.mobileprovision.enc ================================================ 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 ================================================ FILE: External/KeychainAccess/Lib/Configurations/Base.xcconfig ================================================ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES CLANG_CXX_LANGUAGE_STANDARD = gnu++0x; CLANG_CXX_LIBRARY = libc++; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_DYNAMIC_NO_PIC = NO; GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; CODE_SIGN_IDENTITY = ; DEVELOPMENT_TEAM = ; IPHONEOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 3.0; TVOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; SWIFT_VERSION = 5.0; ================================================ FILE: External/KeychainAccess/Lib/Configurations/Debug.xcconfig ================================================ #include "Base.xcconfig" BITCODE_GENERATION_MODE = marker; MTL_ENABLE_DEBUG_INFO = YES; COPY_PHASE_STRIP = NO; ENABLE_TESTABILITY = YES; GCC_OPTIMIZATION_LEVEL = 0; ONLY_ACTIVE_ARCH = YES; SWIFT_OPTIMIZATION_LEVEL = -Onone; GCC_PREPROCESSOR_DEFINITIONS = $(inherited) DEBUG=1; ================================================ FILE: External/KeychainAccess/Lib/Configurations/KeychainAccess.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator watchos watchsimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2,3,4; SUPPORTS_MACCATALYST = YES; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; COMBINE_HIDPI_IMAGES = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PLATFORM_NAME).$(PRODUCT_NAME:rfc1034identifier); PRODUCT_NAME = $(PROJECT_NAME); APPLICATION_EXTENSION_API_ONLY = YES; INFOPLIST_FILE = KeychainAccess/Info.plist; SKIP_INSTALL = YES; BUILD_LIBRARY_FOR_DISTRIBUTION = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = @rpath; ENABLE_BITCODE[sdk=iphone*] = YES; ENABLE_BITCODE[sdk=watch*] = YES; ENABLE_BITCODE[sdk=appletv*] = YES; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; ================================================ FILE: External/KeychainAccess/Lib/Configurations/Release.xcconfig ================================================ #include "Base.xcconfig" BITCODE_GENERATION_MODE = bitcode; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; MTL_ENABLE_DEBUG_INFO = NO; VALIDATE_PRODUCT = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = -Owholemodule; ================================================ FILE: External/KeychainAccess/Lib/Configurations/TestHost.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2,3; SUPPORTS_MACCATALYST = YES; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; INFOPLIST_FILE = TestHost/Info.plist; PRODUCT_NAME = $(TARGET_NAME); CLANG_MODULES_AUTOLINK = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.KeychainAccess.TestHost; CODE_SIGN_ENTITLEMENTS = TestHost/TestHost.entitlements; CODE_SIGN_IDENTITY[sdk=iphone*] = iPhone Developer; CODE_SIGN_IDENTITY[sdk=macosx*] = Developer ID Application; PROVISIONING_PROFILE_SPECIFIER[sdk=iphone*] = iOS Development; PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*] = KeychainAccess Tests; DEVELOPMENT_TEAM = 27AEDK3C9F; PRINCIPAL_CLASS[sdk=iphone*] = UIApplication; PRINCIPAL_CLASS[sdk=appletv*] = UIApplication; PRINCIPAL_CLASS[sdk=macosx*] = NSApplication; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; ================================================ FILE: External/KeychainAccess/Lib/Configurations/Tests.xcconfig ================================================ SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator; TARGETED_DEVICE_FAMILY = 1,2; SUPPORTS_MACCATALYST = YES; COMBINE_HIDPI_IMAGES = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.$(PRODUCT_NAME:rfc1034identifier); PRODUCT_NAME = $(TARGET_NAME); APPLICATION_EXTENSION_API_ONLY = NO; INFOPLIST_FILE = KeychainAccessTests/Info.plist; LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks; LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks; TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost; TEST_HOST[sdk=appletv*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost; TEST_HOST[sdk=macosx*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/Contents/MacOS/TestHost; EXCLUDED_SOURCE_FILE_NAMES[sdk=watch*] = *; EXCLUDED_SOURCE_FILE_NAMES[sdk=appletv*] = SharedCredentialTests.swift; EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = SharedCredentialTests.swift; DEVELOPMENT_TEAM = 27AEDK3C9F; ================================================ FILE: External/KeychainAccess/Lib/Gemfile ================================================ source 'https://rubygems.org' gem 'rake' gem 'xcpretty' gem 'xcjobs' ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 4.2.2 CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess/Keychain.swift ================================================ // // Keychain.swift // KeychainAccess // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import Security #if os(iOS) || os(OSX) import LocalAuthentication #endif public let KeychainAccessErrorDomain = "com.kishikawakatsumi.KeychainAccess.error" public enum ItemClass { case genericPassword case internetPassword } public enum ProtocolType { case ftp case ftpAccount case http case irc case nntp case pop3 case smtp case socks case imap case ldap case appleTalk case afp case telnet case ssh case ftps case https case httpProxy case httpsProxy case ftpProxy case smb case rtsp case rtspProxy case daap case eppc case ipp case nntps case ldaps case telnetS case imaps case ircs case pop3S } public enum AuthenticationType { case ntlm case msn case dpa case rpa case httpBasic case httpDigest case htmlForm case `default` } public enum Accessibility { /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will migrate to a new device when using encrypted backups. */ case whenUnlocked /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accesible by background applications. Items with this attribute will migrate to a new device when using encrypted backups. */ case afterFirstUnlock /** Item data can always be accessed regardless of the lock state of the device. This is not recommended for anything except system use. Items with this attribute will migrate to a new device when using encrypted backups. */ @available(macCatalyst, unavailable) case always /** Item data can only be accessed while the device is unlocked. This class is only available if a passcode is set on the device. This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode will cause all items in this class to be deleted. */ @available(iOS 8.0, OSX 10.10, *) case whenPasscodeSetThisDeviceOnly /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ case whenUnlockedThisDeviceOnly /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accessible by background applications. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device these items will be missing. */ case afterFirstUnlockThisDeviceOnly /** Item data can always be accessed regardless of the lock state of the device. This option is not recommended for anything except system use. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ @available(macCatalyst, unavailable) case alwaysThisDeviceOnly } /** Predefined item attribute constants used to get or set values in a dictionary. The kSecUseAuthenticationUI constant is the key and its value is one of the constants defined here. If the key kSecUseAuthenticationUI not provided then kSecUseAuthenticationUIAllow is used as default. */ public enum AuthenticationUI { /** Specifies that authenticate UI can appear. */ case allow /** Specifies that the error errSecInteractionNotAllowed will be returned if an item needs to authenticate with UI */ case fail /** Specifies that all items which need to authenticate with UI will be silently skipped. This value can be used only with SecItemCopyMatching. */ case skip } @available(iOS 9.0, OSX 10.11, *) extension AuthenticationUI { public var rawValue: String { switch self { case .allow: return UseAuthenticationUIAllow case .fail: return UseAuthenticationUIFail case .skip: return UseAuthenticationUISkip } } public var description: String { switch self { case .allow: return "allow" case .fail: return "fail" case .skip: return "skip" } } } public struct AuthenticationPolicy: OptionSet { /** User presence policy using Touch ID or Passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed. */ @available(iOS 8.0, OSX 10.10, watchOS 2.0, tvOS 8.0, *) public static let userPresence = AuthenticationPolicy(rawValue: 1 << 0) /** Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. */ @available(iOS 11.3, OSX 10.13.4, watchOS 4.3, tvOS 11.3, *) public static let biometryAny = AuthenticationPolicy(rawValue: 1 << 1) /** Deprecated, please use biometryAny instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryAny") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryAny") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") public static let touchIDAny = AuthenticationPolicy(rawValue: 1 << 1) /** Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated. */ @available(iOS 11.3, OSX 10.13, watchOS 4.3, tvOS 11.3, *) public static let biometryCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Deprecated, please use biometryCurrentSet instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryCurrentSet") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryCurrentSet") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") public static let touchIDCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Constraint: Device passcode */ @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) public static let devicePasscode = AuthenticationPolicy(rawValue: 1 << 4) /** Constraint: Watch */ @available(iOS, unavailable) @available(OSX 10.15, *) @available(watchOS, unavailable) @available(tvOS, unavailable) public static let watch = AuthenticationPolicy(rawValue: 1 << 5) /** Constraint logic operation: when using more than one constraint, at least one of them must be satisfied. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let or = AuthenticationPolicy(rawValue: 1 << 14) /** Constraint logic operation: when using more than one constraint, all must be satisfied. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let and = AuthenticationPolicy(rawValue: 1 << 15) /** Create access control for private key operations (i.e. sign operation) */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let privateKeyUsage = AuthenticationPolicy(rawValue: 1 << 30) /** Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let applicationPassword = AuthenticationPolicy(rawValue: 1 << 31) #if swift(>=2.3) public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } #else public let rawValue: Int public init(rawValue: Int) { self.rawValue = rawValue } #endif } public struct Attributes { public var `class`: String? { return attributes[Class] as? String } public var data: Data? { return attributes[ValueData] as? Data } public var ref: Data? { return attributes[ValueRef] as? Data } public var persistentRef: Data? { return attributes[ValuePersistentRef] as? Data } public var accessible: String? { return attributes[AttributeAccessible] as? String } public var accessControl: SecAccessControl? { if #available(OSX 10.10, *) { if let accessControl = attributes[AttributeAccessControl] { return (accessControl as! SecAccessControl) } return nil } else { return nil } } public var accessGroup: String? { return attributes[AttributeAccessGroup] as? String } public var synchronizable: Bool? { return attributes[AttributeSynchronizable] as? Bool } public var creationDate: Date? { return attributes[AttributeCreationDate] as? Date } public var modificationDate: Date? { return attributes[AttributeModificationDate] as? Date } public var attributeDescription: String? { return attributes[AttributeDescription] as? String } public var comment: String? { return attributes[AttributeComment] as? String } public var creator: String? { return attributes[AttributeCreator] as? String } public var type: String? { return attributes[AttributeType] as? String } public var label: String? { return attributes[AttributeLabel] as? String } public var isInvisible: Bool? { return attributes[AttributeIsInvisible] as? Bool } public var isNegative: Bool? { return attributes[AttributeIsNegative] as? Bool } public var account: String? { return attributes[AttributeAccount] as? String } public var service: String? { return attributes[AttributeService] as? String } public var generic: Data? { return attributes[AttributeGeneric] as? Data } public var securityDomain: String? { return attributes[AttributeSecurityDomain] as? String } public var server: String? { return attributes[AttributeServer] as? String } public var `protocol`: String? { return attributes[AttributeProtocol] as? String } public var authenticationType: String? { return attributes[AttributeAuthenticationType] as? String } public var port: Int? { return attributes[AttributePort] as? Int } public var path: String? { return attributes[AttributePath] as? String } fileprivate let attributes: [String: Any] init(attributes: [String: Any]) { self.attributes = attributes } public subscript(key: String) -> Any? { get { return attributes[key] } } } public final class Keychain { public var itemClass: ItemClass { return options.itemClass } public var service: String { return options.service } // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. public var accessGroup: String? { return options.accessGroup } public var server: URL { return options.server } public var protocolType: ProtocolType { return options.protocolType } public var authenticationType: AuthenticationType { return options.authenticationType } public var accessibility: Accessibility { return options.accessibility } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public var authenticationPolicy: AuthenticationPolicy? { return options.authenticationPolicy } public var synchronizable: Bool { return options.synchronizable } public var label: String? { return options.label } public var comment: String? { return options.comment } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public var authenticationPrompt: String? { return options.authenticationPrompt } @available(iOS 9.0, OSX 10.11, *) public var authenticationUI: AuthenticationUI { return options.authenticationUI ?? .allow } #if os(iOS) || os(OSX) @available(iOS 9.0, OSX 10.11, *) public var authenticationContext: LAContext? { return options.authenticationContext as? LAContext } #endif fileprivate let options: Options // MARK: public convenience init() { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } self.init(options) } public convenience init(service: String) { var options = Options() options.service = service self.init(options) } public convenience init(accessGroup: String) { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } options.accessGroup = accessGroup self.init(options) } public convenience init(service: String, accessGroup: String) { var options = Options() options.service = service options.accessGroup = accessGroup self.init(options) } public convenience init(server: String, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { self.init(server: URL(string: server)!, protocolType: protocolType, accessGroup: accessGroup, authenticationType: authenticationType) } public convenience init(server: URL, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { var options = Options() options.itemClass = .internetPassword options.server = server options.protocolType = protocolType options.accessGroup = accessGroup options.authenticationType = authenticationType self.init(options) } fileprivate init(_ opts: Options) { options = opts } // MARK: public func accessibility(_ accessibility: Accessibility) -> Keychain { var options = self.options options.accessibility = accessibility return Keychain(options) } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public func accessibility(_ accessibility: Accessibility, authenticationPolicy: AuthenticationPolicy) -> Keychain { var options = self.options options.accessibility = accessibility options.authenticationPolicy = authenticationPolicy return Keychain(options) } public func synchronizable(_ synchronizable: Bool) -> Keychain { var options = self.options options.synchronizable = synchronizable return Keychain(options) } public func label(_ label: String) -> Keychain { var options = self.options options.label = label return Keychain(options) } public func comment(_ comment: String) -> Keychain { var options = self.options options.comment = comment return Keychain(options) } public func attributes(_ attributes: [String: Any]) -> Keychain { var options = self.options attributes.forEach { options.attributes.updateValue($1, forKey: $0) } return Keychain(options) } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public func authenticationPrompt(_ authenticationPrompt: String) -> Keychain { var options = self.options options.authenticationPrompt = authenticationPrompt return Keychain(options) } @available(iOS 9.0, OSX 10.11, *) public func authenticationUI(_ authenticationUI: AuthenticationUI) -> Keychain { var options = self.options options.authenticationUI = authenticationUI return Keychain(options) } #if os(iOS) || os(OSX) @available(iOS 9.0, OSX 10.11, *) public func authenticationContext(_ authenticationContext: LAContext) -> Keychain { var options = self.options options.authenticationContext = authenticationContext return Keychain(options) } #endif // MARK: public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { return try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func getString(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { guard let data = try getData(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else { return nil } guard let string = String(data: data, encoding: .utf8) else { print("failed to convert data to string") throw Status.conversionError } return string } public func getData(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Data? { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let data = result as? Data else { throw Status.unexpectedError } return data case errSecItemNotFound: return nil default: throw securityError(status: status) } } public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true, handler: (Attributes?) -> T) throws -> T { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[ReturnAttributes] = kCFBooleanTrue query[ReturnRef] = kCFBooleanTrue query[ReturnPersistentRef] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let attributes = result as? [String: Any] else { throw Status.unexpectedError } return handler(Attributes(attributes: attributes)) case errSecItemNotFound: return handler(nil) default: throw securityError(status: status) } } // MARK: public func set(_ value: String, key: String, ignoringAttributeSynchronizable: Bool = true) throws { guard let data = value.data(using: .utf8, allowLossyConversion: false) else { print("failed to convert string to data") throw Status.conversionError } try set(data, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func set(_ value: Data, key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key #if os(iOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #elseif os(OSX) query[ReturnData] = kCFBooleanTrue if #available(OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } #else if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } #endif var status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess, errSecInteractionNotAllowed: var query = options.query() query[AttributeAccount] = key var (attributes, error) = options.attributes(key: nil, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } #if os(iOS) if status == errSecInteractionNotAllowed && floor(NSFoundationVersionNumber) <= floor(NSFoundationVersionNumber_iOS_8_0) { try remove(key) try set(value, key: key) } else { status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } } #else status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } #endif case errSecItemNotFound: var (attributes, error) = options.attributes(key: key, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } status = SecItemAdd(attributes as CFDictionary, nil) if status != errSecSuccess { throw securityError(status: status) } default: throw securityError(status: status) } } public subscript(key: String) -> String? { get { #if swift(>=5.0) return try? get(key) #else return (try? get(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(string key: String) -> String? { get { return self[key] } set { self[key] = newValue } } public subscript(data key: String) -> Data? { get { #if swift(>=5.0) return try? getData(key) #else return (try? getData(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(attributes key: String) -> Attributes? { get { #if swift(>=5.0) return try? get(key) { $0 } #else return (try? get(key) { $0 }).flatMap { $0 } #endif } } // MARK: public func remove(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } public func removeAll() throws { var query = options.query() #if !os(iOS) && !os(watchOS) && !os(tvOS) query[MatchLimit] = MatchLimitAll #endif let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } // MARK: public func contains(_ key: String, withoutAuthenticationUI: Bool = false) throws -> Bool { var query = options.query() query[AttributeAccount] = key if withoutAuthenticationUI { #if os(iOS) || os(watchOS) || os(tvOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #else if #available(OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else if #available(OSX 10.10, *) { query[UseNoAuthenticationUI] = kCFBooleanTrue } #endif } else { if #available(iOS 9.0, OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } } } let status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess: return true case errSecInteractionNotAllowed: if withoutAuthenticationUI { return true } return false case errSecItemNotFound: return false default: throw securityError(status: status) } } // MARK: public class func allKeys(_ itemClass: ItemClass) -> [(String, String)] { var query = [String: Any]() query[Class] = itemClass.rawValue query[AttributeSynchronizable] = SynchronizableAny query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items).map { switch itemClass { case .genericPassword: return (($0["service"] ?? "") as! String, ($0["key"] ?? "") as! String) case .internetPassword: return (($0["server"] ?? "") as! String, ($0["key"] ?? "") as! String) } } } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allKeys() -> [String] { let allItems = type(of: self).prettify(itemClass: itemClass, items: items()) let filter: ([String: Any]) -> String? = { $0["key"] as? String } #if swift(>=4.1) return allItems.compactMap(filter) #else return allItems.flatMap(filter) #endif } public class func allItems(_ itemClass: ItemClass) -> [[String: Any]] { var query = [String: Any]() query[Class] = itemClass.rawValue query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items) } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allItems() -> [[String: Any]] { return type(of: self).prettify(itemClass: itemClass, items: items()) } #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ completion: @escaping (_ account: String?, _ password: String?, _ error: Error?) -> () = { account, password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: nil) { (credentials, error) -> () in if let credential = credentials.first { let account = credential["account"] let password = credential["password"] completion(account, password, error) } else { completion(nil, nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ account: String, completion: @escaping (_ password: String?, _ error: Error?) -> () = { password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: account) { (credentials, error) -> () in if let credential = credentials.first { if let password = credential["password"] { completion(password, error) } else { completion(nil, error) } } else { completion(nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func setSharedPassword(_ password: String, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(password as String?, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate func setSharedPassword(_ password: String?, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { if let domain = server.host { SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { error -> () in if let error = error { completion(error.error) } else { completion(nil) } } } else { let error = securityError(status: Status.param.rawValue) completion(error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func removeSharedPassword(_ account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(nil, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(_ completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: nil, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: domain, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, account: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: Optional(domain), account: Optional(account)!, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate class func requestSharedWebCredential(domain: String?, account: String?, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> ()) { SecRequestSharedWebCredential(domain as CFString?, account as CFString?) { (credentials, error) -> () in var remoteError: NSError? if let error = error { remoteError = error.error if remoteError?.code != Int(errSecItemNotFound) { print("error:[\(remoteError!.code)] \(remoteError!.localizedDescription)") } } if let credentials = credentials { let credentials = (credentials as NSArray).map { credentials -> [String: String] in var credential = [String: String]() if let credentials = credentials as? [String: String] { if let server = credentials[AttributeServer] { credential["server"] = server } if let account = credentials[AttributeAccount] { credential["account"] = account } if let password = credentials[SharedPassword] { credential["password"] = password } } return credential } completion(credentials, remoteError) } else { completion([], remoteError) } } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) /** @abstract Returns a randomly generated password. @return String password in the form xxx-xxx-xxx-xxx where x is taken from the sets "abcdefghkmnopqrstuvwxy", "ABCDEFGHJKLMNPQRSTUVWXYZ", "3456789" with at least one character from each set being present. */ @available(iOS 8.0, *) public class func generatePassword() -> String { return SecCreateSharedWebCredentialPassword()! as String } #endif // MARK: fileprivate func items() -> [[String: Any]] { var query = options.query() query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return items } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } fileprivate class func prettify(itemClass: ItemClass, items: [[String: Any]]) -> [[String: Any]] { let items = items.map { attributes -> [String: Any] in var item = [String: Any]() item["class"] = itemClass.description if let accessGroup = attributes[AttributeAccessGroup] as? String { item["accessGroup"] = accessGroup } switch itemClass { case .genericPassword: if let service = attributes[AttributeService] as? String { item["service"] = service } case .internetPassword: if let server = attributes[AttributeServer] as? String { item["server"] = server } if let proto = attributes[AttributeProtocol] as? String { if let protocolType = ProtocolType(rawValue: proto) { item["protocol"] = protocolType.description } } if let auth = attributes[AttributeAuthenticationType] as? String { if let authenticationType = AuthenticationType(rawValue: auth) { item["authenticationType"] = authenticationType.description } } } if let key = attributes[AttributeAccount] as? String { item["key"] = key } if let data = attributes[ValueData] as? Data { if let text = String(data: data, encoding: .utf8) { item["value"] = text } else { item["value"] = data } } if let accessible = attributes[AttributeAccessible] as? String { if let accessibility = Accessibility(rawValue: accessible) { item["accessibility"] = accessibility.description } } if let synchronizable = attributes[AttributeSynchronizable] as? Bool { item["synchronizable"] = synchronizable ? "true" : "false" } return item } return items } // MARK: @discardableResult fileprivate class func securityError(status: OSStatus) -> Error { let error = Status(status: status) if error != .userCanceled { print("OSStatus error:[\(error.errorCode)] \(error.description)") } return error } @discardableResult fileprivate func securityError(status: OSStatus) -> Error { return type(of: self).securityError(status: status) } } struct Options { var itemClass: ItemClass = .genericPassword var service: String = "" var accessGroup: String? = nil var server: URL! var protocolType: ProtocolType! var authenticationType: AuthenticationType = .default var accessibility: Accessibility = .afterFirstUnlock var authenticationPolicy: AuthenticationPolicy? var synchronizable: Bool = false var label: String? var comment: String? var authenticationPrompt: String? var authenticationUI: AuthenticationUI? var authenticationContext: AnyObject? var attributes = [String: Any]() } /** Class Key Constant */ private let Class = String(kSecClass) /** Attribute Key Constants */ private let AttributeAccessible = String(kSecAttrAccessible) @available(iOS 8.0, OSX 10.10, *) private let AttributeAccessControl = String(kSecAttrAccessControl) private let AttributeAccessGroup = String(kSecAttrAccessGroup) private let AttributeSynchronizable = String(kSecAttrSynchronizable) private let AttributeCreationDate = String(kSecAttrCreationDate) private let AttributeModificationDate = String(kSecAttrModificationDate) private let AttributeDescription = String(kSecAttrDescription) private let AttributeComment = String(kSecAttrComment) private let AttributeCreator = String(kSecAttrCreator) private let AttributeType = String(kSecAttrType) private let AttributeLabel = String(kSecAttrLabel) private let AttributeIsInvisible = String(kSecAttrIsInvisible) private let AttributeIsNegative = String(kSecAttrIsNegative) private let AttributeAccount = String(kSecAttrAccount) private let AttributeService = String(kSecAttrService) private let AttributeGeneric = String(kSecAttrGeneric) private let AttributeSecurityDomain = String(kSecAttrSecurityDomain) private let AttributeServer = String(kSecAttrServer) private let AttributeProtocol = String(kSecAttrProtocol) private let AttributeAuthenticationType = String(kSecAttrAuthenticationType) private let AttributePort = String(kSecAttrPort) private let AttributePath = String(kSecAttrPath) private let SynchronizableAny = kSecAttrSynchronizableAny /** Search Constants */ private let MatchLimit = String(kSecMatchLimit) private let MatchLimitOne = kSecMatchLimitOne private let MatchLimitAll = kSecMatchLimitAll /** Return Type Key Constants */ private let ReturnData = String(kSecReturnData) private let ReturnAttributes = String(kSecReturnAttributes) private let ReturnRef = String(kSecReturnRef) private let ReturnPersistentRef = String(kSecReturnPersistentRef) /** Value Type Key Constants */ private let ValueData = String(kSecValueData) private let ValueRef = String(kSecValueRef) private let ValuePersistentRef = String(kSecValuePersistentRef) /** Other Constants */ @available(iOS 8.0, OSX 10.10, tvOS 8.0, *) private let UseOperationPrompt = String(kSecUseOperationPrompt) @available(iOS, introduced: 8.0, deprecated: 9.0, message: "Use a UseAuthenticationUI instead.") @available(OSX, introduced: 10.10, deprecated: 10.11, message: "Use UseAuthenticationUI instead.") @available(watchOS, introduced: 2.0, deprecated: 2.0, message: "Use UseAuthenticationUI instead.") @available(tvOS, introduced: 8.0, deprecated: 9.0, message: "Use UseAuthenticationUI instead.") private let UseNoAuthenticationUI = String(kSecUseNoAuthenticationUI) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUI = String(kSecUseAuthenticationUI) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationContext = String(kSecUseAuthenticationContext) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIAllow = String(kSecUseAuthenticationUIAllow) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIFail = String(kSecUseAuthenticationUIFail) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUISkip = String(kSecUseAuthenticationUISkip) #if os(iOS) && !targetEnvironment(macCatalyst) /** Credential Key Constants */ private let SharedPassword = String(kSecSharedPassword) #endif extension Keychain: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { let items = allItems() if items.isEmpty { return "[]" } var description = "[\n" for item in items { description += " " description += "\(item)\n" } description += "]" return description } public var debugDescription: String { return "\(items())" } } extension Options { func query(ignoringAttributeSynchronizable: Bool = true) -> [String: Any] { var query = [String: Any]() query[Class] = itemClass.rawValue if let accessGroup = self.accessGroup { query[AttributeAccessGroup] = accessGroup } if ignoringAttributeSynchronizable { query[AttributeSynchronizable] = SynchronizableAny } else { query[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse } switch itemClass { case .genericPassword: query[AttributeService] = service case .internetPassword: query[AttributeServer] = server.host query[AttributePort] = server.port query[AttributeProtocol] = protocolType.rawValue query[AttributeAuthenticationType] = authenticationType.rawValue } if #available(OSX 10.10, *) { if authenticationPrompt != nil { query[UseOperationPrompt] = authenticationPrompt } } #if !os(watchOS) if #available(iOS 9.0, OSX 10.11, *) { if authenticationContext != nil { query[UseAuthenticationContext] = authenticationContext } } #endif return query } func attributes(key: String?, value: Data) -> ([String: Any], Error?) { var attributes: [String: Any] if key != nil { attributes = query() attributes[AttributeAccount] = key } else { attributes = [String: Any]() } attributes[ValueData] = value if label != nil { attributes[AttributeLabel] = label } if comment != nil { attributes[AttributeComment] = comment } if let policy = authenticationPolicy { if #available(OSX 10.10, *) { var error: Unmanaged? guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, SecAccessControlCreateFlags(rawValue: CFOptionFlags(policy.rawValue)), &error) else { if let error = error?.takeUnretainedValue() { return (attributes, error.error) } return (attributes, Status.unexpectedError) } attributes[AttributeAccessControl] = accessControl } else { print("Unavailable 'Touch ID integration' on OS X versions prior to 10.10.") } } else { attributes[AttributeAccessible] = accessibility.rawValue } attributes[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse return (attributes, nil) } } // MARK: extension Attributes: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { return "\(attributes)" } public var debugDescription: String { return description } } extension ItemClass: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecClassGenericPassword): self = .genericPassword case String(kSecClassInternetPassword): self = .internetPassword default: return nil } } public var rawValue: String { switch self { case .genericPassword: return String(kSecClassGenericPassword) case .internetPassword: return String(kSecClassInternetPassword) } } public var description: String { switch self { case .genericPassword: return "GenericPassword" case .internetPassword: return "InternetPassword" } } } extension ProtocolType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrProtocolFTP): self = .ftp case String(kSecAttrProtocolFTPAccount): self = .ftpAccount case String(kSecAttrProtocolHTTP): self = .http case String(kSecAttrProtocolIRC): self = .irc case String(kSecAttrProtocolNNTP): self = .nntp case String(kSecAttrProtocolPOP3): self = .pop3 case String(kSecAttrProtocolSMTP): self = .smtp case String(kSecAttrProtocolSOCKS): self = .socks case String(kSecAttrProtocolIMAP): self = .imap case String(kSecAttrProtocolLDAP): self = .ldap case String(kSecAttrProtocolAppleTalk): self = .appleTalk case String(kSecAttrProtocolAFP): self = .afp case String(kSecAttrProtocolTelnet): self = .telnet case String(kSecAttrProtocolSSH): self = .ssh case String(kSecAttrProtocolFTPS): self = .ftps case String(kSecAttrProtocolHTTPS): self = .https case String(kSecAttrProtocolHTTPProxy): self = .httpProxy case String(kSecAttrProtocolHTTPSProxy): self = .httpsProxy case String(kSecAttrProtocolFTPProxy): self = .ftpProxy case String(kSecAttrProtocolSMB): self = .smb case String(kSecAttrProtocolRTSP): self = .rtsp case String(kSecAttrProtocolRTSPProxy): self = .rtspProxy case String(kSecAttrProtocolDAAP): self = .daap case String(kSecAttrProtocolEPPC): self = .eppc case String(kSecAttrProtocolIPP): self = .ipp case String(kSecAttrProtocolNNTPS): self = .nntps case String(kSecAttrProtocolLDAPS): self = .ldaps case String(kSecAttrProtocolTelnetS): self = .telnetS case String(kSecAttrProtocolIMAPS): self = .imaps case String(kSecAttrProtocolIRCS): self = .ircs case String(kSecAttrProtocolPOP3S): self = .pop3S default: return nil } } public var rawValue: String { switch self { case .ftp: return String(kSecAttrProtocolFTP) case .ftpAccount: return String(kSecAttrProtocolFTPAccount) case .http: return String(kSecAttrProtocolHTTP) case .irc: return String(kSecAttrProtocolIRC) case .nntp: return String(kSecAttrProtocolNNTP) case .pop3: return String(kSecAttrProtocolPOP3) case .smtp: return String(kSecAttrProtocolSMTP) case .socks: return String(kSecAttrProtocolSOCKS) case .imap: return String(kSecAttrProtocolIMAP) case .ldap: return String(kSecAttrProtocolLDAP) case .appleTalk: return String(kSecAttrProtocolAppleTalk) case .afp: return String(kSecAttrProtocolAFP) case .telnet: return String(kSecAttrProtocolTelnet) case .ssh: return String(kSecAttrProtocolSSH) case .ftps: return String(kSecAttrProtocolFTPS) case .https: return String(kSecAttrProtocolHTTPS) case .httpProxy: return String(kSecAttrProtocolHTTPProxy) case .httpsProxy: return String(kSecAttrProtocolHTTPSProxy) case .ftpProxy: return String(kSecAttrProtocolFTPProxy) case .smb: return String(kSecAttrProtocolSMB) case .rtsp: return String(kSecAttrProtocolRTSP) case .rtspProxy: return String(kSecAttrProtocolRTSPProxy) case .daap: return String(kSecAttrProtocolDAAP) case .eppc: return String(kSecAttrProtocolEPPC) case .ipp: return String(kSecAttrProtocolIPP) case .nntps: return String(kSecAttrProtocolNNTPS) case .ldaps: return String(kSecAttrProtocolLDAPS) case .telnetS: return String(kSecAttrProtocolTelnetS) case .imaps: return String(kSecAttrProtocolIMAPS) case .ircs: return String(kSecAttrProtocolIRCS) case .pop3S: return String(kSecAttrProtocolPOP3S) } } public var description: String { switch self { case .ftp: return "FTP" case .ftpAccount: return "FTPAccount" case .http: return "HTTP" case .irc: return "IRC" case .nntp: return "NNTP" case .pop3: return "POP3" case .smtp: return "SMTP" case .socks: return "SOCKS" case .imap: return "IMAP" case .ldap: return "LDAP" case .appleTalk: return "AppleTalk" case .afp: return "AFP" case .telnet: return "Telnet" case .ssh: return "SSH" case .ftps: return "FTPS" case .https: return "HTTPS" case .httpProxy: return "HTTPProxy" case .httpsProxy: return "HTTPSProxy" case .ftpProxy: return "FTPProxy" case .smb: return "SMB" case .rtsp: return "RTSP" case .rtspProxy: return "RTSPProxy" case .daap: return "DAAP" case .eppc: return "EPPC" case .ipp: return "IPP" case .nntps: return "NNTPS" case .ldaps: return "LDAPS" case .telnetS: return "TelnetS" case .imaps: return "IMAPS" case .ircs: return "IRCS" case .pop3S: return "POP3S" } } } extension AuthenticationType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrAuthenticationTypeNTLM): self = .ntlm case String(kSecAttrAuthenticationTypeMSN): self = .msn case String(kSecAttrAuthenticationTypeDPA): self = .dpa case String(kSecAttrAuthenticationTypeRPA): self = .rpa case String(kSecAttrAuthenticationTypeHTTPBasic): self = .httpBasic case String(kSecAttrAuthenticationTypeHTTPDigest): self = .httpDigest case String(kSecAttrAuthenticationTypeHTMLForm): self = .htmlForm case String(kSecAttrAuthenticationTypeDefault): self = .`default` default: return nil } } public var rawValue: String { switch self { case .ntlm: return String(kSecAttrAuthenticationTypeNTLM) case .msn: return String(kSecAttrAuthenticationTypeMSN) case .dpa: return String(kSecAttrAuthenticationTypeDPA) case .rpa: return String(kSecAttrAuthenticationTypeRPA) case .httpBasic: return String(kSecAttrAuthenticationTypeHTTPBasic) case .httpDigest: return String(kSecAttrAuthenticationTypeHTTPDigest) case .htmlForm: return String(kSecAttrAuthenticationTypeHTMLForm) case .`default`: return String(kSecAttrAuthenticationTypeDefault) } } public var description: String { switch self { case .ntlm: return "NTLM" case .msn: return "MSN" case .dpa: return "DPA" case .rpa: return "RPA" case .httpBasic: return "HTTPBasic" case .httpDigest: return "HTTPDigest" case .htmlForm: return "HTMLForm" case .`default`: return "Default" } } } extension Accessibility: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { if #available(OSX 10.10, *) { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly): self = .whenPasscodeSetThisDeviceOnly case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } else { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } } public var rawValue: String { switch self { case .whenUnlocked: return String(kSecAttrAccessibleWhenUnlocked) case .afterFirstUnlock: return String(kSecAttrAccessibleAfterFirstUnlock) #if !targetEnvironment(macCatalyst) case .always: return String(kSecAttrAccessibleAlways) #endif case .whenPasscodeSetThisDeviceOnly: if #available(OSX 10.10, *) { return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) } else { fatalError("'Accessibility.WhenPasscodeSetThisDeviceOnly' is not available on this version of OS.") } case .whenUnlockedThisDeviceOnly: return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) case .afterFirstUnlockThisDeviceOnly: return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return String(kSecAttrAccessibleAlwaysThisDeviceOnly) #endif } } public var description: String { switch self { case .whenUnlocked: return "WhenUnlocked" case .afterFirstUnlock: return "AfterFirstUnlock" #if !targetEnvironment(macCatalyst) case .always: return "Always" #endif case .whenPasscodeSetThisDeviceOnly: return "WhenPasscodeSetThisDeviceOnly" case .whenUnlockedThisDeviceOnly: return "WhenUnlockedThisDeviceOnly" case .afterFirstUnlockThisDeviceOnly: return "AfterFirstUnlockThisDeviceOnly" #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return "AlwaysThisDeviceOnly" #endif } } } extension CFError { var error: NSError { let domain = CFErrorGetDomain(self) as String let code = CFErrorGetCode(self) let userInfo = CFErrorCopyUserInfo(self) as! [String: Any] return NSError(domain: domain, code: code, userInfo: userInfo) } } public enum Status: OSStatus, Error { case success = 0 case unimplemented = -4 case diskFull = -34 case io = -36 case opWr = -49 case param = -50 case wrPerm = -61 case allocate = -108 case userCanceled = -128 case badReq = -909 case internalComponent = -2070 case notAvailable = -25291 case readOnly = -25292 case authFailed = -25293 case noSuchKeychain = -25294 case invalidKeychain = -25295 case duplicateKeychain = -25296 case duplicateCallback = -25297 case invalidCallback = -25298 case duplicateItem = -25299 case itemNotFound = -25300 case bufferTooSmall = -25301 case dataTooLarge = -25302 case noSuchAttr = -25303 case invalidItemRef = -25304 case invalidSearchRef = -25305 case noSuchClass = -25306 case noDefaultKeychain = -25307 case interactionNotAllowed = -25308 case readOnlyAttr = -25309 case wrongSecVersion = -25310 case keySizeNotAllowed = -25311 case noStorageModule = -25312 case noCertificateModule = -25313 case noPolicyModule = -25314 case interactionRequired = -25315 case dataNotAvailable = -25316 case dataNotModifiable = -25317 case createChainFailed = -25318 case invalidPrefsDomain = -25319 case inDarkWake = -25320 case aclNotSimple = -25240 case policyNotFound = -25241 case invalidTrustSetting = -25242 case noAccessForItem = -25243 case invalidOwnerEdit = -25244 case trustNotAvailable = -25245 case unsupportedFormat = -25256 case unknownFormat = -25257 case keyIsSensitive = -25258 case multiplePrivKeys = -25259 case passphraseRequired = -25260 case invalidPasswordRef = -25261 case invalidTrustSettings = -25262 case noTrustSettings = -25263 case pkcs12VerifyFailure = -25264 case invalidCertificate = -26265 case notSigner = -26267 case policyDenied = -26270 case invalidKey = -26274 case decode = -26275 case `internal` = -26276 case unsupportedAlgorithm = -26268 case unsupportedOperation = -26271 case unsupportedPadding = -26273 case itemInvalidKey = -34000 case itemInvalidKeyType = -34001 case itemInvalidValue = -34002 case itemClassMissing = -34003 case itemMatchUnsupported = -34004 case useItemListUnsupported = -34005 case useKeychainUnsupported = -34006 case useKeychainListUnsupported = -34007 case returnDataUnsupported = -34008 case returnAttributesUnsupported = -34009 case returnRefUnsupported = -34010 case returnPersitentRefUnsupported = -34011 case valueRefUnsupported = -34012 case valuePersistentRefUnsupported = -34013 case returnMissingPointer = -34014 case matchLimitUnsupported = -34015 case itemIllegalQuery = -34016 case waitForCallback = -34017 case missingEntitlement = -34018 case upgradePending = -34019 case mpSignatureInvalid = -25327 case otrTooOld = -25328 case otrIDTooNew = -25329 case serviceNotAvailable = -67585 case insufficientClientID = -67586 case deviceReset = -67587 case deviceFailed = -67588 case appleAddAppACLSubject = -67589 case applePublicKeyIncomplete = -67590 case appleSignatureMismatch = -67591 case appleInvalidKeyStartDate = -67592 case appleInvalidKeyEndDate = -67593 case conversionError = -67594 case appleSSLv2Rollback = -67595 case quotaExceeded = -67596 case fileTooBig = -67597 case invalidDatabaseBlob = -67598 case invalidKeyBlob = -67599 case incompatibleDatabaseBlob = -67600 case incompatibleKeyBlob = -67601 case hostNameMismatch = -67602 case unknownCriticalExtensionFlag = -67603 case noBasicConstraints = -67604 case noBasicConstraintsCA = -67605 case invalidAuthorityKeyID = -67606 case invalidSubjectKeyID = -67607 case invalidKeyUsageForPolicy = -67608 case invalidExtendedKeyUsage = -67609 case invalidIDLinkage = -67610 case pathLengthConstraintExceeded = -67611 case invalidRoot = -67612 case crlExpired = -67613 case crlNotValidYet = -67614 case crlNotFound = -67615 case crlServerDown = -67616 case crlBadURI = -67617 case unknownCertExtension = -67618 case unknownCRLExtension = -67619 case crlNotTrusted = -67620 case crlPolicyFailed = -67621 case idpFailure = -67622 case smimeEmailAddressesNotFound = -67623 case smimeBadExtendedKeyUsage = -67624 case smimeBadKeyUsage = -67625 case smimeKeyUsageNotCritical = -67626 case smimeNoEmailAddress = -67627 case smimeSubjAltNameNotCritical = -67628 case sslBadExtendedKeyUsage = -67629 case ocspBadResponse = -67630 case ocspBadRequest = -67631 case ocspUnavailable = -67632 case ocspStatusUnrecognized = -67633 case endOfData = -67634 case incompleteCertRevocationCheck = -67635 case networkFailure = -67636 case ocspNotTrustedToAnchor = -67637 case recordModified = -67638 case ocspSignatureError = -67639 case ocspNoSigner = -67640 case ocspResponderMalformedReq = -67641 case ocspResponderInternalError = -67642 case ocspResponderTryLater = -67643 case ocspResponderSignatureRequired = -67644 case ocspResponderUnauthorized = -67645 case ocspResponseNonceMismatch = -67646 case codeSigningBadCertChainLength = -67647 case codeSigningNoBasicConstraints = -67648 case codeSigningBadPathLengthConstraint = -67649 case codeSigningNoExtendedKeyUsage = -67650 case codeSigningDevelopment = -67651 case resourceSignBadCertChainLength = -67652 case resourceSignBadExtKeyUsage = -67653 case trustSettingDeny = -67654 case invalidSubjectName = -67655 case unknownQualifiedCertStatement = -67656 case mobileMeRequestQueued = -67657 case mobileMeRequestRedirected = -67658 case mobileMeServerError = -67659 case mobileMeServerNotAvailable = -67660 case mobileMeServerAlreadyExists = -67661 case mobileMeServerServiceErr = -67662 case mobileMeRequestAlreadyPending = -67663 case mobileMeNoRequestPending = -67664 case mobileMeCSRVerifyFailure = -67665 case mobileMeFailedConsistencyCheck = -67666 case notInitialized = -67667 case invalidHandleUsage = -67668 case pvcReferentNotFound = -67669 case functionIntegrityFail = -67670 case internalError = -67671 case memoryError = -67672 case invalidData = -67673 case mdsError = -67674 case invalidPointer = -67675 case selfCheckFailed = -67676 case functionFailed = -67677 case moduleManifestVerifyFailed = -67678 case invalidGUID = -67679 case invalidHandle = -67680 case invalidDBList = -67681 case invalidPassthroughID = -67682 case invalidNetworkAddress = -67683 case crlAlreadySigned = -67684 case invalidNumberOfFields = -67685 case verificationFailure = -67686 case unknownTag = -67687 case invalidSignature = -67688 case invalidName = -67689 case invalidCertificateRef = -67690 case invalidCertificateGroup = -67691 case tagNotFound = -67692 case invalidQuery = -67693 case invalidValue = -67694 case callbackFailed = -67695 case aclDeleteFailed = -67696 case aclReplaceFailed = -67697 case aclAddFailed = -67698 case aclChangeFailed = -67699 case invalidAccessCredentials = -67700 case invalidRecord = -67701 case invalidACL = -67702 case invalidSampleValue = -67703 case incompatibleVersion = -67704 case privilegeNotGranted = -67705 case invalidScope = -67706 case pvcAlreadyConfigured = -67707 case invalidPVC = -67708 case emmLoadFailed = -67709 case emmUnloadFailed = -67710 case addinLoadFailed = -67711 case invalidKeyRef = -67712 case invalidKeyHierarchy = -67713 case addinUnloadFailed = -67714 case libraryReferenceNotFound = -67715 case invalidAddinFunctionTable = -67716 case invalidServiceMask = -67717 case moduleNotLoaded = -67718 case invalidSubServiceID = -67719 case attributeNotInContext = -67720 case moduleManagerInitializeFailed = -67721 case moduleManagerNotFound = -67722 case eventNotificationCallbackNotFound = -67723 case inputLengthError = -67724 case outputLengthError = -67725 case privilegeNotSupported = -67726 case deviceError = -67727 case attachHandleBusy = -67728 case notLoggedIn = -67729 case algorithmMismatch = -67730 case keyUsageIncorrect = -67731 case keyBlobTypeIncorrect = -67732 case keyHeaderInconsistent = -67733 case unsupportedKeyFormat = -67734 case unsupportedKeySize = -67735 case invalidKeyUsageMask = -67736 case unsupportedKeyUsageMask = -67737 case invalidKeyAttributeMask = -67738 case unsupportedKeyAttributeMask = -67739 case invalidKeyLabel = -67740 case unsupportedKeyLabel = -67741 case invalidKeyFormat = -67742 case unsupportedVectorOfBuffers = -67743 case invalidInputVector = -67744 case invalidOutputVector = -67745 case invalidContext = -67746 case invalidAlgorithm = -67747 case invalidAttributeKey = -67748 case missingAttributeKey = -67749 case invalidAttributeInitVector = -67750 case missingAttributeInitVector = -67751 case invalidAttributeSalt = -67752 case missingAttributeSalt = -67753 case invalidAttributePadding = -67754 case missingAttributePadding = -67755 case invalidAttributeRandom = -67756 case missingAttributeRandom = -67757 case invalidAttributeSeed = -67758 case missingAttributeSeed = -67759 case invalidAttributePassphrase = -67760 case missingAttributePassphrase = -67761 case invalidAttributeKeyLength = -67762 case missingAttributeKeyLength = -67763 case invalidAttributeBlockSize = -67764 case missingAttributeBlockSize = -67765 case invalidAttributeOutputSize = -67766 case missingAttributeOutputSize = -67767 case invalidAttributeRounds = -67768 case missingAttributeRounds = -67769 case invalidAlgorithmParms = -67770 case missingAlgorithmParms = -67771 case invalidAttributeLabel = -67772 case missingAttributeLabel = -67773 case invalidAttributeKeyType = -67774 case missingAttributeKeyType = -67775 case invalidAttributeMode = -67776 case missingAttributeMode = -67777 case invalidAttributeEffectiveBits = -67778 case missingAttributeEffectiveBits = -67779 case invalidAttributeStartDate = -67780 case missingAttributeStartDate = -67781 case invalidAttributeEndDate = -67782 case missingAttributeEndDate = -67783 case invalidAttributeVersion = -67784 case missingAttributeVersion = -67785 case invalidAttributePrime = -67786 case missingAttributePrime = -67787 case invalidAttributeBase = -67788 case missingAttributeBase = -67789 case invalidAttributeSubprime = -67790 case missingAttributeSubprime = -67791 case invalidAttributeIterationCount = -67792 case missingAttributeIterationCount = -67793 case invalidAttributeDLDBHandle = -67794 case missingAttributeDLDBHandle = -67795 case invalidAttributeAccessCredentials = -67796 case missingAttributeAccessCredentials = -67797 case invalidAttributePublicKeyFormat = -67798 case missingAttributePublicKeyFormat = -67799 case invalidAttributePrivateKeyFormat = -67800 case missingAttributePrivateKeyFormat = -67801 case invalidAttributeSymmetricKeyFormat = -67802 case missingAttributeSymmetricKeyFormat = -67803 case invalidAttributeWrappedKeyFormat = -67804 case missingAttributeWrappedKeyFormat = -67805 case stagedOperationInProgress = -67806 case stagedOperationNotStarted = -67807 case verifyFailed = -67808 case querySizeUnknown = -67809 case blockSizeMismatch = -67810 case publicKeyInconsistent = -67811 case deviceVerifyFailed = -67812 case invalidLoginName = -67813 case alreadyLoggedIn = -67814 case invalidDigestAlgorithm = -67815 case invalidCRLGroup = -67816 case certificateCannotOperate = -67817 case certificateExpired = -67818 case certificateNotValidYet = -67819 case certificateRevoked = -67820 case certificateSuspended = -67821 case insufficientCredentials = -67822 case invalidAction = -67823 case invalidAuthority = -67824 case verifyActionFailed = -67825 case invalidCertAuthority = -67826 case invaldCRLAuthority = -67827 case invalidCRLEncoding = -67828 case invalidCRLType = -67829 case invalidCRL = -67830 case invalidFormType = -67831 case invalidID = -67832 case invalidIdentifier = -67833 case invalidIndex = -67834 case invalidPolicyIdentifiers = -67835 case invalidTimeString = -67836 case invalidReason = -67837 case invalidRequestInputs = -67838 case invalidResponseVector = -67839 case invalidStopOnPolicy = -67840 case invalidTuple = -67841 case multipleValuesUnsupported = -67842 case notTrusted = -67843 case noDefaultAuthority = -67844 case rejectedForm = -67845 case requestLost = -67846 case requestRejected = -67847 case unsupportedAddressType = -67848 case unsupportedService = -67849 case invalidTupleGroup = -67850 case invalidBaseACLs = -67851 case invalidTupleCredendtials = -67852 case invalidEncoding = -67853 case invalidValidityPeriod = -67854 case invalidRequestor = -67855 case requestDescriptor = -67856 case invalidBundleInfo = -67857 case invalidCRLIndex = -67858 case noFieldValues = -67859 case unsupportedFieldFormat = -67860 case unsupportedIndexInfo = -67861 case unsupportedLocality = -67862 case unsupportedNumAttributes = -67863 case unsupportedNumIndexes = -67864 case unsupportedNumRecordTypes = -67865 case fieldSpecifiedMultiple = -67866 case incompatibleFieldFormat = -67867 case invalidParsingModule = -67868 case databaseLocked = -67869 case datastoreIsOpen = -67870 case missingValue = -67871 case unsupportedQueryLimits = -67872 case unsupportedNumSelectionPreds = -67873 case unsupportedOperator = -67874 case invalidDBLocation = -67875 case invalidAccessRequest = -67876 case invalidIndexInfo = -67877 case invalidNewOwner = -67878 case invalidModifyMode = -67879 case missingRequiredExtension = -67880 case extendedKeyUsageNotCritical = -67881 case timestampMissing = -67882 case timestampInvalid = -67883 case timestampNotTrusted = -67884 case timestampServiceNotAvailable = -67885 case timestampBadAlg = -67886 case timestampBadRequest = -67887 case timestampBadDataFormat = -67888 case timestampTimeNotAvailable = -67889 case timestampUnacceptedPolicy = -67890 case timestampUnacceptedExtension = -67891 case timestampAddInfoNotAvailable = -67892 case timestampSystemFailure = -67893 case signingTimeMissing = -67894 case timestampRejection = -67895 case timestampWaiting = -67896 case timestampRevocationWarning = -67897 case timestampRevocationNotification = -67898 case unexpectedError = -99999 } extension Status: RawRepresentable, CustomStringConvertible { public init(status: OSStatus) { if let mappedStatus = Status(rawValue: status) { self = mappedStatus } else { self = .unexpectedError } } public var description: String { switch self { case .success: return "No error." case .unimplemented: return "Function or operation not implemented." case .diskFull: return "The disk is full." case .io: return "I/O error (bummers)" case .opWr: return "file already open with with write permission" case .param: return "One or more parameters passed to a function were not valid." case .wrPerm: return "write permissions error" case .allocate: return "Failed to allocate memory." case .userCanceled: return "User canceled the operation." case .badReq: return "Bad parameter or invalid state for operation." case .internalComponent: return "" case .notAvailable: return "No keychain is available. You may need to restart your computer." case .readOnly: return "This keychain cannot be modified." case .authFailed: return "The user name or passphrase you entered is not correct." case .noSuchKeychain: return "The specified keychain could not be found." case .invalidKeychain: return "The specified keychain is not a valid keychain file." case .duplicateKeychain: return "A keychain with the same name already exists." case .duplicateCallback: return "The specified callback function is already installed." case .invalidCallback: return "The specified callback function is not valid." case .duplicateItem: return "The specified item already exists in the keychain." case .itemNotFound: return "The specified item could not be found in the keychain." case .bufferTooSmall: return "There is not enough memory available to use the specified item." case .dataTooLarge: return "This item contains information which is too large or in a format that cannot be displayed." case .noSuchAttr: return "The specified attribute does not exist." case .invalidItemRef: return "The specified item is no longer valid. It may have been deleted from the keychain." case .invalidSearchRef: return "Unable to search the current keychain." case .noSuchClass: return "The specified item does not appear to be a valid keychain item." case .noDefaultKeychain: return "A default keychain could not be found." case .interactionNotAllowed: return "User interaction is not allowed." case .readOnlyAttr: return "The specified attribute could not be modified." case .wrongSecVersion: return "This keychain was created by a different version of the system software and cannot be opened." case .keySizeNotAllowed: return "This item specifies a key size which is too large." case .noStorageModule: return "A required component (data storage module) could not be loaded. You may need to restart your computer." case .noCertificateModule: return "A required component (certificate module) could not be loaded. You may need to restart your computer." case .noPolicyModule: return "A required component (policy module) could not be loaded. You may need to restart your computer." case .interactionRequired: return "User interaction is required, but is currently not allowed." case .dataNotAvailable: return "The contents of this item cannot be retrieved." case .dataNotModifiable: return "The contents of this item cannot be modified." case .createChainFailed: return "One or more certificates required to validate this certificate cannot be found." case .invalidPrefsDomain: return "The specified preferences domain is not valid." case .inDarkWake: return "In dark wake, no UI possible" case .aclNotSimple: return "The specified access control list is not in standard (simple) form." case .policyNotFound: return "The specified policy cannot be found." case .invalidTrustSetting: return "The specified trust setting is invalid." case .noAccessForItem: return "The specified item has no access control." case .invalidOwnerEdit: return "Invalid attempt to change the owner of this item." case .trustNotAvailable: return "No trust results are available." case .unsupportedFormat: return "Import/Export format unsupported." case .unknownFormat: return "Unknown format in import." case .keyIsSensitive: return "Key material must be wrapped for export." case .multiplePrivKeys: return "An attempt was made to import multiple private keys." case .passphraseRequired: return "Passphrase is required for import/export." case .invalidPasswordRef: return "The password reference was invalid." case .invalidTrustSettings: return "The Trust Settings Record was corrupted." case .noTrustSettings: return "No Trust Settings were found." case .pkcs12VerifyFailure: return "MAC verification failed during PKCS12 import (wrong password?)" case .invalidCertificate: return "This certificate could not be decoded." case .notSigner: return "A certificate was not signed by its proposed parent." case .policyDenied: return "The certificate chain was not trusted due to a policy not accepting it." case .invalidKey: return "The provided key material was not valid." case .decode: return "Unable to decode the provided data." case .`internal`: return "An internal error occurred in the Security framework." case .unsupportedAlgorithm: return "An unsupported algorithm was encountered." case .unsupportedOperation: return "The operation you requested is not supported by this key." case .unsupportedPadding: return "The padding you requested is not supported." case .itemInvalidKey: return "A string key in dictionary is not one of the supported keys." case .itemInvalidKeyType: return "A key in a dictionary is neither a CFStringRef nor a CFNumberRef." case .itemInvalidValue: return "A value in a dictionary is an invalid (or unsupported) CF type." case .itemClassMissing: return "No kSecItemClass key was specified in a dictionary." case .itemMatchUnsupported: return "The caller passed one or more kSecMatch keys to a function which does not support matches." case .useItemListUnsupported: return "The caller passed in a kSecUseItemList key to a function which does not support it." case .useKeychainUnsupported: return "The caller passed in a kSecUseKeychain key to a function which does not support it." case .useKeychainListUnsupported: return "The caller passed in a kSecUseKeychainList key to a function which does not support it." case .returnDataUnsupported: return "The caller passed in a kSecReturnData key to a function which does not support it." case .returnAttributesUnsupported: return "The caller passed in a kSecReturnAttributes key to a function which does not support it." case .returnRefUnsupported: return "The caller passed in a kSecReturnRef key to a function which does not support it." case .returnPersitentRefUnsupported: return "The caller passed in a kSecReturnPersistentRef key to a function which does not support it." case .valueRefUnsupported: return "The caller passed in a kSecValueRef key to a function which does not support it." case .valuePersistentRefUnsupported: return "The caller passed in a kSecValuePersistentRef key to a function which does not support it." case .returnMissingPointer: return "The caller passed asked for something to be returned but did not pass in a result pointer." case .matchLimitUnsupported: return "The caller passed in a kSecMatchLimit key to a call which does not support limits." case .itemIllegalQuery: return "The caller passed in a query which contained too many keys." case .waitForCallback: return "This operation is incomplete, until the callback is invoked (not an error)." case .missingEntitlement: return "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements." case .upgradePending: return "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command." case .mpSignatureInvalid: return "Signature invalid on MP message" case .otrTooOld: return "Message is too old to use" case .otrIDTooNew: return "Key ID is too new to use! Message from the future?" case .serviceNotAvailable: return "The required service is not available." case .insufficientClientID: return "The client ID is not correct." case .deviceReset: return "A device reset has occurred." case .deviceFailed: return "A device failure has occurred." case .appleAddAppACLSubject: return "Adding an application ACL subject failed." case .applePublicKeyIncomplete: return "The public key is incomplete." case .appleSignatureMismatch: return "A signature mismatch has occurred." case .appleInvalidKeyStartDate: return "The specified key has an invalid start date." case .appleInvalidKeyEndDate: return "The specified key has an invalid end date." case .conversionError: return "A conversion error has occurred." case .appleSSLv2Rollback: return "A SSLv2 rollback error has occurred." case .quotaExceeded: return "The quota was exceeded." case .fileTooBig: return "The file is too big." case .invalidDatabaseBlob: return "The specified database has an invalid blob." case .invalidKeyBlob: return "The specified database has an invalid key blob." case .incompatibleDatabaseBlob: return "The specified database has an incompatible blob." case .incompatibleKeyBlob: return "The specified database has an incompatible key blob." case .hostNameMismatch: return "A host name mismatch has occurred." case .unknownCriticalExtensionFlag: return "There is an unknown critical extension flag." case .noBasicConstraints: return "No basic constraints were found." case .noBasicConstraintsCA: return "No basic CA constraints were found." case .invalidAuthorityKeyID: return "The authority key ID is not valid." case .invalidSubjectKeyID: return "The subject key ID is not valid." case .invalidKeyUsageForPolicy: return "The key usage is not valid for the specified policy." case .invalidExtendedKeyUsage: return "The extended key usage is not valid." case .invalidIDLinkage: return "The ID linkage is not valid." case .pathLengthConstraintExceeded: return "The path length constraint was exceeded." case .invalidRoot: return "The root or anchor certificate is not valid." case .crlExpired: return "The CRL has expired." case .crlNotValidYet: return "The CRL is not yet valid." case .crlNotFound: return "The CRL was not found." case .crlServerDown: return "The CRL server is down." case .crlBadURI: return "The CRL has a bad Uniform Resource Identifier." case .unknownCertExtension: return "An unknown certificate extension was encountered." case .unknownCRLExtension: return "An unknown CRL extension was encountered." case .crlNotTrusted: return "The CRL is not trusted." case .crlPolicyFailed: return "The CRL policy failed." case .idpFailure: return "The issuing distribution point was not valid." case .smimeEmailAddressesNotFound: return "An email address mismatch was encountered." case .smimeBadExtendedKeyUsage: return "The appropriate extended key usage for SMIME was not found." case .smimeBadKeyUsage: return "The key usage is not compatible with SMIME." case .smimeKeyUsageNotCritical: return "The key usage extension is not marked as critical." case .smimeNoEmailAddress: return "No email address was found in the certificate." case .smimeSubjAltNameNotCritical: return "The subject alternative name extension is not marked as critical." case .sslBadExtendedKeyUsage: return "The appropriate extended key usage for SSL was not found." case .ocspBadResponse: return "The OCSP response was incorrect or could not be parsed." case .ocspBadRequest: return "The OCSP request was incorrect or could not be parsed." case .ocspUnavailable: return "OCSP service is unavailable." case .ocspStatusUnrecognized: return "The OCSP server did not recognize this certificate." case .endOfData: return "An end-of-data was detected." case .incompleteCertRevocationCheck: return "An incomplete certificate revocation check occurred." case .networkFailure: return "A network failure occurred." case .ocspNotTrustedToAnchor: return "The OCSP response was not trusted to a root or anchor certificate." case .recordModified: return "The record was modified." case .ocspSignatureError: return "The OCSP response had an invalid signature." case .ocspNoSigner: return "The OCSP response had no signer." case .ocspResponderMalformedReq: return "The OCSP responder was given a malformed request." case .ocspResponderInternalError: return "The OCSP responder encountered an internal error." case .ocspResponderTryLater: return "The OCSP responder is busy, try again later." case .ocspResponderSignatureRequired: return "The OCSP responder requires a signature." case .ocspResponderUnauthorized: return "The OCSP responder rejected this request as unauthorized." case .ocspResponseNonceMismatch: return "The OCSP response nonce did not match the request." case .codeSigningBadCertChainLength: return "Code signing encountered an incorrect certificate chain length." case .codeSigningNoBasicConstraints: return "Code signing found no basic constraints." case .codeSigningBadPathLengthConstraint: return "Code signing encountered an incorrect path length constraint." case .codeSigningNoExtendedKeyUsage: return "Code signing found no extended key usage." case .codeSigningDevelopment: return "Code signing indicated use of a development-only certificate." case .resourceSignBadCertChainLength: return "Resource signing has encountered an incorrect certificate chain length." case .resourceSignBadExtKeyUsage: return "Resource signing has encountered an error in the extended key usage." case .trustSettingDeny: return "The trust setting for this policy was set to Deny." case .invalidSubjectName: return "An invalid certificate subject name was encountered." case .unknownQualifiedCertStatement: return "An unknown qualified certificate statement was encountered." case .mobileMeRequestQueued: return "The MobileMe request will be sent during the next connection." case .mobileMeRequestRedirected: return "The MobileMe request was redirected." case .mobileMeServerError: return "A MobileMe server error occurred." case .mobileMeServerNotAvailable: return "The MobileMe server is not available." case .mobileMeServerAlreadyExists: return "The MobileMe server reported that the item already exists." case .mobileMeServerServiceErr: return "A MobileMe service error has occurred." case .mobileMeRequestAlreadyPending: return "A MobileMe request is already pending." case .mobileMeNoRequestPending: return "MobileMe has no request pending." case .mobileMeCSRVerifyFailure: return "A MobileMe CSR verification failure has occurred." case .mobileMeFailedConsistencyCheck: return "MobileMe has found a failed consistency check." case .notInitialized: return "A function was called without initializing CSSM." case .invalidHandleUsage: return "The CSSM handle does not match with the service type." case .pvcReferentNotFound: return "A reference to the calling module was not found in the list of authorized callers." case .functionIntegrityFail: return "A function address was not within the verified module." case .internalError: return "An internal error has occurred." case .memoryError: return "A memory error has occurred." case .invalidData: return "Invalid data was encountered." case .mdsError: return "A Module Directory Service error has occurred." case .invalidPointer: return "An invalid pointer was encountered." case .selfCheckFailed: return "Self-check has failed." case .functionFailed: return "A function has failed." case .moduleManifestVerifyFailed: return "A module manifest verification failure has occurred." case .invalidGUID: return "An invalid GUID was encountered." case .invalidHandle: return "An invalid handle was encountered." case .invalidDBList: return "An invalid DB list was encountered." case .invalidPassthroughID: return "An invalid passthrough ID was encountered." case .invalidNetworkAddress: return "An invalid network address was encountered." case .crlAlreadySigned: return "The certificate revocation list is already signed." case .invalidNumberOfFields: return "An invalid number of fields were encountered." case .verificationFailure: return "A verification failure occurred." case .unknownTag: return "An unknown tag was encountered." case .invalidSignature: return "An invalid signature was encountered." case .invalidName: return "An invalid name was encountered." case .invalidCertificateRef: return "An invalid certificate reference was encountered." case .invalidCertificateGroup: return "An invalid certificate group was encountered." case .tagNotFound: return "The specified tag was not found." case .invalidQuery: return "The specified query was not valid." case .invalidValue: return "An invalid value was detected." case .callbackFailed: return "A callback has failed." case .aclDeleteFailed: return "An ACL delete operation has failed." case .aclReplaceFailed: return "An ACL replace operation has failed." case .aclAddFailed: return "An ACL add operation has failed." case .aclChangeFailed: return "An ACL change operation has failed." case .invalidAccessCredentials: return "Invalid access credentials were encountered." case .invalidRecord: return "An invalid record was encountered." case .invalidACL: return "An invalid ACL was encountered." case .invalidSampleValue: return "An invalid sample value was encountered." case .incompatibleVersion: return "An incompatible version was encountered." case .privilegeNotGranted: return "The privilege was not granted." case .invalidScope: return "An invalid scope was encountered." case .pvcAlreadyConfigured: return "The PVC is already configured." case .invalidPVC: return "An invalid PVC was encountered." case .emmLoadFailed: return "The EMM load has failed." case .emmUnloadFailed: return "The EMM unload has failed." case .addinLoadFailed: return "The add-in load operation has failed." case .invalidKeyRef: return "An invalid key was encountered." case .invalidKeyHierarchy: return "An invalid key hierarchy was encountered." case .addinUnloadFailed: return "The add-in unload operation has failed." case .libraryReferenceNotFound: return "A library reference was not found." case .invalidAddinFunctionTable: return "An invalid add-in function table was encountered." case .invalidServiceMask: return "An invalid service mask was encountered." case .moduleNotLoaded: return "A module was not loaded." case .invalidSubServiceID: return "An invalid subservice ID was encountered." case .attributeNotInContext: return "An attribute was not in the context." case .moduleManagerInitializeFailed: return "A module failed to initialize." case .moduleManagerNotFound: return "A module was not found." case .eventNotificationCallbackNotFound: return "An event notification callback was not found." case .inputLengthError: return "An input length error was encountered." case .outputLengthError: return "An output length error was encountered." case .privilegeNotSupported: return "The privilege is not supported." case .deviceError: return "A device error was encountered." case .attachHandleBusy: return "The CSP handle was busy." case .notLoggedIn: return "You are not logged in." case .algorithmMismatch: return "An algorithm mismatch was encountered." case .keyUsageIncorrect: return "The key usage is incorrect." case .keyBlobTypeIncorrect: return "The key blob type is incorrect." case .keyHeaderInconsistent: return "The key header is inconsistent." case .unsupportedKeyFormat: return "The key header format is not supported." case .unsupportedKeySize: return "The key size is not supported." case .invalidKeyUsageMask: return "The key usage mask is not valid." case .unsupportedKeyUsageMask: return "The key usage mask is not supported." case .invalidKeyAttributeMask: return "The key attribute mask is not valid." case .unsupportedKeyAttributeMask: return "The key attribute mask is not supported." case .invalidKeyLabel: return "The key label is not valid." case .unsupportedKeyLabel: return "The key label is not supported." case .invalidKeyFormat: return "The key format is not valid." case .unsupportedVectorOfBuffers: return "The vector of buffers is not supported." case .invalidInputVector: return "The input vector is not valid." case .invalidOutputVector: return "The output vector is not valid." case .invalidContext: return "An invalid context was encountered." case .invalidAlgorithm: return "An invalid algorithm was encountered." case .invalidAttributeKey: return "A key attribute was not valid." case .missingAttributeKey: return "A key attribute was missing." case .invalidAttributeInitVector: return "An init vector attribute was not valid." case .missingAttributeInitVector: return "An init vector attribute was missing." case .invalidAttributeSalt: return "A salt attribute was not valid." case .missingAttributeSalt: return "A salt attribute was missing." case .invalidAttributePadding: return "A padding attribute was not valid." case .missingAttributePadding: return "A padding attribute was missing." case .invalidAttributeRandom: return "A random number attribute was not valid." case .missingAttributeRandom: return "A random number attribute was missing." case .invalidAttributeSeed: return "A seed attribute was not valid." case .missingAttributeSeed: return "A seed attribute was missing." case .invalidAttributePassphrase: return "A passphrase attribute was not valid." case .missingAttributePassphrase: return "A passphrase attribute was missing." case .invalidAttributeKeyLength: return "A key length attribute was not valid." case .missingAttributeKeyLength: return "A key length attribute was missing." case .invalidAttributeBlockSize: return "A block size attribute was not valid." case .missingAttributeBlockSize: return "A block size attribute was missing." case .invalidAttributeOutputSize: return "An output size attribute was not valid." case .missingAttributeOutputSize: return "An output size attribute was missing." case .invalidAttributeRounds: return "The number of rounds attribute was not valid." case .missingAttributeRounds: return "The number of rounds attribute was missing." case .invalidAlgorithmParms: return "An algorithm parameters attribute was not valid." case .missingAlgorithmParms: return "An algorithm parameters attribute was missing." case .invalidAttributeLabel: return "A label attribute was not valid." case .missingAttributeLabel: return "A label attribute was missing." case .invalidAttributeKeyType: return "A key type attribute was not valid." case .missingAttributeKeyType: return "A key type attribute was missing." case .invalidAttributeMode: return "A mode attribute was not valid." case .missingAttributeMode: return "A mode attribute was missing." case .invalidAttributeEffectiveBits: return "An effective bits attribute was not valid." case .missingAttributeEffectiveBits: return "An effective bits attribute was missing." case .invalidAttributeStartDate: return "A start date attribute was not valid." case .missingAttributeStartDate: return "A start date attribute was missing." case .invalidAttributeEndDate: return "An end date attribute was not valid." case .missingAttributeEndDate: return "An end date attribute was missing." case .invalidAttributeVersion: return "A version attribute was not valid." case .missingAttributeVersion: return "A version attribute was missing." case .invalidAttributePrime: return "A prime attribute was not valid." case .missingAttributePrime: return "A prime attribute was missing." case .invalidAttributeBase: return "A base attribute was not valid." case .missingAttributeBase: return "A base attribute was missing." case .invalidAttributeSubprime: return "A subprime attribute was not valid." case .missingAttributeSubprime: return "A subprime attribute was missing." case .invalidAttributeIterationCount: return "An iteration count attribute was not valid." case .missingAttributeIterationCount: return "An iteration count attribute was missing." case .invalidAttributeDLDBHandle: return "A database handle attribute was not valid." case .missingAttributeDLDBHandle: return "A database handle attribute was missing." case .invalidAttributeAccessCredentials: return "An access credentials attribute was not valid." case .missingAttributeAccessCredentials: return "An access credentials attribute was missing." case .invalidAttributePublicKeyFormat: return "A public key format attribute was not valid." case .missingAttributePublicKeyFormat: return "A public key format attribute was missing." case .invalidAttributePrivateKeyFormat: return "A private key format attribute was not valid." case .missingAttributePrivateKeyFormat: return "A private key format attribute was missing." case .invalidAttributeSymmetricKeyFormat: return "A symmetric key format attribute was not valid." case .missingAttributeSymmetricKeyFormat: return "A symmetric key format attribute was missing." case .invalidAttributeWrappedKeyFormat: return "A wrapped key format attribute was not valid." case .missingAttributeWrappedKeyFormat: return "A wrapped key format attribute was missing." case .stagedOperationInProgress: return "A staged operation is in progress." case .stagedOperationNotStarted: return "A staged operation was not started." case .verifyFailed: return "A cryptographic verification failure has occurred." case .querySizeUnknown: return "The query size is unknown." case .blockSizeMismatch: return "A block size mismatch occurred." case .publicKeyInconsistent: return "The public key was inconsistent." case .deviceVerifyFailed: return "A device verification failure has occurred." case .invalidLoginName: return "An invalid login name was detected." case .alreadyLoggedIn: return "The user is already logged in." case .invalidDigestAlgorithm: return "An invalid digest algorithm was detected." case .invalidCRLGroup: return "An invalid CRL group was detected." case .certificateCannotOperate: return "The certificate cannot operate." case .certificateExpired: return "An expired certificate was detected." case .certificateNotValidYet: return "The certificate is not yet valid." case .certificateRevoked: return "The certificate was revoked." case .certificateSuspended: return "The certificate was suspended." case .insufficientCredentials: return "Insufficient credentials were detected." case .invalidAction: return "The action was not valid." case .invalidAuthority: return "The authority was not valid." case .verifyActionFailed: return "A verify action has failed." case .invalidCertAuthority: return "The certificate authority was not valid." case .invaldCRLAuthority: return "The CRL authority was not valid." case .invalidCRLEncoding: return "The CRL encoding was not valid." case .invalidCRLType: return "The CRL type was not valid." case .invalidCRL: return "The CRL was not valid." case .invalidFormType: return "The form type was not valid." case .invalidID: return "The ID was not valid." case .invalidIdentifier: return "The identifier was not valid." case .invalidIndex: return "The index was not valid." case .invalidPolicyIdentifiers: return "The policy identifiers are not valid." case .invalidTimeString: return "The time specified was not valid." case .invalidReason: return "The trust policy reason was not valid." case .invalidRequestInputs: return "The request inputs are not valid." case .invalidResponseVector: return "The response vector was not valid." case .invalidStopOnPolicy: return "The stop-on policy was not valid." case .invalidTuple: return "The tuple was not valid." case .multipleValuesUnsupported: return "Multiple values are not supported." case .notTrusted: return "The trust policy was not trusted." case .noDefaultAuthority: return "No default authority was detected." case .rejectedForm: return "The trust policy had a rejected form." case .requestLost: return "The request was lost." case .requestRejected: return "The request was rejected." case .unsupportedAddressType: return "The address type is not supported." case .unsupportedService: return "The service is not supported." case .invalidTupleGroup: return "The tuple group was not valid." case .invalidBaseACLs: return "The base ACLs are not valid." case .invalidTupleCredendtials: return "The tuple credentials are not valid." case .invalidEncoding: return "The encoding was not valid." case .invalidValidityPeriod: return "The validity period was not valid." case .invalidRequestor: return "The requestor was not valid." case .requestDescriptor: return "The request descriptor was not valid." case .invalidBundleInfo: return "The bundle information was not valid." case .invalidCRLIndex: return "The CRL index was not valid." case .noFieldValues: return "No field values were detected." case .unsupportedFieldFormat: return "The field format is not supported." case .unsupportedIndexInfo: return "The index information is not supported." case .unsupportedLocality: return "The locality is not supported." case .unsupportedNumAttributes: return "The number of attributes is not supported." case .unsupportedNumIndexes: return "The number of indexes is not supported." case .unsupportedNumRecordTypes: return "The number of record types is not supported." case .fieldSpecifiedMultiple: return "Too many fields were specified." case .incompatibleFieldFormat: return "The field format was incompatible." case .invalidParsingModule: return "The parsing module was not valid." case .databaseLocked: return "The database is locked." case .datastoreIsOpen: return "The data store is open." case .missingValue: return "A missing value was detected." case .unsupportedQueryLimits: return "The query limits are not supported." case .unsupportedNumSelectionPreds: return "The number of selection predicates is not supported." case .unsupportedOperator: return "The operator is not supported." case .invalidDBLocation: return "The database location is not valid." case .invalidAccessRequest: return "The access request is not valid." case .invalidIndexInfo: return "The index information is not valid." case .invalidNewOwner: return "The new owner is not valid." case .invalidModifyMode: return "The modify mode is not valid." case .missingRequiredExtension: return "A required certificate extension is missing." case .extendedKeyUsageNotCritical: return "The extended key usage extension was not marked critical." case .timestampMissing: return "A timestamp was expected but was not found." case .timestampInvalid: return "The timestamp was not valid." case .timestampNotTrusted: return "The timestamp was not trusted." case .timestampServiceNotAvailable: return "The timestamp service is not available." case .timestampBadAlg: return "An unrecognized or unsupported Algorithm Identifier in timestamp." case .timestampBadRequest: return "The timestamp transaction is not permitted or supported." case .timestampBadDataFormat: return "The timestamp data submitted has the wrong format." case .timestampTimeNotAvailable: return "The time source for the Timestamp Authority is not available." case .timestampUnacceptedPolicy: return "The requested policy is not supported by the Timestamp Authority." case .timestampUnacceptedExtension: return "The requested extension is not supported by the Timestamp Authority." case .timestampAddInfoNotAvailable: return "The additional information requested is not available." case .timestampSystemFailure: return "The timestamp request cannot be handled due to system failure." case .signingTimeMissing: return "A signing time was expected but was not found." case .timestampRejection: return "A timestamp transaction was rejected." case .timestampWaiting: return "A timestamp transaction is waiting." case .timestampRevocationWarning: return "A timestamp authority revocation warning was issued." case .timestampRevocationNotification: return "A timestamp authority revocation notification was issued." case .unexpectedError: return "Unexpected error has occurred." } } } extension Status: CustomNSError { public static let errorDomain = KeychainAccessErrorDomain public var errorCode: Int { return Int(rawValue) } public var errorUserInfo: [String : Any] { return [NSLocalizedDescriptionKey: description] } } ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess/KeychainAccess.h ================================================ // // KeychainAccess.h // KeychainAccess // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import FOUNDATION_EXPORT double KeychainAccessVersionNumber; FOUNDATION_EXPORT const unsigned char KeychainAccessVersionString[]; ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 140F19621A49D79400B0016A /* KeychainAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = 140F19611A49D79400B0016A /* KeychainAccess.h */; settings = {ATTRIBUTES = (Public, ); }; }; 140F196F1A49D79500B0016A /* KeychainAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */; }; 140F197B1A49D89200B0016A /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140F197A1A49D89200B0016A /* Keychain.swift */; }; 142EDA851BCB505F00A32149 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */; }; 142EDB041BCBB0DD00A32149 /* SharedCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */; }; 148F9D4A1BCB4118006EDF48 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148F9D491BCB4118006EDF48 /* EnumTests.swift */; }; 14A630181D3293C700809B3F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A630171D3293C700809B3F /* AppDelegate.swift */; }; 14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A6301E1D3293C700809B3F /* Assets.xcassets */; }; 14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; }; 14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 140F19691A49D79500B0016A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 140F195B1A49D79400B0016A; remoteInfo = KeychainAccess; }; 14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 140F195B1A49D79400B0016A; remoteInfo = KeychainAccess; }; 14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 140F19531A49D79400B0016A /* Project object */; proxyType = 1; remoteGlobalIDString = 14A630141D3293C700809B3F; remoteInfo = TestHost; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14C3A67C1D32BF9D00349459 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 140F195C1A49D79400B0016A /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 140F19601A49D79400B0016A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140F19611A49D79400B0016A /* KeychainAccess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeychainAccess.h; sourceTree = ""; }; 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainAccessTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 140F196D1A49D79500B0016A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessTests.swift; sourceTree = ""; }; 140F197A1A49D89200B0016A /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTypeTests.swift; sourceTree = ""; }; 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedCredentialTests.swift; sourceTree = ""; }; 148E44E51BF9EDCB004FFEC1 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Base.xcconfig; path = Configurations/Base.xcconfig; sourceTree = ""; }; 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configurations/Debug.xcconfig; sourceTree = ""; }; 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configurations/Release.xcconfig; sourceTree = ""; }; 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Tests.xcconfig; path = Configurations/Tests.xcconfig; sourceTree = ""; }; 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = KeychainAccess.xcconfig; path = Configurations/KeychainAccess.xcconfig; sourceTree = ""; }; 148F9D491BCB4118006EDF48 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = ""; }; 14A630151D3293C700809B3F /* TestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; 14A630171D3293C700809B3F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14A6301E1D3293C700809B3F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 14A630231D3293C700809B3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14F0C1961D3295C4007DCDDB /* TestHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = TestHost.entitlements; sourceTree = ""; }; 14F0C1981D329832007DCDDB /* TestHost.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = TestHost.xcconfig; path = Configurations/TestHost.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 140F19581A49D79400B0016A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 140F19641A49D79500B0016A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 14A630121D3293C700809B3F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 140F19521A49D79400B0016A = { isa = PBXGroup; children = ( 140F195E1A49D79400B0016A /* KeychainAccess */, 140F196B1A49D79500B0016A /* KeychainAccessTests */, 148E44E41BF9ED6D004FFEC1 /* Cofigurations */, 14A630161D3293C700809B3F /* TestHost */, 140F195D1A49D79400B0016A /* Products */, ); sourceTree = ""; }; 140F195D1A49D79400B0016A /* Products */ = { isa = PBXGroup; children = ( 140F195C1A49D79400B0016A /* KeychainAccess.framework */, 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */, 14A630151D3293C700809B3F /* TestHost.app */, ); name = Products; sourceTree = ""; }; 140F195E1A49D79400B0016A /* KeychainAccess */ = { isa = PBXGroup; children = ( 140F19611A49D79400B0016A /* KeychainAccess.h */, 140F197A1A49D89200B0016A /* Keychain.swift */, 140F195F1A49D79400B0016A /* Supporting Files */, ); path = KeychainAccess; sourceTree = ""; }; 140F195F1A49D79400B0016A /* Supporting Files */ = { isa = PBXGroup; children = ( 140F19601A49D79400B0016A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 140F196B1A49D79500B0016A /* KeychainAccessTests */ = { isa = PBXGroup; children = ( 140F196E1A49D79500B0016A /* KeychainAccessTests.swift */, 148F9D491BCB4118006EDF48 /* EnumTests.swift */, 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */, 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */, 140F196C1A49D79500B0016A /* Supporting Files */, ); path = KeychainAccessTests; sourceTree = ""; }; 140F196C1A49D79500B0016A /* Supporting Files */ = { isa = PBXGroup; children = ( 140F196D1A49D79500B0016A /* Info.plist */, ); name = "Supporting Files"; sourceTree = ""; }; 148E44E41BF9ED6D004FFEC1 /* Cofigurations */ = { isa = PBXGroup; children = ( 148E44E51BF9EDCB004FFEC1 /* Base.xcconfig */, 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */, 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */, 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */, 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */, 14F0C1981D329832007DCDDB /* TestHost.xcconfig */, ); name = Cofigurations; sourceTree = ""; }; 14A630161D3293C700809B3F /* TestHost */ = { isa = PBXGroup; children = ( 14A630171D3293C700809B3F /* AppDelegate.swift */, 14A6301E1D3293C700809B3F /* Assets.xcassets */, 14A630231D3293C700809B3F /* Info.plist */, 14F0C1961D3295C4007DCDDB /* TestHost.entitlements */, ); path = TestHost; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 140F19591A49D79400B0016A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 140F19621A49D79400B0016A /* KeychainAccess.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 140F195B1A49D79400B0016A /* KeychainAccess */ = { isa = PBXNativeTarget; buildConfigurationList = 140F19721A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccess" */; buildPhases = ( 140F19571A49D79400B0016A /* Sources */, 140F19581A49D79400B0016A /* Frameworks */, 140F19591A49D79400B0016A /* Headers */, 140F195A1A49D79400B0016A /* Resources */, ); buildRules = ( ); dependencies = ( ); name = KeychainAccess; productName = KeychainAccess; productReference = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; productType = "com.apple.product-type.framework"; }; 140F19661A49D79500B0016A /* KeychainAccessTests */ = { isa = PBXNativeTarget; buildConfigurationList = 140F19751A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccessTests" */; buildPhases = ( 140F19631A49D79500B0016A /* Sources */, 140F19641A49D79500B0016A /* Frameworks */, 140F19651A49D79500B0016A /* Resources */, ); buildRules = ( ); dependencies = ( 140F196A1A49D79500B0016A /* PBXTargetDependency */, 14F0C19A1D32A160007DCDDB /* PBXTargetDependency */, ); name = KeychainAccessTests; productName = KeychainAccessTests; productReference = 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 14A630141D3293C700809B3F /* TestHost */ = { isa = PBXNativeTarget; buildConfigurationList = 14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */; buildPhases = ( 14A630111D3293C700809B3F /* Sources */, 14A630121D3293C700809B3F /* Frameworks */, 14A630131D3293C700809B3F /* Resources */, 14C3A67C1D32BF9D00349459 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 14C3A67B1D32BF9C00349459 /* PBXTargetDependency */, ); name = TestHost; productName = TestHost; productReference = 14A630151D3293C700809B3F /* TestHost.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 140F19531A49D79400B0016A /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "kishikawa katsumi"; TargetAttributes = { 140F195B1A49D79400B0016A = { CreatedOnToolsVersion = 6.1.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; 140F19661A49D79500B0016A = { CreatedOnToolsVersion = 6.1.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; TestTargetID = 14A62FFC1D32922C00809B3F; }; 14A630141D3293C700809B3F = { CreatedOnToolsVersion = 7.3.1; DevelopmentTeam = 27AEDK3C9F; LastSwiftMigration = 1020; SystemCapabilities = { com.apple.Keychain = { enabled = 1; }; }; }; }; }; buildConfigurationList = 140F19561A49D79400B0016A /* Build configuration list for PBXProject "KeychainAccess" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 140F19521A49D79400B0016A; productRefGroup = 140F195D1A49D79400B0016A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 140F195B1A49D79400B0016A /* KeychainAccess */, 140F19661A49D79500B0016A /* KeychainAccessTests */, 14A630141D3293C700809B3F /* TestHost */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 140F195A1A49D79400B0016A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 140F19651A49D79500B0016A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 14A630131D3293C700809B3F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 140F19571A49D79400B0016A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 140F197B1A49D89200B0016A /* Keychain.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 140F19631A49D79500B0016A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 140F196F1A49D79500B0016A /* KeychainAccessTests.swift in Sources */, 148F9D4A1BCB4118006EDF48 /* EnumTests.swift in Sources */, 142EDA851BCB505F00A32149 /* ErrorTypeTests.swift in Sources */, 142EDB041BCBB0DD00A32149 /* SharedCredentialTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A630111D3293C700809B3F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A630181D3293C700809B3F /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 140F196A1A49D79500B0016A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 140F195B1A49D79400B0016A /* KeychainAccess */; targetProxy = 140F19691A49D79500B0016A /* PBXContainerItemProxy */; }; 14C3A67B1D32BF9C00349459 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 140F195B1A49D79400B0016A /* KeychainAccess */; targetProxy = 14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */; }; 14F0C19A1D32A160007DCDDB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 14A630141D3293C700809B3F /* TestHost */; targetProxy = 14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 140F19701A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E61BF9EDCB004FFEC1 /* Debug.xcconfig */; buildSettings = { }; name = Debug; }; 140F19711A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */; buildSettings = { }; name = Release; }; 140F19731A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */; buildSettings = { }; name = Debug; }; 140F19741A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */; buildSettings = { }; name = Release; }; 140F19761A49D79500B0016A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */; buildSettings = { }; name = Debug; }; 140F19771A49D79500B0016A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */; buildSettings = { }; name = Release; }; 14A630251D3293C700809B3F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */; buildSettings = { }; name = Debug; }; 14A630261D3293C700809B3F /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */; buildSettings = { }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 140F19561A49D79400B0016A /* Build configuration list for PBXProject "KeychainAccess" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19701A49D79500B0016A /* Debug */, 140F19711A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 140F19721A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccess" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19731A49D79500B0016A /* Debug */, 140F19741A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 140F19751A49D79500B0016A /* Build configuration list for PBXNativeTarget "KeychainAccessTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 140F19761A49D79500B0016A /* Debug */, 140F19771A49D79500B0016A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A630251D3293C700809B3F /* Debug */, 14A630261D3293C700809B3F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 140F19531A49D79400B0016A /* Project object */; } ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/KeychainAccess.xcscheme ================================================ ================================================ FILE: External/KeychainAccess/Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme ================================================ ================================================ FILE: External/KeychainAccess/Lib/KeychainAccessTests/EnumTests.swift ================================================ // // EnumTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class EnumTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testItemClass() { do { let itemClass = ItemClass(rawValue: kSecClassGenericPassword as String) XCTAssertEqual(itemClass, .genericPassword) XCTAssertEqual(itemClass?.description, "GenericPassword") } do { let itemClass = ItemClass(rawValue: kSecClassInternetPassword as String) XCTAssertEqual(itemClass, .internetPassword) XCTAssertEqual(itemClass?.description, "InternetPassword") } do { let itemClass = ItemClass(rawValue: kSecClassCertificate as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassKey as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassIdentity as String) XCTAssertNil(itemClass) } } func testProtocolType() { do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTP as String) XCTAssertEqual(protocolType, .ftp) XCTAssertEqual(protocolType?.description, "FTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPAccount as String) XCTAssertEqual(protocolType, .ftpAccount) XCTAssertEqual(protocolType?.description, "FTPAccount") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTP as String) XCTAssertEqual(protocolType, .http) XCTAssertEqual(protocolType?.description, "HTTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRC as String) XCTAssertEqual(protocolType, .irc) XCTAssertEqual(protocolType?.description, "IRC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTP as String) XCTAssertEqual(protocolType, .nntp) XCTAssertEqual(protocolType?.description, "NNTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3 as String) XCTAssertEqual(protocolType, .pop3) XCTAssertEqual(protocolType?.description, "POP3") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMTP as String) XCTAssertEqual(protocolType, .smtp) XCTAssertEqual(protocolType?.description, "SMTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSOCKS as String) XCTAssertEqual(protocolType, .socks) XCTAssertEqual(protocolType?.description, "SOCKS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAP as String) XCTAssertEqual(protocolType, .imap) XCTAssertEqual(protocolType?.description, "IMAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAP as String) XCTAssertEqual(protocolType, .ldap) XCTAssertEqual(protocolType?.description, "LDAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAppleTalk as String) XCTAssertEqual(protocolType, .appleTalk) XCTAssertEqual(protocolType?.description, "AppleTalk") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAFP as String) XCTAssertEqual(protocolType, .afp) XCTAssertEqual(protocolType?.description, "AFP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnet as String) XCTAssertEqual(protocolType, .telnet) XCTAssertEqual(protocolType?.description, "Telnet") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSSH as String) XCTAssertEqual(protocolType, .ssh) XCTAssertEqual(protocolType?.description, "SSH") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPS as String) XCTAssertEqual(protocolType, .ftps) XCTAssertEqual(protocolType?.description, "FTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPS as String) XCTAssertEqual(protocolType, .https) XCTAssertEqual(protocolType?.description, "HTTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPProxy as String) XCTAssertEqual(protocolType, .httpProxy) XCTAssertEqual(protocolType?.description, "HTTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPSProxy as String) XCTAssertEqual(protocolType, .httpsProxy) XCTAssertEqual(protocolType?.description, "HTTPSProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPProxy as String) XCTAssertEqual(protocolType, .ftpProxy) XCTAssertEqual(protocolType?.description, "FTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMB as String) XCTAssertEqual(protocolType, .smb) XCTAssertEqual(protocolType?.description, "SMB") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSP as String) XCTAssertEqual(protocolType, .rtsp) XCTAssertEqual(protocolType?.description, "RTSP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSPProxy as String) XCTAssertEqual(protocolType, .rtspProxy) XCTAssertEqual(protocolType?.description, "RTSPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolDAAP as String) XCTAssertEqual(protocolType, .daap) XCTAssertEqual(protocolType?.description, "DAAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolEPPC as String) XCTAssertEqual(protocolType, .eppc) XCTAssertEqual(protocolType?.description, "EPPC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIPP as String) XCTAssertEqual(protocolType, .ipp) XCTAssertEqual(protocolType?.description, "IPP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTPS as String) XCTAssertEqual(protocolType, .nntps) XCTAssertEqual(protocolType?.description, "NNTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAPS as String) XCTAssertEqual(protocolType, .ldaps) XCTAssertEqual(protocolType?.description, "LDAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnetS as String) XCTAssertEqual(protocolType, .telnetS) XCTAssertEqual(protocolType?.description, "TelnetS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAPS as String) XCTAssertEqual(protocolType, .imaps) XCTAssertEqual(protocolType?.description, "IMAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRCS as String) XCTAssertEqual(protocolType, .ircs) XCTAssertEqual(protocolType?.description, "IRCS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3S as String) XCTAssertEqual(protocolType, .pop3S) XCTAssertEqual(protocolType?.description, "POP3S") } } func testAuthenticationType() { do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeNTLM as String) XCTAssertEqual(authenticationType, .ntlm) XCTAssertEqual(authenticationType?.description, "NTLM") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeMSN as String) XCTAssertEqual(authenticationType, .msn) XCTAssertEqual(authenticationType?.description, "MSN") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDPA as String) XCTAssertEqual(authenticationType, .dpa) XCTAssertEqual(authenticationType?.description, "DPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeRPA as String) XCTAssertEqual(authenticationType, .rpa) XCTAssertEqual(authenticationType?.description, "RPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPBasic as String) XCTAssertEqual(authenticationType, .httpBasic) XCTAssertEqual(authenticationType?.description, "HTTPBasic") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPDigest as String) XCTAssertEqual(authenticationType, .httpDigest) XCTAssertEqual(authenticationType?.description, "HTTPDigest") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTMLForm as String) XCTAssertEqual(authenticationType, .htmlForm) XCTAssertEqual(authenticationType?.description, "HTMLForm") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDefault as String) XCTAssertEqual(authenticationType, .default) XCTAssertEqual(authenticationType?.description, "Default") } } func testAccessibility() { guard #available(OSX 10.10, *) else { return } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlocked as String) XCTAssertEqual(accessibility, .whenUnlocked) XCTAssertEqual(accessibility?.description, "WhenUnlocked") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlock as String) XCTAssertEqual(accessibility, .afterFirstUnlock) XCTAssertEqual(accessibility?.description, "AfterFirstUnlock") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlways as String) XCTAssertEqual(accessibility, .always) XCTAssertEqual(accessibility?.description, "Always") } #endif do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenPasscodeSetThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenPasscodeSetThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlockedThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenUnlockedThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenUnlockedThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly as String) XCTAssertEqual(accessibility, .afterFirstUnlockThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AfterFirstUnlockThisDeviceOnly") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlwaysThisDeviceOnly as String) XCTAssertEqual(accessibility, .alwaysThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AlwaysThisDeviceOnly") } #endif } } ================================================ FILE: External/KeychainAccess/Lib/KeychainAccessTests/ErrorTypeTests.swift ================================================ // // ErrorTypeTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class ErrorTypeTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testErrorType() { do { let status = Status(rawValue: errSecSuccess) XCTAssertEqual(status, .success) XCTAssertEqual(status?.description, "No error.") } do { let status = Status(rawValue: errSecUnimplemented) XCTAssertEqual(status, .unimplemented) XCTAssertEqual(status?.description, "Function or operation not implemented.") } #if os(OSX) do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } #endif do { let status = Status(rawValue: errSecIO) XCTAssertEqual(status, .io) XCTAssertEqual(status?.description, "I/O error (bummers)") } #if os(iOS) do { let status = Status(rawValue: errSecOpWr) XCTAssertEqual(status, .opWr) XCTAssertEqual(status?.description, "file already open with with write permission") } #endif do { let status = Status(rawValue: errSecParam) XCTAssertEqual(status, .param) XCTAssertEqual(status?.description, "One or more parameters passed to a function were not valid.") } #if os(OSX) do { let status = Status(rawValue: errSecWrPerm) XCTAssertEqual(status, .wrPerm) XCTAssertEqual(status?.description, "write permissions error") } #endif do { let status = Status(rawValue: errSecAllocate) XCTAssertEqual(status, .allocate) XCTAssertEqual(status?.description, "Failed to allocate memory.") } do { let status = Status(rawValue: errSecUserCanceled) XCTAssertEqual(status, .userCanceled) XCTAssertEqual(status?.description, "User canceled the operation.") } do { let status = Status(rawValue: errSecBadReq) XCTAssertEqual(status, .badReq) XCTAssertEqual(status?.description, "Bad parameter or invalid state for operation.") } do { let status = Status(rawValue: errSecInternalComponent) XCTAssertEqual(status, .internalComponent) XCTAssertEqual(status?.description, "") } do { let status = Status(rawValue: errSecNotAvailable) XCTAssertEqual(status, .notAvailable) XCTAssertEqual(status?.description, "No keychain is available. You may need to restart your computer.") } #if os(OSX) do { let status = Status(rawValue: errSecReadOnly) XCTAssertEqual(status, .readOnly) XCTAssertEqual(status?.description, "This keychain cannot be modified.") } #endif do { let status = Status(rawValue: errSecAuthFailed) XCTAssertEqual(status, .authFailed) XCTAssertEqual(status?.description, "The user name or passphrase you entered is not correct.") } #if os(OSX) do { let status = Status(rawValue: errSecNoSuchKeychain) XCTAssertEqual(status, .noSuchKeychain) XCTAssertEqual(status?.description, "The specified keychain could not be found.") } do { let status = Status(rawValue: errSecInvalidKeychain) XCTAssertEqual(status, .invalidKeychain) XCTAssertEqual(status?.description, "The specified keychain is not a valid keychain file.") } do { let status = Status(rawValue: errSecDuplicateKeychain) XCTAssertEqual(status, .duplicateKeychain) XCTAssertEqual(status?.description, "A keychain with the same name already exists.") } do { let status = Status(rawValue: errSecDuplicateCallback) XCTAssertEqual(status, .duplicateCallback) XCTAssertEqual(status?.description, "The specified callback function is already installed.") } do { let status = Status(rawValue: errSecInvalidCallback) XCTAssertEqual(status, .invalidCallback) XCTAssertEqual(status?.description, "The specified callback function is not valid.") } #endif do { let status = Status(rawValue: errSecDuplicateItem) XCTAssertEqual(status, .duplicateItem) XCTAssertEqual(status?.description, "The specified item already exists in the keychain.") } do { let status = Status(rawValue: errSecItemNotFound) XCTAssertEqual(status, .itemNotFound) XCTAssertEqual(status?.description, "The specified item could not be found in the keychain.") } #if os(OSX) do { let status = Status(rawValue: errSecBufferTooSmall) XCTAssertEqual(status, .bufferTooSmall) XCTAssertEqual(status?.description, "There is not enough memory available to use the specified item.") } do { let status = Status(rawValue: errSecDataTooLarge) XCTAssertEqual(status, .dataTooLarge) XCTAssertEqual(status?.description, "This item contains information which is too large or in a format that cannot be displayed.") } do { let status = Status(rawValue: errSecNoSuchAttr) XCTAssertEqual(status, .noSuchAttr) XCTAssertEqual(status?.description, "The specified attribute does not exist.") } do { let status = Status(rawValue: errSecInvalidItemRef) XCTAssertEqual(status, .invalidItemRef) XCTAssertEqual(status?.description, "The specified item is no longer valid. It may have been deleted from the keychain.") } do { let status = Status(rawValue: errSecInvalidSearchRef) XCTAssertEqual(status, .invalidSearchRef) XCTAssertEqual(status?.description, "Unable to search the current keychain.") } do { let status = Status(rawValue: errSecNoSuchClass) XCTAssertEqual(status, .noSuchClass) XCTAssertEqual(status?.description, "The specified item does not appear to be a valid keychain item.") } do { let status = Status(rawValue: errSecNoDefaultKeychain) XCTAssertEqual(status, .noDefaultKeychain) XCTAssertEqual(status?.description, "A default keychain could not be found.") } #endif do { let status = Status(rawValue: errSecInteractionNotAllowed) XCTAssertEqual(status, .interactionNotAllowed) XCTAssertEqual(status?.description, "User interaction is not allowed.") } #if os(OSX) do { let status = Status(rawValue: errSecReadOnlyAttr) XCTAssertEqual(status, .readOnlyAttr) XCTAssertEqual(status?.description, "The specified attribute could not be modified.") } do { let status = Status(rawValue: errSecWrongSecVersion) XCTAssertEqual(status, .wrongSecVersion) XCTAssertEqual(status?.description, "This keychain was created by a different version of the system software and cannot be opened.") } do { let status = Status(rawValue: errSecKeySizeNotAllowed) XCTAssertEqual(status, .keySizeNotAllowed) XCTAssertEqual(status?.description, "This item specifies a key size which is too large.") } do { let status = Status(rawValue: errSecNoStorageModule) XCTAssertEqual(status, .noStorageModule) XCTAssertEqual(status?.description, "A required component (data storage module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoCertificateModule) XCTAssertEqual(status, .noCertificateModule) XCTAssertEqual(status?.description, "A required component (certificate module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoPolicyModule) XCTAssertEqual(status, .noPolicyModule) XCTAssertEqual(status?.description, "A required component (policy module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecInteractionRequired) XCTAssertEqual(status, .interactionRequired) XCTAssertEqual(status?.description, "User interaction is required, but is currently not allowed.") } do { let status = Status(rawValue: errSecDataNotAvailable) XCTAssertEqual(status, .dataNotAvailable) XCTAssertEqual(status?.description, "The contents of this item cannot be retrieved.") } do { let status = Status(rawValue: errSecDataNotModifiable) XCTAssertEqual(status, .dataNotModifiable) XCTAssertEqual(status?.description, "The contents of this item cannot be modified.") } do { let status = Status(rawValue: errSecCreateChainFailed) XCTAssertEqual(status, .createChainFailed) XCTAssertEqual(status?.description, "One or more certificates required to validate this certificate cannot be found.") } do { let status = Status(rawValue: errSecInvalidPrefsDomain) XCTAssertEqual(status, .invalidPrefsDomain) XCTAssertEqual(status?.description, "The specified preferences domain is not valid.") } do { let status = Status(rawValue: errSecInDarkWake) XCTAssertEqual(status, .inDarkWake) XCTAssertEqual(status?.description, "In dark wake, no UI possible") } do { let status = Status(rawValue: errSecACLNotSimple) XCTAssertEqual(status, .aclNotSimple) XCTAssertEqual(status?.description, "The specified access control list is not in standard (simple) form.") } do { let status = Status(rawValue: errSecPolicyNotFound) XCTAssertEqual(status, .policyNotFound) XCTAssertEqual(status?.description, "The specified policy cannot be found.") } do { let status = Status(rawValue: errSecInvalidTrustSetting) XCTAssertEqual(status, .invalidTrustSetting) XCTAssertEqual(status?.description, "The specified trust setting is invalid.") } do { let status = Status(rawValue: errSecNoAccessForItem) XCTAssertEqual(status, .noAccessForItem) XCTAssertEqual(status?.description, "The specified item has no access control.") } do { let status = Status(rawValue: errSecInvalidOwnerEdit) XCTAssertEqual(status, .invalidOwnerEdit) XCTAssertEqual(status?.description, "Invalid attempt to change the owner of this item.") } do { let status = Status(rawValue: errSecTrustNotAvailable) XCTAssertEqual(status, .trustNotAvailable) XCTAssertEqual(status?.description, "No trust results are available.") } do { let status = Status(rawValue: errSecUnsupportedFormat) XCTAssertEqual(status, .unsupportedFormat) XCTAssertEqual(status?.description, "Import/Export format unsupported.") } do { let status = Status(rawValue: errSecUnknownFormat) XCTAssertEqual(status, .unknownFormat) XCTAssertEqual(status?.description, "Unknown format in import.") } do { let status = Status(rawValue: errSecKeyIsSensitive) XCTAssertEqual(status, .keyIsSensitive) XCTAssertEqual(status?.description, "Key material must be wrapped for export.") } do { let status = Status(rawValue: errSecMultiplePrivKeys) XCTAssertEqual(status, .multiplePrivKeys) XCTAssertEqual(status?.description, "An attempt was made to import multiple private keys.") } do { let status = Status(rawValue: errSecPassphraseRequired) XCTAssertEqual(status, .passphraseRequired) XCTAssertEqual(status?.description, "Passphrase is required for import/export.") } do { let status = Status(rawValue: errSecInvalidPasswordRef) XCTAssertEqual(status, .invalidPasswordRef) XCTAssertEqual(status?.description, "The password reference was invalid.") } do { let status = Status(rawValue: errSecInvalidTrustSettings) XCTAssertEqual(status, .invalidTrustSettings) XCTAssertEqual(status?.description, "The Trust Settings Record was corrupted.") } do { let status = Status(rawValue: errSecNoTrustSettings) XCTAssertEqual(status, .noTrustSettings) XCTAssertEqual(status?.description, "No Trust Settings were found.") } do { let status = Status(rawValue: errSecPkcs12VerifyFailure) XCTAssertEqual(status, .pkcs12VerifyFailure) XCTAssertEqual(status?.description, "MAC verification failed during PKCS12 import (wrong password?)") } do { let errSecInvalidCertificate: OSStatus = -26265 let status = Status(rawValue: errSecInvalidCertificate) XCTAssertEqual(status, .invalidCertificate) XCTAssertEqual(status?.description, "This certificate could not be decoded.") } do { let status = Status(rawValue: errSecNotSigner) XCTAssertEqual(status, .notSigner) XCTAssertEqual(status?.description, "A certificate was not signed by its proposed parent.") } do { let errSecPolicyDenied: OSStatus = -26270 let status = Status(rawValue: errSecPolicyDenied) XCTAssertEqual(status, .policyDenied) XCTAssertEqual(status?.description, "The certificate chain was not trusted due to a policy not accepting it.") } do { let errSecInvalidKey: OSStatus = -26274 let status = Status(rawValue: errSecInvalidKey) XCTAssertEqual(status, .invalidKey) XCTAssertEqual(status?.description, "The provided key material was not valid.") } #endif do { let status = Status(rawValue: errSecDecode) XCTAssertEqual(status, .decode) XCTAssertEqual(status?.description, "Unable to decode the provided data.") } do { let errSecInternal: OSStatus = -26276 let status = Status(rawValue: errSecInternal) XCTAssertEqual(status, .internal) XCTAssertEqual(status?.description, "An internal error occurred in the Security framework.") } #if os(OSX) do { let status = Status(rawValue: errSecServiceNotAvailable) XCTAssertEqual(status, .serviceNotAvailable) XCTAssertEqual(status?.description, "The required service is not available.") } do { let errSecUnsupportedAlgorithm: OSStatus = -26268 let status = Status(rawValue: errSecUnsupportedAlgorithm) XCTAssertEqual(status, .unsupportedAlgorithm) XCTAssertEqual(status?.description, "An unsupported algorithm was encountered.") } do { let errSecUnsupportedOperation: OSStatus = -26271 let status = Status(rawValue: errSecUnsupportedOperation) XCTAssertEqual(status, .unsupportedOperation) XCTAssertEqual(status?.description, "The operation you requested is not supported by this key.") } do { let errSecUnsupportedPadding: OSStatus = -26273 let status = Status(rawValue: errSecUnsupportedPadding) XCTAssertEqual(status, .unsupportedPadding) XCTAssertEqual(status?.description, "The padding you requested is not supported.") } do { let errSecItemInvalidKey: OSStatus = -34000 let status = Status(rawValue: errSecItemInvalidKey) XCTAssertEqual(status, .itemInvalidKey) XCTAssertEqual(status?.description, "A string key in dictionary is not one of the supported keys.") } do { let errSecItemInvalidKeyType: OSStatus = -34001 let status = Status(rawValue: errSecItemInvalidKeyType) XCTAssertEqual(status, .itemInvalidKeyType) XCTAssertEqual(status?.description, "A key in a dictionary is neither a CFStringRef nor a CFNumberRef.") } do { let errSecItemInvalidValue: OSStatus = -34002 let status = Status(rawValue: errSecItemInvalidValue) XCTAssertEqual(status, .itemInvalidValue) XCTAssertEqual(status?.description, "A value in a dictionary is an invalid (or unsupported) CF type.") } do { let errSecItemClassMissing: OSStatus = -34003 let status = Status(rawValue: errSecItemClassMissing) XCTAssertEqual(status, .itemClassMissing) XCTAssertEqual(status?.description, "No kSecItemClass key was specified in a dictionary.") } do { let errSecItemMatchUnsupported: OSStatus = -34004 let status = Status(rawValue: errSecItemMatchUnsupported) XCTAssertEqual(status, .itemMatchUnsupported) XCTAssertEqual(status?.description, "The caller passed one or more kSecMatch keys to a function which does not support matches.") } do { let errSecUseItemListUnsupported: OSStatus = -34005 let status = Status(rawValue: errSecUseItemListUnsupported) XCTAssertEqual(status, .useItemListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseItemList key to a function which does not support it.") } do { let errSecUseKeychainUnsupported: OSStatus = -34006 let status = Status(rawValue: errSecUseKeychainUnsupported) XCTAssertEqual(status, .useKeychainUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychain key to a function which does not support it.") } do { let errSecUseKeychainListUnsupported: OSStatus = -34007 let status = Status(rawValue: errSecUseKeychainListUnsupported) XCTAssertEqual(status, .useKeychainListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychainList key to a function which does not support it.") } do { let errSecReturnDataUnsupported: OSStatus = -34008 let status = Status(rawValue: errSecReturnDataUnsupported) XCTAssertEqual(status, .returnDataUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnData key to a function which does not support it.") } do { let errSecReturnAttributesUnsupported: OSStatus = -34009 let status = Status(rawValue: errSecReturnAttributesUnsupported) XCTAssertEqual(status, .returnAttributesUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnAttributes key to a function which does not support it.") } do { let errSecReturnRefUnsupported: OSStatus = -34010 let status = Status(rawValue: errSecReturnRefUnsupported) XCTAssertEqual(status, .returnRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnRef key to a function which does not support it.") } do { let errSecReturnPersitentRefUnsupported: OSStatus = -34011 let status = Status(rawValue: errSecReturnPersitentRefUnsupported) XCTAssertEqual(status, .returnPersitentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnPersistentRef key to a function which does not support it.") } do { let errSecValueRefUnsupported: OSStatus = -34012 let status = Status(rawValue: errSecValueRefUnsupported) XCTAssertEqual(status, .valueRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValueRef key to a function which does not support it.") } do { let errSecValuePersistentRefUnsupported: OSStatus = -34013 let status = Status(rawValue: errSecValuePersistentRefUnsupported) XCTAssertEqual(status, .valuePersistentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValuePersistentRef key to a function which does not support it.") } do { let errSecReturnMissingPointer: OSStatus = -34014 let status = Status(rawValue: errSecReturnMissingPointer) XCTAssertEqual(status, .returnMissingPointer) XCTAssertEqual(status?.description, "The caller passed asked for something to be returned but did not pass in a result pointer.") } do { let errSecMatchLimitUnsupported: OSStatus = -34015 let status = Status(rawValue: errSecMatchLimitUnsupported) XCTAssertEqual(status, .matchLimitUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecMatchLimit key to a call which does not support limits.") } do { let errSecItemIllegalQuery: OSStatus = -34016 let status = Status(rawValue: errSecItemIllegalQuery) XCTAssertEqual(status, .itemIllegalQuery) XCTAssertEqual(status?.description, "The caller passed in a query which contained too many keys.") } do { let errSecWaitForCallback: OSStatus = -34017 let status = Status(rawValue: errSecWaitForCallback) XCTAssertEqual(status, .waitForCallback) XCTAssertEqual(status?.description, "This operation is incomplete, until the callback is invoked (not an error).") } do { let errSecMissingEntitlement: OSStatus = -34018 let status = Status(rawValue: errSecMissingEntitlement) XCTAssertEqual(status, .missingEntitlement) XCTAssertEqual(status?.description, "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements.") } do { let errSecUpgradePending: OSStatus = -34019 let status = Status(rawValue: errSecUpgradePending) XCTAssertEqual(status, .upgradePending) XCTAssertEqual(status?.description, "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command.") } do { let errSecMPSignatureInvalid: OSStatus = -25327 let status = Status(rawValue: errSecMPSignatureInvalid) XCTAssertEqual(status, .mpSignatureInvalid) XCTAssertEqual(status?.description, "Signature invalid on MP message") } do { let errSecOTRTooOld: OSStatus = -25328 let status = Status(rawValue: errSecOTRTooOld) XCTAssertEqual(status, .otrTooOld) XCTAssertEqual(status?.description, "Message is too old to use") } do { let errSecOTRIDTooNew: OSStatus = -25329 let status = Status(rawValue: errSecOTRIDTooNew) XCTAssertEqual(status, .otrIDTooNew) XCTAssertEqual(status?.description, "Key ID is too new to use! Message from the future?") } do { let status = Status(rawValue: errSecInsufficientClientID) XCTAssertEqual(status, .insufficientClientID) XCTAssertEqual(status?.description, "The client ID is not correct.") } do { let status = Status(rawValue: errSecDeviceReset) XCTAssertEqual(status, .deviceReset) XCTAssertEqual(status?.description, "A device reset has occurred.") } do { let status = Status(rawValue: errSecDeviceFailed) XCTAssertEqual(status, .deviceFailed) XCTAssertEqual(status?.description, "A device failure has occurred.") } do { let status = Status(rawValue: errSecAppleAddAppACLSubject) XCTAssertEqual(status, .appleAddAppACLSubject) XCTAssertEqual(status?.description, "Adding an application ACL subject failed.") } do { let status = Status(rawValue: errSecApplePublicKeyIncomplete) XCTAssertEqual(status, .applePublicKeyIncomplete) XCTAssertEqual(status?.description, "The public key is incomplete.") } do { let status = Status(rawValue: errSecAppleSignatureMismatch) XCTAssertEqual(status, .appleSignatureMismatch) XCTAssertEqual(status?.description, "A signature mismatch has occurred.") } do { let status = Status(rawValue: errSecAppleInvalidKeyStartDate) XCTAssertEqual(status, .appleInvalidKeyStartDate) XCTAssertEqual(status?.description, "The specified key has an invalid start date.") } do { let status = Status(rawValue: errSecAppleInvalidKeyEndDate) XCTAssertEqual(status, .appleInvalidKeyEndDate) XCTAssertEqual(status?.description, "The specified key has an invalid end date.") } do { let status = Status(rawValue: errSecConversionError) XCTAssertEqual(status, .conversionError) XCTAssertEqual(status?.description, "A conversion error has occurred.") } do { let status = Status(rawValue: errSecAppleSSLv2Rollback) XCTAssertEqual(status, .appleSSLv2Rollback) XCTAssertEqual(status?.description, "A SSLv2 rollback error has occurred.") } do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } do { let status = Status(rawValue: errSecQuotaExceeded) XCTAssertEqual(status, .quotaExceeded) XCTAssertEqual(status?.description, "The quota was exceeded.") } do { let status = Status(rawValue: errSecFileTooBig) XCTAssertEqual(status, .fileTooBig) XCTAssertEqual(status?.description, "The file is too big.") } do { let status = Status(rawValue: errSecInvalidDatabaseBlob) XCTAssertEqual(status, .invalidDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an invalid blob.") } do { let status = Status(rawValue: errSecInvalidKeyBlob) XCTAssertEqual(status, .invalidKeyBlob) XCTAssertEqual(status?.description, "The specified database has an invalid key blob.") } do { let status = Status(rawValue: errSecIncompatibleDatabaseBlob) XCTAssertEqual(status, .incompatibleDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible blob.") } do { let status = Status(rawValue: errSecIncompatibleKeyBlob) XCTAssertEqual(status, .incompatibleKeyBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible key blob.") } do { let status = Status(rawValue: errSecHostNameMismatch) XCTAssertEqual(status, .hostNameMismatch) XCTAssertEqual(status?.description, "A host name mismatch has occurred.") } do { let status = Status(rawValue: errSecUnknownCriticalExtensionFlag) XCTAssertEqual(status, .unknownCriticalExtensionFlag) XCTAssertEqual(status?.description, "There is an unknown critical extension flag.") } do { let status = Status(rawValue: errSecNoBasicConstraints) XCTAssertEqual(status, .noBasicConstraints) XCTAssertEqual(status?.description, "No basic constraints were found.") } do { let status = Status(rawValue: errSecNoBasicConstraintsCA) XCTAssertEqual(status, .noBasicConstraintsCA) XCTAssertEqual(status?.description, "No basic CA constraints were found.") } do { let status = Status(rawValue: errSecInvalidAuthorityKeyID) XCTAssertEqual(status, .invalidAuthorityKeyID) XCTAssertEqual(status?.description, "The authority key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidSubjectKeyID) XCTAssertEqual(status, .invalidSubjectKeyID) XCTAssertEqual(status?.description, "The subject key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidKeyUsageForPolicy) XCTAssertEqual(status, .invalidKeyUsageForPolicy) XCTAssertEqual(status?.description, "The key usage is not valid for the specified policy.") } do { let status = Status(rawValue: errSecInvalidExtendedKeyUsage) XCTAssertEqual(status, .invalidExtendedKeyUsage) XCTAssertEqual(status?.description, "The extended key usage is not valid.") } do { let status = Status(rawValue: errSecInvalidIDLinkage) XCTAssertEqual(status, .invalidIDLinkage) XCTAssertEqual(status?.description, "The ID linkage is not valid.") } do { let status = Status(rawValue: errSecPathLengthConstraintExceeded) XCTAssertEqual(status, .pathLengthConstraintExceeded) XCTAssertEqual(status?.description, "The path length constraint was exceeded.") } do { let status = Status(rawValue: errSecInvalidRoot) XCTAssertEqual(status, .invalidRoot) XCTAssertEqual(status?.description, "The root or anchor certificate is not valid.") } do { let status = Status(rawValue: errSecCRLExpired) XCTAssertEqual(status, .crlExpired) XCTAssertEqual(status?.description, "The CRL has expired.") } do { let status = Status(rawValue: errSecCRLNotValidYet) XCTAssertEqual(status, .crlNotValidYet) XCTAssertEqual(status?.description, "The CRL is not yet valid.") } do { let status = Status(rawValue: errSecCRLNotFound) XCTAssertEqual(status, .crlNotFound) XCTAssertEqual(status?.description, "The CRL was not found.") } do { let status = Status(rawValue: errSecCRLServerDown) XCTAssertEqual(status, .crlServerDown) XCTAssertEqual(status?.description, "The CRL server is down.") } do { let status = Status(rawValue: errSecCRLBadURI) XCTAssertEqual(status, .crlBadURI) XCTAssertEqual(status?.description, "The CRL has a bad Uniform Resource Identifier.") } do { let status = Status(rawValue: errSecUnknownCertExtension) XCTAssertEqual(status, .unknownCertExtension) XCTAssertEqual(status?.description, "An unknown certificate extension was encountered.") } do { let status = Status(rawValue: errSecUnknownCRLExtension) XCTAssertEqual(status, .unknownCRLExtension) XCTAssertEqual(status?.description, "An unknown CRL extension was encountered.") } do { let status = Status(rawValue: errSecCRLNotTrusted) XCTAssertEqual(status, .crlNotTrusted) XCTAssertEqual(status?.description, "The CRL is not trusted.") } do { let status = Status(rawValue: errSecCRLPolicyFailed) XCTAssertEqual(status, .crlPolicyFailed) XCTAssertEqual(status?.description, "The CRL policy failed.") } do { let status = Status(rawValue: errSecIDPFailure) XCTAssertEqual(status, .idpFailure) XCTAssertEqual(status?.description, "The issuing distribution point was not valid.") } do { let status = Status(rawValue: errSecSMIMEEmailAddressesNotFound) XCTAssertEqual(status, .smimeEmailAddressesNotFound) XCTAssertEqual(status?.description, "An email address mismatch was encountered.") } do { let status = Status(rawValue: errSecSMIMEBadExtendedKeyUsage) XCTAssertEqual(status, .smimeBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SMIME was not found.") } do { let status = Status(rawValue: errSecSMIMEBadKeyUsage) XCTAssertEqual(status, .smimeBadKeyUsage) XCTAssertEqual(status?.description, "The key usage is not compatible with SMIME.") } do { let status = Status(rawValue: errSecSMIMEKeyUsageNotCritical) XCTAssertEqual(status, .smimeKeyUsageNotCritical) XCTAssertEqual(status?.description, "The key usage extension is not marked as critical.") } do { let status = Status(rawValue: errSecSMIMENoEmailAddress) XCTAssertEqual(status, .smimeNoEmailAddress) XCTAssertEqual(status?.description, "No email address was found in the certificate.") } do { let status = Status(rawValue: errSecSMIMESubjAltNameNotCritical) XCTAssertEqual(status, .smimeSubjAltNameNotCritical) XCTAssertEqual(status?.description, "The subject alternative name extension is not marked as critical.") } do { let status = Status(rawValue: errSecSSLBadExtendedKeyUsage) XCTAssertEqual(status, .sslBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SSL was not found.") } do { let status = Status(rawValue: errSecOCSPBadResponse) XCTAssertEqual(status, .ocspBadResponse) XCTAssertEqual(status?.description, "The OCSP response was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPBadRequest) XCTAssertEqual(status, .ocspBadRequest) XCTAssertEqual(status?.description, "The OCSP request was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPUnavailable) XCTAssertEqual(status, .ocspUnavailable) XCTAssertEqual(status?.description, "OCSP service is unavailable.") } do { let status = Status(rawValue: errSecOCSPStatusUnrecognized) XCTAssertEqual(status, .ocspStatusUnrecognized) XCTAssertEqual(status?.description, "The OCSP server did not recognize this certificate.") } do { let status = Status(rawValue: errSecEndOfData) XCTAssertEqual(status, .endOfData) XCTAssertEqual(status?.description, "An end-of-data was detected.") } do { let status = Status(rawValue: errSecIncompleteCertRevocationCheck) XCTAssertEqual(status, .incompleteCertRevocationCheck) XCTAssertEqual(status?.description, "An incomplete certificate revocation check occurred.") } do { let status = Status(rawValue: errSecNetworkFailure) XCTAssertEqual(status, .networkFailure) XCTAssertEqual(status?.description, "A network failure occurred.") } do { let status = Status(rawValue: errSecOCSPNotTrustedToAnchor) XCTAssertEqual(status, .ocspNotTrustedToAnchor) XCTAssertEqual(status?.description, "The OCSP response was not trusted to a root or anchor certificate.") } do { let status = Status(rawValue: errSecRecordModified) XCTAssertEqual(status, .recordModified) XCTAssertEqual(status?.description, "The record was modified.") } do { let status = Status(rawValue: errSecOCSPSignatureError) XCTAssertEqual(status, .ocspSignatureError) XCTAssertEqual(status?.description, "The OCSP response had an invalid signature.") } do { let status = Status(rawValue: errSecOCSPNoSigner) XCTAssertEqual(status, .ocspNoSigner) XCTAssertEqual(status?.description, "The OCSP response had no signer.") } do { let status = Status(rawValue: errSecOCSPResponderMalformedReq) XCTAssertEqual(status, .ocspResponderMalformedReq) XCTAssertEqual(status?.description, "The OCSP responder was given a malformed request.") } do { let status = Status(rawValue: errSecOCSPResponderInternalError) XCTAssertEqual(status, .ocspResponderInternalError) XCTAssertEqual(status?.description, "The OCSP responder encountered an internal error.") } do { let status = Status(rawValue: errSecOCSPResponderTryLater) XCTAssertEqual(status, .ocspResponderTryLater) XCTAssertEqual(status?.description, "The OCSP responder is busy, try again later.") } do { let status = Status(rawValue: errSecOCSPResponderSignatureRequired) XCTAssertEqual(status, .ocspResponderSignatureRequired) XCTAssertEqual(status?.description, "The OCSP responder requires a signature.") } do { let status = Status(rawValue: errSecOCSPResponderUnauthorized) XCTAssertEqual(status, .ocspResponderUnauthorized) XCTAssertEqual(status?.description, "The OCSP responder rejected this request as unauthorized.") } do { let status = Status(rawValue: errSecOCSPResponseNonceMismatch) XCTAssertEqual(status, .ocspResponseNonceMismatch) XCTAssertEqual(status?.description, "The OCSP response nonce did not match the request.") } do { let status = Status(rawValue: errSecCodeSigningBadCertChainLength) XCTAssertEqual(status, .codeSigningBadCertChainLength) XCTAssertEqual(status?.description, "Code signing encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecCodeSigningNoBasicConstraints) XCTAssertEqual(status, .codeSigningNoBasicConstraints) XCTAssertEqual(status?.description, "Code signing found no basic constraints.") } do { let status = Status(rawValue: errSecCodeSigningBadPathLengthConstraint) XCTAssertEqual(status, .codeSigningBadPathLengthConstraint) XCTAssertEqual(status?.description, "Code signing encountered an incorrect path length constraint.") } do { let status = Status(rawValue: errSecCodeSigningNoExtendedKeyUsage) XCTAssertEqual(status, .codeSigningNoExtendedKeyUsage) XCTAssertEqual(status?.description, "Code signing found no extended key usage.") } do { let status = Status(rawValue: errSecCodeSigningDevelopment) XCTAssertEqual(status, .codeSigningDevelopment) XCTAssertEqual(status?.description, "Code signing indicated use of a development-only certificate.") } do { let status = Status(rawValue: errSecResourceSignBadCertChainLength) XCTAssertEqual(status, .resourceSignBadCertChainLength) XCTAssertEqual(status?.description, "Resource signing has encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecResourceSignBadExtKeyUsage) XCTAssertEqual(status, .resourceSignBadExtKeyUsage) XCTAssertEqual(status?.description, "Resource signing has encountered an error in the extended key usage.") } do { let status = Status(rawValue: errSecTrustSettingDeny) XCTAssertEqual(status, .trustSettingDeny) XCTAssertEqual(status?.description, "The trust setting for this policy was set to Deny.") } do { let status = Status(rawValue: errSecInvalidSubjectName) XCTAssertEqual(status, .invalidSubjectName) XCTAssertEqual(status?.description, "An invalid certificate subject name was encountered.") } do { let status = Status(rawValue: errSecUnknownQualifiedCertStatement) XCTAssertEqual(status, .unknownQualifiedCertStatement) XCTAssertEqual(status?.description, "An unknown qualified certificate statement was encountered.") } do { let status = Status(rawValue: errSecMobileMeRequestQueued) XCTAssertEqual(status, .mobileMeRequestQueued) XCTAssertEqual(status?.description, "The MobileMe request will be sent during the next connection.") } do { let status = Status(rawValue: errSecMobileMeRequestRedirected) XCTAssertEqual(status, .mobileMeRequestRedirected) XCTAssertEqual(status?.description, "The MobileMe request was redirected.") } do { let status = Status(rawValue: errSecMobileMeServerError) XCTAssertEqual(status, .mobileMeServerError) XCTAssertEqual(status?.description, "A MobileMe server error occurred.") } do { let status = Status(rawValue: errSecMobileMeServerNotAvailable) XCTAssertEqual(status, .mobileMeServerNotAvailable) XCTAssertEqual(status?.description, "The MobileMe server is not available.") } do { let status = Status(rawValue: errSecMobileMeServerAlreadyExists) XCTAssertEqual(status, .mobileMeServerAlreadyExists) XCTAssertEqual(status?.description, "The MobileMe server reported that the item already exists.") } do { let status = Status(rawValue: errSecMobileMeServerServiceErr) XCTAssertEqual(status, .mobileMeServerServiceErr) XCTAssertEqual(status?.description, "A MobileMe service error has occurred.") } do { let status = Status(rawValue: errSecMobileMeRequestAlreadyPending) XCTAssertEqual(status, .mobileMeRequestAlreadyPending) XCTAssertEqual(status?.description, "A MobileMe request is already pending.") } do { let status = Status(rawValue: errSecMobileMeNoRequestPending) XCTAssertEqual(status, .mobileMeNoRequestPending) XCTAssertEqual(status?.description, "MobileMe has no request pending.") } do { let status = Status(rawValue: errSecMobileMeCSRVerifyFailure) XCTAssertEqual(status, .mobileMeCSRVerifyFailure) XCTAssertEqual(status?.description, "A MobileMe CSR verification failure has occurred.") } do { let status = Status(rawValue: errSecMobileMeFailedConsistencyCheck) XCTAssertEqual(status, .mobileMeFailedConsistencyCheck) XCTAssertEqual(status?.description, "MobileMe has found a failed consistency check.") } do { let status = Status(rawValue: errSecNotInitialized) XCTAssertEqual(status, .notInitialized) XCTAssertEqual(status?.description, "A function was called without initializing CSSM.") } do { let status = Status(rawValue: errSecInvalidHandleUsage) XCTAssertEqual(status, .invalidHandleUsage) XCTAssertEqual(status?.description, "The CSSM handle does not match with the service type.") } do { let status = Status(rawValue: errSecPVCReferentNotFound) XCTAssertEqual(status, .pvcReferentNotFound) XCTAssertEqual(status?.description, "A reference to the calling module was not found in the list of authorized callers.") } do { let status = Status(rawValue: errSecFunctionIntegrityFail) XCTAssertEqual(status, .functionIntegrityFail) XCTAssertEqual(status?.description, "A function address was not within the verified module.") } do { let status = Status(rawValue: errSecInternalError) XCTAssertEqual(status, .internalError) XCTAssertEqual(status?.description, "An internal error has occurred.") } do { let status = Status(rawValue: errSecMemoryError) XCTAssertEqual(status, .memoryError) XCTAssertEqual(status?.description, "A memory error has occurred.") } do { let status = Status(rawValue: errSecInvalidData) XCTAssertEqual(status, .invalidData) XCTAssertEqual(status?.description, "Invalid data was encountered.") } do { let status = Status(rawValue: errSecMDSError) XCTAssertEqual(status, .mdsError) XCTAssertEqual(status?.description, "A Module Directory Service error has occurred.") } do { let status = Status(rawValue: errSecInvalidPointer) XCTAssertEqual(status, .invalidPointer) XCTAssertEqual(status?.description, "An invalid pointer was encountered.") } do { let status = Status(rawValue: errSecSelfCheckFailed) XCTAssertEqual(status, .selfCheckFailed) XCTAssertEqual(status?.description, "Self-check has failed.") } do { let status = Status(rawValue: errSecFunctionFailed) XCTAssertEqual(status, .functionFailed) XCTAssertEqual(status?.description, "A function has failed.") } do { let status = Status(rawValue: errSecModuleManifestVerifyFailed) XCTAssertEqual(status, .moduleManifestVerifyFailed) XCTAssertEqual(status?.description, "A module manifest verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidGUID) XCTAssertEqual(status, .invalidGUID) XCTAssertEqual(status?.description, "An invalid GUID was encountered.") } do { let status = Status(rawValue: errSecInvalidHandle) XCTAssertEqual(status, .invalidHandle) XCTAssertEqual(status?.description, "An invalid handle was encountered.") } do { let status = Status(rawValue: errSecInvalidDBList) XCTAssertEqual(status, .invalidDBList) XCTAssertEqual(status?.description, "An invalid DB list was encountered.") } do { let status = Status(rawValue: errSecInvalidPassthroughID) XCTAssertEqual(status, .invalidPassthroughID) XCTAssertEqual(status?.description, "An invalid passthrough ID was encountered.") } do { let status = Status(rawValue: errSecInvalidNetworkAddress) XCTAssertEqual(status, .invalidNetworkAddress) XCTAssertEqual(status?.description, "An invalid network address was encountered.") } do { let status = Status(rawValue: errSecCRLAlreadySigned) XCTAssertEqual(status, .crlAlreadySigned) XCTAssertEqual(status?.description, "The certificate revocation list is already signed.") } do { let status = Status(rawValue: errSecInvalidNumberOfFields) XCTAssertEqual(status, .invalidNumberOfFields) XCTAssertEqual(status?.description, "An invalid number of fields were encountered.") } do { let status = Status(rawValue: errSecVerificationFailure) XCTAssertEqual(status, .verificationFailure) XCTAssertEqual(status?.description, "A verification failure occurred.") } do { let status = Status(rawValue: errSecUnknownTag) XCTAssertEqual(status, .unknownTag) XCTAssertEqual(status?.description, "An unknown tag was encountered.") } do { let status = Status(rawValue: errSecInvalidSignature) XCTAssertEqual(status, .invalidSignature) XCTAssertEqual(status?.description, "An invalid signature was encountered.") } do { let status = Status(rawValue: errSecInvalidName) XCTAssertEqual(status, .invalidName) XCTAssertEqual(status?.description, "An invalid name was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateRef) XCTAssertEqual(status, .invalidCertificateRef) XCTAssertEqual(status?.description, "An invalid certificate reference was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateGroup) XCTAssertEqual(status, .invalidCertificateGroup) XCTAssertEqual(status?.description, "An invalid certificate group was encountered.") } do { let status = Status(rawValue: errSecTagNotFound) XCTAssertEqual(status, .tagNotFound) XCTAssertEqual(status?.description, "The specified tag was not found.") } do { let status = Status(rawValue: errSecInvalidQuery) XCTAssertEqual(status, .invalidQuery) XCTAssertEqual(status?.description, "The specified query was not valid.") } do { let status = Status(rawValue: errSecInvalidValue) XCTAssertEqual(status, .invalidValue) XCTAssertEqual(status?.description, "An invalid value was detected.") } do { let status = Status(rawValue: errSecCallbackFailed) XCTAssertEqual(status, .callbackFailed) XCTAssertEqual(status?.description, "A callback has failed.") } do { let status = Status(rawValue: errSecACLDeleteFailed) XCTAssertEqual(status, .aclDeleteFailed) XCTAssertEqual(status?.description, "An ACL delete operation has failed.") } do { let status = Status(rawValue: errSecACLReplaceFailed) XCTAssertEqual(status, .aclReplaceFailed) XCTAssertEqual(status?.description, "An ACL replace operation has failed.") } do { let status = Status(rawValue: errSecACLAddFailed) XCTAssertEqual(status, .aclAddFailed) XCTAssertEqual(status?.description, "An ACL add operation has failed.") } do { let status = Status(rawValue: errSecACLChangeFailed) XCTAssertEqual(status, .aclChangeFailed) XCTAssertEqual(status?.description, "An ACL change operation has failed.") } do { let status = Status(rawValue: errSecInvalidAccessCredentials) XCTAssertEqual(status, .invalidAccessCredentials) XCTAssertEqual(status?.description, "Invalid access credentials were encountered.") } do { let status = Status(rawValue: errSecInvalidRecord) XCTAssertEqual(status, .invalidRecord) XCTAssertEqual(status?.description, "An invalid record was encountered.") } do { let status = Status(rawValue: errSecInvalidACL) XCTAssertEqual(status, .invalidACL) XCTAssertEqual(status?.description, "An invalid ACL was encountered.") } do { let status = Status(rawValue: errSecInvalidSampleValue) XCTAssertEqual(status, .invalidSampleValue) XCTAssertEqual(status?.description, "An invalid sample value was encountered.") } do { let status = Status(rawValue: errSecIncompatibleVersion) XCTAssertEqual(status, .incompatibleVersion) XCTAssertEqual(status?.description, "An incompatible version was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotGranted) XCTAssertEqual(status, .privilegeNotGranted) XCTAssertEqual(status?.description, "The privilege was not granted.") } do { let status = Status(rawValue: errSecInvalidScope) XCTAssertEqual(status, .invalidScope) XCTAssertEqual(status?.description, "An invalid scope was encountered.") } do { let status = Status(rawValue: errSecPVCAlreadyConfigured) XCTAssertEqual(status, .pvcAlreadyConfigured) XCTAssertEqual(status?.description, "The PVC is already configured.") } do { let status = Status(rawValue: errSecInvalidPVC) XCTAssertEqual(status, .invalidPVC) XCTAssertEqual(status?.description, "An invalid PVC was encountered.") } do { let status = Status(rawValue: errSecEMMLoadFailed) XCTAssertEqual(status, .emmLoadFailed) XCTAssertEqual(status?.description, "The EMM load has failed.") } do { let status = Status(rawValue: errSecEMMUnloadFailed) XCTAssertEqual(status, .emmUnloadFailed) XCTAssertEqual(status?.description, "The EMM unload has failed.") } do { let status = Status(rawValue: errSecAddinLoadFailed) XCTAssertEqual(status, .addinLoadFailed) XCTAssertEqual(status?.description, "The add-in load operation has failed.") } do { let status = Status(rawValue: errSecInvalidKeyRef) XCTAssertEqual(status, .invalidKeyRef) XCTAssertEqual(status?.description, "An invalid key was encountered.") } do { let status = Status(rawValue: errSecInvalidKeyHierarchy) XCTAssertEqual(status, .invalidKeyHierarchy) XCTAssertEqual(status?.description, "An invalid key hierarchy was encountered.") } do { let status = Status(rawValue: errSecAddinUnloadFailed) XCTAssertEqual(status, .addinUnloadFailed) XCTAssertEqual(status?.description, "The add-in unload operation has failed.") } do { let status = Status(rawValue: errSecLibraryReferenceNotFound) XCTAssertEqual(status, .libraryReferenceNotFound) XCTAssertEqual(status?.description, "A library reference was not found.") } do { let status = Status(rawValue: errSecInvalidAddinFunctionTable) XCTAssertEqual(status, .invalidAddinFunctionTable) XCTAssertEqual(status?.description, "An invalid add-in function table was encountered.") } do { let status = Status(rawValue: errSecInvalidServiceMask) XCTAssertEqual(status, .invalidServiceMask) XCTAssertEqual(status?.description, "An invalid service mask was encountered.") } do { let status = Status(rawValue: errSecModuleNotLoaded) XCTAssertEqual(status, .moduleNotLoaded) XCTAssertEqual(status?.description, "A module was not loaded.") } do { let status = Status(rawValue: errSecInvalidSubServiceID) XCTAssertEqual(status, .invalidSubServiceID) XCTAssertEqual(status?.description, "An invalid subservice ID was encountered.") } do { let status = Status(rawValue: errSecAttributeNotInContext) XCTAssertEqual(status, .attributeNotInContext) XCTAssertEqual(status?.description, "An attribute was not in the context.") } do { let status = Status(rawValue: errSecModuleManagerInitializeFailed) XCTAssertEqual(status, .moduleManagerInitializeFailed) XCTAssertEqual(status?.description, "A module failed to initialize.") } do { let status = Status(rawValue: errSecModuleManagerNotFound) XCTAssertEqual(status, .moduleManagerNotFound) XCTAssertEqual(status?.description, "A module was not found.") } do { let status = Status(rawValue: errSecEventNotificationCallbackNotFound) XCTAssertEqual(status, .eventNotificationCallbackNotFound) XCTAssertEqual(status?.description, "An event notification callback was not found.") } do { let status = Status(rawValue: errSecInputLengthError) XCTAssertEqual(status, .inputLengthError) XCTAssertEqual(status?.description, "An input length error was encountered.") } do { let status = Status(rawValue: errSecOutputLengthError) XCTAssertEqual(status, .outputLengthError) XCTAssertEqual(status?.description, "An output length error was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotSupported) XCTAssertEqual(status, .privilegeNotSupported) XCTAssertEqual(status?.description, "The privilege is not supported.") } do { let status = Status(rawValue: errSecDeviceError) XCTAssertEqual(status, .deviceError) XCTAssertEqual(status?.description, "A device error was encountered.") } do { let status = Status(rawValue: errSecAttachHandleBusy) XCTAssertEqual(status, .attachHandleBusy) XCTAssertEqual(status?.description, "The CSP handle was busy.") } do { let status = Status(rawValue: errSecNotLoggedIn) XCTAssertEqual(status, .notLoggedIn) XCTAssertEqual(status?.description, "You are not logged in.") } do { let status = Status(rawValue: errSecAlgorithmMismatch) XCTAssertEqual(status, .algorithmMismatch) XCTAssertEqual(status?.description, "An algorithm mismatch was encountered.") } do { let status = Status(rawValue: errSecKeyUsageIncorrect) XCTAssertEqual(status, .keyUsageIncorrect) XCTAssertEqual(status?.description, "The key usage is incorrect.") } do { let status = Status(rawValue: errSecKeyBlobTypeIncorrect) XCTAssertEqual(status, .keyBlobTypeIncorrect) XCTAssertEqual(status?.description, "The key blob type is incorrect.") } do { let status = Status(rawValue: errSecKeyHeaderInconsistent) XCTAssertEqual(status, .keyHeaderInconsistent) XCTAssertEqual(status?.description, "The key header is inconsistent.") } do { let status = Status(rawValue: errSecUnsupportedKeyFormat) XCTAssertEqual(status, .unsupportedKeyFormat) XCTAssertEqual(status?.description, "The key header format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedKeySize) XCTAssertEqual(status, .unsupportedKeySize) XCTAssertEqual(status?.description, "The key size is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyUsageMask) XCTAssertEqual(status, .invalidKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyUsageMask) XCTAssertEqual(status, .unsupportedKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyAttributeMask) XCTAssertEqual(status, .invalidKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyAttributeMask) XCTAssertEqual(status, .unsupportedKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyLabel) XCTAssertEqual(status, .invalidKeyLabel) XCTAssertEqual(status?.description, "The key label is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyLabel) XCTAssertEqual(status, .unsupportedKeyLabel) XCTAssertEqual(status?.description, "The key label is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyFormat) XCTAssertEqual(status, .invalidKeyFormat) XCTAssertEqual(status?.description, "The key format is not valid.") } do { let status = Status(rawValue: errSecUnsupportedVectorOfBuffers) XCTAssertEqual(status, .unsupportedVectorOfBuffers) XCTAssertEqual(status?.description, "The vector of buffers is not supported.") } do { let status = Status(rawValue: errSecInvalidInputVector) XCTAssertEqual(status, .invalidInputVector) XCTAssertEqual(status?.description, "The input vector is not valid.") } do { let status = Status(rawValue: errSecInvalidOutputVector) XCTAssertEqual(status, .invalidOutputVector) XCTAssertEqual(status?.description, "The output vector is not valid.") } do { let status = Status(rawValue: errSecInvalidContext) XCTAssertEqual(status, .invalidContext) XCTAssertEqual(status?.description, "An invalid context was encountered.") } do { let status = Status(rawValue: errSecInvalidAlgorithm) XCTAssertEqual(status, .invalidAlgorithm) XCTAssertEqual(status?.description, "An invalid algorithm was encountered.") } do { let status = Status(rawValue: errSecInvalidAttributeKey) XCTAssertEqual(status, .invalidAttributeKey) XCTAssertEqual(status?.description, "A key attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKey) XCTAssertEqual(status, .missingAttributeKey) XCTAssertEqual(status?.description, "A key attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeInitVector) XCTAssertEqual(status, .invalidAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeInitVector) XCTAssertEqual(status, .missingAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSalt) XCTAssertEqual(status, .invalidAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSalt) XCTAssertEqual(status, .missingAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePadding) XCTAssertEqual(status, .invalidAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePadding) XCTAssertEqual(status, .missingAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRandom) XCTAssertEqual(status, .invalidAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRandom) XCTAssertEqual(status, .missingAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSeed) XCTAssertEqual(status, .invalidAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSeed) XCTAssertEqual(status, .missingAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePassphrase) XCTAssertEqual(status, .invalidAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePassphrase) XCTAssertEqual(status, .missingAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyLength) XCTAssertEqual(status, .invalidAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyLength) XCTAssertEqual(status, .missingAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBlockSize) XCTAssertEqual(status, .invalidAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBlockSize) XCTAssertEqual(status, .missingAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeOutputSize) XCTAssertEqual(status, .invalidAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeOutputSize) XCTAssertEqual(status, .missingAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRounds) XCTAssertEqual(status, .invalidAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRounds) XCTAssertEqual(status, .missingAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAlgorithmParms) XCTAssertEqual(status, .invalidAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAlgorithmParms) XCTAssertEqual(status, .missingAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeLabel) XCTAssertEqual(status, .invalidAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeLabel) XCTAssertEqual(status, .missingAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyType) XCTAssertEqual(status, .invalidAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyType) XCTAssertEqual(status, .missingAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeMode) XCTAssertEqual(status, .invalidAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeMode) XCTAssertEqual(status, .missingAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEffectiveBits) XCTAssertEqual(status, .invalidAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEffectiveBits) XCTAssertEqual(status, .missingAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeStartDate) XCTAssertEqual(status, .invalidAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeStartDate) XCTAssertEqual(status, .missingAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEndDate) XCTAssertEqual(status, .invalidAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEndDate) XCTAssertEqual(status, .missingAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeVersion) XCTAssertEqual(status, .invalidAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeVersion) XCTAssertEqual(status, .missingAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrime) XCTAssertEqual(status, .invalidAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrime) XCTAssertEqual(status, .missingAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBase) XCTAssertEqual(status, .invalidAttributeBase) XCTAssertEqual(status?.description, "A base attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBase) XCTAssertEqual(status, .missingAttributeBase) XCTAssertEqual(status?.description, "A base attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSubprime) XCTAssertEqual(status, .invalidAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSubprime) XCTAssertEqual(status, .missingAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeIterationCount) XCTAssertEqual(status, .invalidAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeIterationCount) XCTAssertEqual(status, .missingAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeDLDBHandle) XCTAssertEqual(status, .invalidAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeDLDBHandle) XCTAssertEqual(status, .missingAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeAccessCredentials) XCTAssertEqual(status, .invalidAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeAccessCredentials) XCTAssertEqual(status, .missingAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePublicKeyFormat) XCTAssertEqual(status, .invalidAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePublicKeyFormat) XCTAssertEqual(status, .missingAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrivateKeyFormat) XCTAssertEqual(status, .invalidAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrivateKeyFormat) XCTAssertEqual(status, .missingAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSymmetricKeyFormat) XCTAssertEqual(status, .invalidAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSymmetricKeyFormat) XCTAssertEqual(status, .missingAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeWrappedKeyFormat) XCTAssertEqual(status, .invalidAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeWrappedKeyFormat) XCTAssertEqual(status, .missingAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was missing.") } do { let status = Status(rawValue: errSecStagedOperationInProgress) XCTAssertEqual(status, .stagedOperationInProgress) XCTAssertEqual(status?.description, "A staged operation is in progress.") } do { let status = Status(rawValue: errSecStagedOperationNotStarted) XCTAssertEqual(status, .stagedOperationNotStarted) XCTAssertEqual(status?.description, "A staged operation was not started.") } do { let status = Status(rawValue: errSecVerifyFailed) XCTAssertEqual(status, .verifyFailed) XCTAssertEqual(status?.description, "A cryptographic verification failure has occurred.") } do { let status = Status(rawValue: errSecQuerySizeUnknown) XCTAssertEqual(status, .querySizeUnknown) XCTAssertEqual(status?.description, "The query size is unknown.") } do { let status = Status(rawValue: errSecBlockSizeMismatch) XCTAssertEqual(status, .blockSizeMismatch) XCTAssertEqual(status?.description, "A block size mismatch occurred.") } do { let status = Status(rawValue: errSecPublicKeyInconsistent) XCTAssertEqual(status, .publicKeyInconsistent) XCTAssertEqual(status?.description, "The public key was inconsistent.") } do { let status = Status(rawValue: errSecDeviceVerifyFailed) XCTAssertEqual(status, .deviceVerifyFailed) XCTAssertEqual(status?.description, "A device verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidLoginName) XCTAssertEqual(status, .invalidLoginName) XCTAssertEqual(status?.description, "An invalid login name was detected.") } do { let status = Status(rawValue: errSecAlreadyLoggedIn) XCTAssertEqual(status, .alreadyLoggedIn) XCTAssertEqual(status?.description, "The user is already logged in.") } do { let status = Status(rawValue: errSecInvalidDigestAlgorithm) XCTAssertEqual(status, .invalidDigestAlgorithm) XCTAssertEqual(status?.description, "An invalid digest algorithm was detected.") } do { let status = Status(rawValue: errSecInvalidCRLGroup) XCTAssertEqual(status, .invalidCRLGroup) XCTAssertEqual(status?.description, "An invalid CRL group was detected.") } do { let status = Status(rawValue: errSecCertificateCannotOperate) XCTAssertEqual(status, .certificateCannotOperate) XCTAssertEqual(status?.description, "The certificate cannot operate.") } do { let status = Status(rawValue: errSecCertificateExpired) XCTAssertEqual(status, .certificateExpired) XCTAssertEqual(status?.description, "An expired certificate was detected.") } do { let status = Status(rawValue: errSecCertificateNotValidYet) XCTAssertEqual(status, .certificateNotValidYet) XCTAssertEqual(status?.description, "The certificate is not yet valid.") } do { let status = Status(rawValue: errSecCertificateRevoked) XCTAssertEqual(status, .certificateRevoked) XCTAssertEqual(status?.description, "The certificate was revoked.") } do { let status = Status(rawValue: errSecCertificateSuspended) XCTAssertEqual(status, .certificateSuspended) XCTAssertEqual(status?.description, "The certificate was suspended.") } do { let status = Status(rawValue: errSecInsufficientCredentials) XCTAssertEqual(status, .insufficientCredentials) XCTAssertEqual(status?.description, "Insufficient credentials were detected.") } do { let status = Status(rawValue: errSecInvalidAction) XCTAssertEqual(status, .invalidAction) XCTAssertEqual(status?.description, "The action was not valid.") } do { let status = Status(rawValue: errSecInvalidAuthority) XCTAssertEqual(status, .invalidAuthority) XCTAssertEqual(status?.description, "The authority was not valid.") } do { let status = Status(rawValue: errSecVerifyActionFailed) XCTAssertEqual(status, .verifyActionFailed) XCTAssertEqual(status?.description, "A verify action has failed.") } do { let status = Status(rawValue: errSecInvalidCertAuthority) XCTAssertEqual(status, .invalidCertAuthority) XCTAssertEqual(status?.description, "The certificate authority was not valid.") } do { let status = Status(rawValue: errSecInvaldCRLAuthority) XCTAssertEqual(status, .invaldCRLAuthority) XCTAssertEqual(status?.description, "The CRL authority was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLEncoding) XCTAssertEqual(status, .invalidCRLEncoding) XCTAssertEqual(status?.description, "The CRL encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLType) XCTAssertEqual(status, .invalidCRLType) XCTAssertEqual(status?.description, "The CRL type was not valid.") } do { let status = Status(rawValue: errSecInvalidCRL) XCTAssertEqual(status, .invalidCRL) XCTAssertEqual(status?.description, "The CRL was not valid.") } do { let status = Status(rawValue: errSecInvalidFormType) XCTAssertEqual(status, .invalidFormType) XCTAssertEqual(status?.description, "The form type was not valid.") } do { let status = Status(rawValue: errSecInvalidID) XCTAssertEqual(status, .invalidID) XCTAssertEqual(status?.description, "The ID was not valid.") } do { let status = Status(rawValue: errSecInvalidIdentifier) XCTAssertEqual(status, .invalidIdentifier) XCTAssertEqual(status?.description, "The identifier was not valid.") } do { let status = Status(rawValue: errSecInvalidIndex) XCTAssertEqual(status, .invalidIndex) XCTAssertEqual(status?.description, "The index was not valid.") } do { let status = Status(rawValue: errSecInvalidPolicyIdentifiers) XCTAssertEqual(status, .invalidPolicyIdentifiers) XCTAssertEqual(status?.description, "The policy identifiers are not valid.") } do { let status = Status(rawValue: errSecInvalidTimeString) XCTAssertEqual(status, .invalidTimeString) XCTAssertEqual(status?.description, "The time specified was not valid.") } do { let status = Status(rawValue: errSecInvalidReason) XCTAssertEqual(status, .invalidReason) XCTAssertEqual(status?.description, "The trust policy reason was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestInputs) XCTAssertEqual(status, .invalidRequestInputs) XCTAssertEqual(status?.description, "The request inputs are not valid.") } do { let status = Status(rawValue: errSecInvalidResponseVector) XCTAssertEqual(status, .invalidResponseVector) XCTAssertEqual(status?.description, "The response vector was not valid.") } do { let status = Status(rawValue: errSecInvalidStopOnPolicy) XCTAssertEqual(status, .invalidStopOnPolicy) XCTAssertEqual(status?.description, "The stop-on policy was not valid.") } do { let status = Status(rawValue: errSecInvalidTuple) XCTAssertEqual(status, .invalidTuple) XCTAssertEqual(status?.description, "The tuple was not valid.") } do { let status = Status(rawValue: errSecMultipleValuesUnsupported) XCTAssertEqual(status, .multipleValuesUnsupported) XCTAssertEqual(status?.description, "Multiple values are not supported.") } do { let status = Status(rawValue: errSecNotTrusted) XCTAssertEqual(status, .notTrusted) XCTAssertEqual(status?.description, "The trust policy was not trusted.") } do { let status = Status(rawValue: errSecNoDefaultAuthority) XCTAssertEqual(status, .noDefaultAuthority) XCTAssertEqual(status?.description, "No default authority was detected.") } do { let status = Status(rawValue: errSecRejectedForm) XCTAssertEqual(status, .rejectedForm) XCTAssertEqual(status?.description, "The trust policy had a rejected form.") } do { let status = Status(rawValue: errSecRequestLost) XCTAssertEqual(status, .requestLost) XCTAssertEqual(status?.description, "The request was lost.") } do { let status = Status(rawValue: errSecRequestRejected) XCTAssertEqual(status, .requestRejected) XCTAssertEqual(status?.description, "The request was rejected.") } do { let status = Status(rawValue: errSecUnsupportedAddressType) XCTAssertEqual(status, .unsupportedAddressType) XCTAssertEqual(status?.description, "The address type is not supported.") } do { let status = Status(rawValue: errSecUnsupportedService) XCTAssertEqual(status, .unsupportedService) XCTAssertEqual(status?.description, "The service is not supported.") } do { let status = Status(rawValue: errSecInvalidTupleGroup) XCTAssertEqual(status, .invalidTupleGroup) XCTAssertEqual(status?.description, "The tuple group was not valid.") } do { let status = Status(rawValue: errSecInvalidBaseACLs) XCTAssertEqual(status, .invalidBaseACLs) XCTAssertEqual(status?.description, "The base ACLs are not valid.") } do { let status = Status(rawValue: errSecInvalidTupleCredendtials) XCTAssertEqual(status, .invalidTupleCredendtials) XCTAssertEqual(status?.description, "The tuple credentials are not valid.") } do { let status = Status(rawValue: errSecInvalidEncoding) XCTAssertEqual(status, .invalidEncoding) XCTAssertEqual(status?.description, "The encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidValidityPeriod) XCTAssertEqual(status, .invalidValidityPeriod) XCTAssertEqual(status?.description, "The validity period was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestor) XCTAssertEqual(status, .invalidRequestor) XCTAssertEqual(status?.description, "The requestor was not valid.") } do { let status = Status(rawValue: errSecRequestDescriptor) XCTAssertEqual(status, .requestDescriptor) XCTAssertEqual(status?.description, "The request descriptor was not valid.") } do { let status = Status(rawValue: errSecInvalidBundleInfo) XCTAssertEqual(status, .invalidBundleInfo) XCTAssertEqual(status?.description, "The bundle information was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLIndex) XCTAssertEqual(status, .invalidCRLIndex) XCTAssertEqual(status?.description, "The CRL index was not valid.") } do { let status = Status(rawValue: errSecNoFieldValues) XCTAssertEqual(status, .noFieldValues) XCTAssertEqual(status?.description, "No field values were detected.") } do { let status = Status(rawValue: errSecUnsupportedFieldFormat) XCTAssertEqual(status, .unsupportedFieldFormat) XCTAssertEqual(status?.description, "The field format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedIndexInfo) XCTAssertEqual(status, .unsupportedIndexInfo) XCTAssertEqual(status?.description, "The index information is not supported.") } do { let status = Status(rawValue: errSecUnsupportedLocality) XCTAssertEqual(status, .unsupportedLocality) XCTAssertEqual(status?.description, "The locality is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumAttributes) XCTAssertEqual(status, .unsupportedNumAttributes) XCTAssertEqual(status?.description, "The number of attributes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumIndexes) XCTAssertEqual(status, .unsupportedNumIndexes) XCTAssertEqual(status?.description, "The number of indexes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumRecordTypes) XCTAssertEqual(status, .unsupportedNumRecordTypes) XCTAssertEqual(status?.description, "The number of record types is not supported.") } do { let status = Status(rawValue: errSecFieldSpecifiedMultiple) XCTAssertEqual(status, .fieldSpecifiedMultiple) XCTAssertEqual(status?.description, "Too many fields were specified.") } do { let status = Status(rawValue: errSecIncompatibleFieldFormat) XCTAssertEqual(status, .incompatibleFieldFormat) XCTAssertEqual(status?.description, "The field format was incompatible.") } do { let status = Status(rawValue: errSecInvalidParsingModule) XCTAssertEqual(status, .invalidParsingModule) XCTAssertEqual(status?.description, "The parsing module was not valid.") } do { let status = Status(rawValue: errSecDatabaseLocked) XCTAssertEqual(status, .databaseLocked) XCTAssertEqual(status?.description, "The database is locked.") } do { let status = Status(rawValue: errSecDatastoreIsOpen) XCTAssertEqual(status, .datastoreIsOpen) XCTAssertEqual(status?.description, "The data store is open.") } do { let status = Status(rawValue: errSecMissingValue) XCTAssertEqual(status, .missingValue) XCTAssertEqual(status?.description, "A missing value was detected.") } do { let status = Status(rawValue: errSecUnsupportedQueryLimits) XCTAssertEqual(status, .unsupportedQueryLimits) XCTAssertEqual(status?.description, "The query limits are not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumSelectionPreds) XCTAssertEqual(status, .unsupportedNumSelectionPreds) XCTAssertEqual(status?.description, "The number of selection predicates is not supported.") } do { let status = Status(rawValue: errSecUnsupportedOperator) XCTAssertEqual(status, .unsupportedOperator) XCTAssertEqual(status?.description, "The operator is not supported.") } do { let status = Status(rawValue: errSecInvalidDBLocation) XCTAssertEqual(status, .invalidDBLocation) XCTAssertEqual(status?.description, "The database location is not valid.") } do { let status = Status(rawValue: errSecInvalidAccessRequest) XCTAssertEqual(status, .invalidAccessRequest) XCTAssertEqual(status?.description, "The access request is not valid.") } do { let status = Status(rawValue: errSecInvalidIndexInfo) XCTAssertEqual(status, .invalidIndexInfo) XCTAssertEqual(status?.description, "The index information is not valid.") } do { let status = Status(rawValue: errSecInvalidNewOwner) XCTAssertEqual(status, .invalidNewOwner) XCTAssertEqual(status?.description, "The new owner is not valid.") } do { let status = Status(rawValue: errSecInvalidModifyMode) XCTAssertEqual(status, .invalidModifyMode) XCTAssertEqual(status?.description, "The modify mode is not valid.") } do { let status = Status(rawValue: errSecMissingRequiredExtension) XCTAssertEqual(status, .missingRequiredExtension) XCTAssertEqual(status?.description, "A required certificate extension is missing.") } do { let status = Status(rawValue: errSecExtendedKeyUsageNotCritical) XCTAssertEqual(status, .extendedKeyUsageNotCritical) XCTAssertEqual(status?.description, "The extended key usage extension was not marked critical.") } do { let status = Status(rawValue: errSecTimestampMissing) XCTAssertEqual(status, .timestampMissing) XCTAssertEqual(status?.description, "A timestamp was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampInvalid) XCTAssertEqual(status, .timestampInvalid) XCTAssertEqual(status?.description, "The timestamp was not valid.") } do { let status = Status(rawValue: errSecTimestampNotTrusted) XCTAssertEqual(status, .timestampNotTrusted) XCTAssertEqual(status?.description, "The timestamp was not trusted.") } do { let status = Status(rawValue: errSecTimestampServiceNotAvailable) XCTAssertEqual(status, .timestampServiceNotAvailable) XCTAssertEqual(status?.description, "The timestamp service is not available.") } do { let status = Status(rawValue: errSecTimestampBadAlg) XCTAssertEqual(status, .timestampBadAlg) XCTAssertEqual(status?.description, "An unrecognized or unsupported Algorithm Identifier in timestamp.") } do { let status = Status(rawValue: errSecTimestampBadRequest) XCTAssertEqual(status, .timestampBadRequest) XCTAssertEqual(status?.description, "The timestamp transaction is not permitted or supported.") } do { let status = Status(rawValue: errSecTimestampBadDataFormat) XCTAssertEqual(status, .timestampBadDataFormat) XCTAssertEqual(status?.description, "The timestamp data submitted has the wrong format.") } do { let status = Status(rawValue: errSecTimestampTimeNotAvailable) XCTAssertEqual(status, .timestampTimeNotAvailable) XCTAssertEqual(status?.description, "The time source for the Timestamp Authority is not available.") } do { let status = Status(rawValue: errSecTimestampUnacceptedPolicy) XCTAssertEqual(status, .timestampUnacceptedPolicy) XCTAssertEqual(status?.description, "The requested policy is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampUnacceptedExtension) XCTAssertEqual(status, .timestampUnacceptedExtension) XCTAssertEqual(status?.description, "The requested extension is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampAddInfoNotAvailable) XCTAssertEqual(status, .timestampAddInfoNotAvailable) XCTAssertEqual(status?.description, "The additional information requested is not available.") } do { let status = Status(rawValue: errSecTimestampSystemFailure) XCTAssertEqual(status, .timestampSystemFailure) XCTAssertEqual(status?.description, "The timestamp request cannot be handled due to system failure.") } do { let status = Status(rawValue: errSecSigningTimeMissing) XCTAssertEqual(status, .signingTimeMissing) XCTAssertEqual(status?.description, "A signing time was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampRejection) XCTAssertEqual(status, .timestampRejection) XCTAssertEqual(status?.description, "A timestamp transaction was rejected.") } do { let status = Status(rawValue: errSecTimestampWaiting) XCTAssertEqual(status, .timestampWaiting) XCTAssertEqual(status?.description, "A timestamp transaction is waiting.") } do { let status = Status(rawValue: errSecTimestampRevocationWarning) XCTAssertEqual(status, .timestampRevocationWarning) XCTAssertEqual(status?.description, "A timestamp authority revocation warning was issued.") } do { let status = Status(rawValue: errSecTimestampRevocationNotification) XCTAssertEqual(status, .timestampRevocationNotification) XCTAssertEqual(status?.description, "A timestamp authority revocation notification was issued.") } #endif } } ================================================ FILE: External/KeychainAccess/Lib/KeychainAccessTests/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType BNDL CFBundleShortVersionString 1.0.0 CFBundleSignature ???? CFBundleVersion 1 ================================================ FILE: External/KeychainAccess/Lib/KeychainAccessTests/KeychainAccessTests.swift ================================================ // // KeychainAccessTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import XCTest import KeychainAccess class KeychainAccessTests: XCTestCase { override func setUp() { super.setUp() do { try Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").removeAll() } catch {} do { try Keychain(service: "Twitter").removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com")!, protocolType: .https).removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com:443")!, protocolType: .https).removeAll() } catch {} do { try Keychain().removeAll() } catch {} } override func tearDown() { super.tearDown() } // MARK: func testGenericPassword() { do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testGenericPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testInternetPassword() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testInternetPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testDefaultInitializer() { let keychain = Keychain() XCTAssertEqual(keychain.service, Bundle.main.bundleIdentifier) let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertNil(keychain.accessGroup) } func testInitializerWithService() { let keychain = Keychain(service: "com.example.github-token") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertNil(keychain.accessGroup) } func testInitializerWithAccessGroup() { let keychain = Keychain(accessGroup: "27AEDK3C9F.shared") let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServiceAndAccessGroup() { let keychain = Keychain(service: "com.example.github-token", accessGroup: "27AEDK3C9F.shared") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServer() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } do { let keychain = Keychain(server: url, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } } func testInitializerWithServerAndAuthenticationType() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } do { let keychain = Keychain(server: url, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } } // MARK: func testContains() { let keychain = Keychain(service: "Twitter") XCTAssertFalse(try! keychain.contains("username"), "not stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertTrue(try! keychain.contains("password"), "stored password") } // MARK: func testSetString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") } func testSetStringWithLabel() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") } catch { XCTFail("error occurred") } } func testSetStringWithComment() { let keychain = Keychain(service: "Twitter") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetStringWithLabelAndComment() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") } func testStringConversionError() { let keychain = Keychain(service: "Twitter") let length = 256 let data = NSMutableData(length: length)! let bytes = data.mutableBytes.bindMemory(to: UInt8.self, capacity: length) _ = SecRandomCopyBytes(kSecRandomDefault, length, bytes) do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch let error as NSError { XCTAssertEqual(error.domain, KeychainAccessErrorDomain) XCTAssertEqual(error.code, Int(Status.conversionError.rawValue)) XCTAssertEqual(error.userInfo[NSLocalizedDescriptionKey] as! String, Status.conversionError.localizedDescription) } catch { XCTFail("unexpected error occurred") } do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch Status.conversionError { XCTAssertTrue(true) } catch { XCTFail("unexpected error occurred") } } func testGetPersistentRef() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNil(persistentRef) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNotNil(persistentRef) } catch { XCTFail("error occurred") } } #if os(iOS) || os(tvOS) func testSetAttributes() { do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true let keychain = Keychain(service: "Twitter") .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if targetEnvironment(macCatalyst) XCTAssertNotNil(attributes?.accessControl) #else if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 3, patchVersion: 0)) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertEqual(attributes?.service, "Twitter") XCTAssertNil(attributes?.generic) XCTAssertNil(attributes?.securityDomain) XCTAssertNil(attributes?.server) XCTAssertNil(attributes?.`protocol`) XCTAssertNil(attributes?.authenticationType) XCTAssertNil(attributes?.port) XCTAssertNil(attributes?.path) XCTAssertEqual(attributes?[String(kSecClass)] as? String, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?[String(kSecValueData)] as? Data, "password1234".data(using: .utf8)) } catch { XCTFail("error occurred") } } do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true attributes[String(kSecAttrSecurityDomain)] = "securitydomain" let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } do { keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") keychain["kishikawakatsumi"] = "1234password" XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "updated password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "1234password".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes([String(kSecAttrDescription): "Updated Description"]) XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "stored password") keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "updated password") let attributes = keychain[attributes: "kishikawakatsumi"] XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Updated Description") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } } } #endif func testRemoveString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertEqual(try! keychain.get("password"), "password1234", "left password") do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertNil(try! keychain.get("password"), "removed password") } func testRemoveData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") do { try keychain.remove("JSONData") } catch {} XCTAssertNil(try! keychain.getData("JSONData"), "removed JSON data") } // MARK: func testSubscripting() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["username"], "not stored username") XCTAssertNil(keychain["password"], "not stored password") XCTAssertNil(keychain[string: "username"], "not stored username") XCTAssertNil(keychain[string: "password"], "not stored password") keychain["username"] = "kishikawakatsumi" XCTAssertEqual(keychain["username"], "kishikawakatsumi", "stored username") XCTAssertEqual(keychain[string: "username"], "kishikawakatsumi", "stored username") keychain["password"] = "password1234" XCTAssertEqual(keychain["password"], "password1234", "stored password") XCTAssertEqual(keychain[string: "password"], "password1234", "stored password") keychain[string: "username"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertEqual(keychain["password"], "password1234", "left password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertEqual(keychain[string: "password"], "password1234", "left password") keychain[string: "password"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertNil(keychain["password"], "removed password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertNil(keychain[string: "password"], "removed password") let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) XCTAssertNil(keychain[data:"JSONData"], "not stored JSON data") keychain[data: "JSONData"] = JSONData XCTAssertEqual(keychain[data: "JSONData"], JSONData, "stored JSON data") keychain[data: "JSONData"] = nil XCTAssertNil(keychain[data:"JSONData"], "removed JSON data") } // MARK: func testErrorHandling() { do { let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(service: "Twitter") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain() try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("password_1234", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "password_1234") } catch { XCTFail("error occurred") } } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("1234_password", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "1234_password") } catch { XCTFail("error occurred") } } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } do { try keychain.remove("password") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } } } // MARK: func testSetStringWithCustomService() { let username_1 = "kishikawakatsumi" let password_1 = "password1234" let username_2 = "kishikawa_katsumi" let password_2 = "password_1234" let username_3 = "k_katsumi" let password_3 = "12341234" let service_1 = "" let service_2 = "com.kishikawakatsumi.KeychainAccess" let service_3 = "example.com" do { try Keychain().removeAll() } catch {} do { try Keychain(service: service_1).removeAll() } catch {} do { try Keychain(service: service_2).removeAll() } catch {} do { try Keychain(service: service_3).removeAll() } catch {} XCTAssertNil(try! Keychain().get("username"), "not stored username") XCTAssertNil(try! Keychain().get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_1).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain().set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_1).set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_2).set(username_2, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_3).set(username_3, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "stored username") do { try Keychain().set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_1).set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_2).set(password_2, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_3).set(password_3, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "stored password") do { try Keychain().remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "left username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_1).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_2).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_3).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "removed username") do { try Keychain().remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "left password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_1).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_2).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_3).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") } // MARK: func testProperties() { guard #available(OSX 10.10, *) else { return } let keychain = Keychain() XCTAssertEqual(keychain.synchronizable, false) XCTAssertEqual(keychain.synchronizable(true).synchronizable, true) XCTAssertEqual(keychain.synchronizable(false).synchronizable, false) XCTAssertEqual(keychain.accessibility(.afterFirstUnlock).accessibility, Accessibility.afterFirstUnlock) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).accessibility, Accessibility.whenPasscodeSetThisDeviceOnly) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).authenticationPolicy, AuthenticationPolicy.userPresence) XCTAssertNil(keychain.label) XCTAssertEqual(keychain.label("Label").label, "Label") XCTAssertNil(keychain.comment) XCTAssertEqual(keychain.comment("Comment").comment, "Comment") XCTAssertEqual(keychain.authenticationPrompt("Prompt").authenticationPrompt, "Prompt") } // MARK: func testAllKeys() { do { let keychain = Keychain() keychain["key1"] = "value1" keychain["key2"] = "value2" keychain["key3"] = "value3" let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 3) XCTAssertEqual(allKeys.sorted(), ["key1", "key2", "key3"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 3) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) let service: String let accessGroup: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["service"] as? String, service) XCTAssertEqual(sortedItems[0]["value"] as? String, "value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, service) XCTAssertEqual(sortedItems[1]["value"] as? String, "value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[2]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[2]["service"] as? String, service) XCTAssertEqual(sortedItems[2]["value"] as? String, "value3") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(service: "service1") try! keychain .synchronizable(true) .accessibility(.whenUnlockedThisDeviceOnly) .set("service1_value1", key: "service1_key1") try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("service1_value2", key: "service1_key2") let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["service1_key1", "service1_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["value"] as? String, "service1_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "WhenUnlockedThisDeviceOnly") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["value"] as? String, "service1_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(server: "https://google.com", protocolType: .https) #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(false) .accessibility(.alwaysThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #else try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #endif #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(true) .accessibility(.always) .set("google.com_value2", key: "google.com_key2") #else try! keychain .synchronizable(true) .accessibility(.afterFirstUnlock) .set("google.com_value2", key: "google.com_key2") #endif let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["google.com_key1", "google.com_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["value"] as? String, "google.com_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AlwaysThisDeviceOnly") #endif XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[1]["value"] as? String, "google.com_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "Always") #endif #else XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #endif } #if !os(OSX) do { let allKeys = Keychain.allKeys(.genericPassword) XCTAssertEqual(allKeys.count, 5) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedKeys[0].0, service) XCTAssertEqual(sortedKeys[0].1, "key1") XCTAssertEqual(sortedKeys[1].0, service) XCTAssertEqual(sortedKeys[1].1, "key2") XCTAssertEqual(sortedKeys[2].0, service) XCTAssertEqual(sortedKeys[2].1, "key3") XCTAssertEqual(sortedKeys[3].0, "service1") XCTAssertEqual(sortedKeys[3].1, "service1_key1") XCTAssertEqual(sortedKeys[4].0, "service1") XCTAssertEqual(sortedKeys[4].1, "service1_key2") } do { let allKeys = Keychain.allKeys(.internetPassword) XCTAssertEqual(allKeys.count, 2) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } XCTAssertEqual(sortedKeys[0].0, "google.com") XCTAssertEqual(sortedKeys[0].1, "google.com_key1") XCTAssertEqual(sortedKeys[1].0, "google.com") XCTAssertEqual(sortedKeys[1].1, "google.com_key2") } #endif } func testDescription() { do { let keychain = Keychain() XCTAssertEqual(keychain.description, "[]") XCTAssertEqual(keychain.debugDescription, "[]") } } // MARK: func testAuthenticationPolicy() { guard #available(iOS 9.0, OSX 10.11, *) else { return } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #if os(iOS) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .or, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .and, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } #endif #if os(OSX) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #endif } func testIgnoringAttributeSynchronizable() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("password1234", key: "password", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("password1234_synchronizable", key: "password", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") do { try keychainSynchronizable.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") } func testIgnoringAttributeSynchronizableBackwardCompatibility() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi", "stored username") do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234", "stored password") do { try keychainSynchronizable.set("password1234_synchronizable", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") } } ================================================ FILE: External/KeychainAccess/Lib/KeychainAccessTests/SharedCredentialTests.swift ================================================ // // SharedCredentialTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class SharedCredentialTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testGetSharedPassword() { do { let expectation = self.expectation(description: "getSharedPassword") let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https) keychain.getSharedPassword("kishikawakatsumi") { (password, error) -> () in XCTAssertNil(password) XCTAssertNotNil(error) expectation.fulfill() } waitForExpectations(timeout: 10.0, handler: nil) } do { let expectation = self.expectation(description: "getSharedPassword") let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https) keychain.getSharedPassword { (account, password, error) -> () in XCTAssertNil(account) XCTAssertNil(password) XCTAssertNotNil(error) expectation.fulfill() } waitForExpectations(timeout: 10.0, handler: nil) } } func testGeneratePassword() { do { var passwords = Set() for _ in 0...100_000 { let password = Keychain.generatePassword() #if swift(>=4.2) XCTAssertEqual(password.count, "xxx-xxx-xxx-xxx".count) #else XCTAssertEqual(password.characters.count, "xxx-xxx-xxx-xxx".characters.count) #endif XCTAssertFalse(passwords.contains(password)) passwords.insert(password) } } } } ================================================ FILE: External/KeychainAccess/Lib/Rakefile ================================================ require 'xcjobs' require 'json' def xcode_version `xcodebuild -version`.split("\n").first.scan(/\d+/).join('.') end def destinations(platform: 'iphonesimulator') if platform == 'iphonesimulator' if xcode_version.start_with?('11') [ 'name=iPhone 11 Pro,OS=13.0' ] elsif xcode_version.start_with?('10') [ 'name=iPhone 5s,OS=12.0', 'name=iPhone 6,OS=12.0', 'name=iPhone 6s Plus,OS=12.0', 'name=iPhone SE,OS=12.0', 'name=iPad Air 2,OS=12.0', 'name=iPad Pro (9.7-inch),OS=12.0', 'name=iPad Pro (12.9-inch),OS=12.0' ] elsif xcode_version.start_with?('9.4') [ 'name=iPhone 5s,OS=11.4', 'name=iPhone 6,OS=11.4', 'name=iPhone 6s Plus,OS=11.4', 'name=iPhone SE,OS=11.4', 'name=iPad Air 2,OS=11.4', 'name=iPad Pro (9.7-inch),OS=11.4', 'name=iPad Pro (12.9-inch),OS=11.4' ] elsif xcode_version.start_with?('9.3') [ 'name=iPhone 5s,OS=11.0.1', 'name=iPhone 5s,OS=11.1', 'name=iPhone 6,OS=11.2', 'name=iPhone 6s Plus,OS=11.3', 'name=iPhone SE,OS=11.0.1', 'name=iPad Air 2,OS=11.1', 'name=iPad Pro (9.7-inch),OS=11.2', 'name=iPad Pro (12.9-inch),OS=11.3' ] elsif xcode_version.start_with?('9.2') [ 'name=iPhone 5s,OS=11.2', 'name=iPhone 5s,OS=11.2', 'name=iPhone 6,OS=11.2', 'name=iPhone 6s Plus,OS=11.2', 'name=iPhone SE,OS=11.2', 'name=iPad Air 2,OS=11.2', 'name=iPad Pro (9.7-inch),OS=11.2', 'name=iPad Pro (12.9-inch),OS=11.2' ] elsif xcode_version.start_with?('9.1') [ 'name=iPhone 5s,OS=11.1', 'name=iPhone 5s,OS=11.1', 'name=iPhone 6,OS=11.1', 'name=iPhone 6s Plus,OS=11.1', 'name=iPhone SE,OS=11.1', 'name=iPad Air 2,OS=11.1', 'name=iPad Pro (9.7-inch),OS=11.1', 'name=iPad Pro (12.9-inch),OS=11.1' ] elsif xcode_version.start_with?('9') [ 'name=iPhone 5,OS=10.0', 'name=iPhone 5s,OS=10.0', 'name=iPhone 6,OS=10.0', 'name=iPhone 6s Plus,OS=10.3.1', 'name=iPhone SE,OS=10.3.1', 'name=iPhone 7,OS=11.0', 'name=iPad Air 2,OS=10.0', 'name=iPad Pro (9.7-inch),OS=10.0', 'name=iPad Pro (12.9-inch),OS=10.0', 'name=iPad Pro (10.5-inch),OS=11.0' ] else [ 'name=iPhone 5,OS=10.0', 'name=iPhone 5s,OS=10.0', 'name=iPhone 6,OS=10.0', 'name=iPhone 6s Plus,OS=10.3.1', 'name=iPhone SE,OS=10.3.1', 'name=iPad Air 2,OS=10.0', 'name=iPad Pro (9.7-inch),OS=10.0', 'name=iPad Pro (12.9-inch),OS=10.0' ] end elsif platform == 'watchsimulator' if xcode_version.start_with?('11') [ 'name=Apple Watch Series 4 - 44mm,OS=6.0' ] elsif xcode_version.start_with?('10') [ 'name=Apple Watch Series 4 - 40mm,OS=5.0', 'name=Apple Watch Series 4 - 44mm,OS=5.0' ] elsif xcode_version.start_with?('9.4') [ 'name=Apple Watch Series 3 - 38mm,OS=4.3', 'name=Apple Watch Series 3 - 42mm,OS=4.3' ] elsif xcode_version.start_with?('9.3') [ 'name=Apple Watch - 38mm,OS=4.3', 'name=Apple Watch - 42mm,OS=4.3', 'name=Apple Watch Series 2 - 42mm,OS=4.3' ] elsif xcode_version.start_with?('9.2') [ 'name=Apple Watch - 38mm,OS=4.2', 'name=Apple Watch - 42mm,OS=4.2', 'name=Apple Watch Series 2 - 42mm,OS=4.2' ] elsif xcode_version.start_with?('9.1') [ 'name=Apple Watch - 38mm,OS=4.1', 'name=Apple Watch - 42mm,OS=4.1', 'name=Apple Watch Series 2 - 42mm,OS=4.1' ] elsif xcode_version.start_with?('9') [ 'name=Apple Watch - 38mm,OS=3.2', 'name=Apple Watch - 42mm,OS=4.0', 'name=Apple Watch Series 2 - 42mm,OS=4.0' ] else [ 'name=Apple Watch - 38mm,OS=3.2', 'name=Apple Watch Series 2 - 42mm,OS=3.2' ] end elsif platform == 'appletvsimulator' if xcode_version.start_with?('11') [ 'name=Apple TV 4K,OS=13.0' ] elsif xcode_version.start_with?('10') [ 'name=Apple TV 4K,OS=12.0' ] elsif xcode_version.start_with?('9.4') [ 'name=Apple TV 4K,OS=11.4' ] elsif xcode_version.start_with?('9.3') [ 'name=Apple TV 4K,OS=11.3' ] elsif xcode_version.start_with?('9.2') [ 'name=Apple TV 4K,OS=11.2' ] elsif xcode_version.start_with?('9.1') [ 'name=Apple TV 4K,OS=11.1' ] elsif xcode_version.start_with?('9') [ 'name=Apple TV 1080p,OS=10.2', 'name=Apple TV 1080p,OS=11.0' ] else [ 'name=Apple TV 1080p,OS=10.2' ] end else [ 'platform=OS X,arch=x86_64' ] end end def supportedPlatforms ['macosx', 'iphoneos', 'iphonesimulator', 'watchos', 'watchsimulator', 'appletvos', 'appletvsimulator'] end def configurations ['Debug', 'Release'] end desc "build for all platforms" task :build do |t| supportedPlatforms .product(configurations) .map { |platform, configuration| Rake::Task["build:#{platform}:#{configuration.downcase}"] } .map(&:invoke) end namespace :build do supportedPlatforms.product(configurations).each do |platform, configuration| XCJobs::Build.new("#{platform}:#{configuration.downcase}") do |t| t.project = 'KeychainAccess' t.scheme = 'KeychainAccess' t.sdk = platform t.configuration = configuration t.build_dir = 'build' t.hide_shell_script_environment = true t.formatter = 'xcpretty -c' if ENV['CI'] t.add_build_setting('CODE_SIGN_IDENTITY', '') t.add_build_setting('CODE_SIGNING_REQUIRED', 'NO') end if xcode_version.start_with?('11') t.add_build_setting('SWIFT_VERSION', '5.1') if platform == 'iphonesimulator' t.add_destination('name=iPhone 11,OS=13.0') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch Series 4 - 44mm,OS=6.0') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=13.0') end elsif xcode_version.start_with?('10.3') t.add_build_setting('SWIFT_VERSION', '5.0') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=12.0') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch Series 4 - 44mm,OS=5.0') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=12.0') end elsif xcode_version.start_with?('10') t.add_build_setting('SWIFT_VERSION', '4.2') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=12.0') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch Series 4 - 44mm,OS=5.0') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=12.0') end elsif xcode_version.start_with?('9.4') t.add_build_setting('SWIFT_VERSION', '4.1') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=11.4') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch Series 3 - 42mm,OS=4.3') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=11.4') end elsif xcode_version.start_with?('9.3') t.add_build_setting('SWIFT_VERSION', '4.1') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=11.3') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch - 42mm,OS=4.3') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=11.3') end elsif xcode_version.start_with?('9.2') t.add_build_setting('SWIFT_VERSION', '4.0') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=11.2') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch - 42mm,OS=4.2') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=11.2') end elsif xcode_version.start_with?('9.1') t.add_build_setting('SWIFT_VERSION', '4.0') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=11.1') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch - 42mm,OS=4.1') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 4K,OS=11.1') end elsif xcode_version.start_with?('9') t.add_build_setting('SWIFT_VERSION', '4.0') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=11.0') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch - 42mm,OS=4.0') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 1080p,OS=11.0') end else t.add_build_setting('SWIFT_VERSION', '3.0') if platform == 'iphonesimulator' t.add_destination('name=iPhone 7,OS=10.3.1') elsif platform == 'watchsimulator' t.add_destination('name=Apple Watch - 42mm,OS=3.2') elsif platform == 'appletvsimulator' t.add_destination('name=Apple TV 1080p,OS=10.0') end end end end task :carthage do sh %[echo 'github \"kishikawakatsumi/KeychainAccess\"' > Cartfile] if xcode_version.start_with?('11') sh %[echo SWIFT_VERSION=\"5.1\" > swift.xcconfig] elsif xcode_version.start_with?('10.3') sh %[echo SWIFT_VERSION=\"5.0\" > swift.xcconfig] elsif xcode_version.start_with?('10') sh %[echo SWIFT_VERSION=\"4.2\" > swift.xcconfig] elsif xcode_version.start_with?('9.4') sh %[echo SWIFT_VERSION=\"4.1\" > swift.xcconfig] elsif xcode_version.start_with?('9.3') sh %[echo SWIFT_VERSION=\"4.1\" > swift.xcconfig] elsif xcode_version.start_with?('9') sh %[echo SWIFT_VERSION=\"4.0\" > swift.xcconfig] else sh %[echo SWIFT_VERSION=\"3.0\" > swift.xcconfig] end sh %[XCODE_XCCONFIG_FILE=`pwd`/swift.xcconfig carthage update --no-use-binaries] sh %[find . -name '*.bcsymbolmap' | xargs grep swiftlang] end end namespace :test do supportedPlatforms .select { |platform| platform == 'macosx' || platform == 'iphonesimulator' || platform == 'appletvsimulator' } .each do |platform| task platform do |t| configurations.each do |configuration| destinations(platform: platform) .map { |destination| Rake::Task["test:#{platform}:#{configuration.downcase}:#{destination}"] } .map(&:invoke) end end end end namespace :test do supportedPlatforms .select { |platform| platform == 'macosx' || platform == 'iphonesimulator' || platform == 'appletvsimulator' } .product(configurations) .each do |platform, configuration| destinations(platform: platform).each do |destination| XCJobs::Test.new("#{platform}:#{configuration.downcase}:#{destination}") do |t| t.project = 'KeychainAccess' t.scheme = 'KeychainAccess' t.sdk = platform t.configuration = configuration t.add_destination(destination) t.coverage = true t.build_dir = 'build' t.hide_shell_script_environment = true if xcode_version.start_with?('11') t.add_build_setting('SWIFT_VERSION', '5.1') elsif xcode_version.start_with?('10.3') t.add_build_setting('SWIFT_VERSION', '5.0') elsif xcode_version.start_with?('10') t.add_build_setting('SWIFT_VERSION', '4.2') elsif xcode_version.start_with?('9.4') t.add_build_setting('SWIFT_VERSION', '4.1') elsif xcode_version.start_with?('9.3') t.add_build_setting('SWIFT_VERSION', '4.1') elsif xcode_version.start_with?('9') t.add_build_setting('SWIFT_VERSION', '4.0') else t.add_build_setting('SWIFT_VERSION', '3.0') end t.after_action do build_coverage_reports() puts `curl -L https://codecov.io/bash | bash -s -- -f 'coverage.txt'` end end end end end def build_coverage_reports() project_name = 'KeychainAccess' profdata = Dir.glob(File.join('build', '/**/Coverage.profdata')).first Dir.glob(File.join('build', "/**/#{project_name}")) do |target| output = `xcrun llvm-cov report -instr-profile "#{profdata}" "#{target}" -arch=x86_64` if $?.success? puts output `xcrun llvm-cov show -instr-profile "#{profdata}" "#{target}" -arch=x86_64 -use-color=0 > coverage.txt` break end end end ================================================ FILE: External/KeychainAccess/Lib/Scripts/add_key.sh ================================================ #!/bin/sh security create-keychain -p travis build.keychain security default-keychain -s build.keychain security unlock-keychain -p travis build.keychain security set-keychain-settings -t 3600 -l ~/Library/Keychains/build.keychain security import ./Lib/Certificates/apple.cer -k ~/Library/Keychains/build.keychain -T /usr/bin/codesign security import ./Lib/Certificates/ios_developer.p12 -k ~/Library/Keychains/build.keychain -P $PASSPHRASE -T /usr/bin/codesign security import ./Lib/Certificates/developer_id_app.p12 -k ~/Library/Keychains/build.keychain -P $PASSPHRASE -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k travis build.keychain mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles cp ./Lib/Certificates/*.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ cp ./Lib/Certificates/*.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/ ================================================ FILE: External/KeychainAccess/Lib/Scripts/decode_cert.sh ================================================ #!/bin/sh CERT_DIR="./Lib/Certificates" FILES=('ios_developer.p12' 'developer_id_app.p12' 'iOS_Development.mobileprovision'\ 'tvOS_Development.mobileprovision' 'KeychainAccess_Tests.provisionprofile') for file in ${FILES[@]}; do openssl aes-256-cbc -k "$ENCRYPTION_SECRET" -in "$CERT_DIR"/"$file".enc -d -a -out "$CERT_DIR"/"$file" done ================================================ FILE: External/KeychainAccess/Lib/TestHost/AppDelegate.swift ================================================ // // AppDelegate.swift // TestHost // // Created by kishikawa katsumi on 7/10/16. // Copyright © 2016 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #if os(OSX) import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var window: NSWindow! func applicationDidFinishLaunching(aNotification: NSNotification) {} } #else import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? #if swift(>=4.2) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { return true } #else func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { return true } #endif } #endif ================================================ FILE: External/KeychainAccess/Lib/TestHost/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: External/KeychainAccess/Lib/TestHost/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 LSRequiresIPhoneOS UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIRequiresFullScreen NSPrincipalClass $(PRINCIPAL_CLASS) ================================================ FILE: External/KeychainAccess/Lib/TestHost/TestHost.entitlements ================================================ keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.KeychainAccess.TestHost $(AppIdentifierPrefix)shared ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/EnumTests.swift ================================================ // // EnumTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class EnumTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testItemClass() { do { let itemClass = ItemClass(rawValue: kSecClassGenericPassword as String) XCTAssertEqual(itemClass, .genericPassword) XCTAssertEqual(itemClass?.description, "GenericPassword") } do { let itemClass = ItemClass(rawValue: kSecClassInternetPassword as String) XCTAssertEqual(itemClass, .internetPassword) XCTAssertEqual(itemClass?.description, "InternetPassword") } do { let itemClass = ItemClass(rawValue: kSecClassCertificate as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassKey as String) XCTAssertNil(itemClass) } do { let itemClass = ItemClass(rawValue: kSecClassIdentity as String) XCTAssertNil(itemClass) } } func testProtocolType() { do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTP as String) XCTAssertEqual(protocolType, .ftp) XCTAssertEqual(protocolType?.description, "FTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPAccount as String) XCTAssertEqual(protocolType, .ftpAccount) XCTAssertEqual(protocolType?.description, "FTPAccount") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTP as String) XCTAssertEqual(protocolType, .http) XCTAssertEqual(protocolType?.description, "HTTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRC as String) XCTAssertEqual(protocolType, .irc) XCTAssertEqual(protocolType?.description, "IRC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTP as String) XCTAssertEqual(protocolType, .nntp) XCTAssertEqual(protocolType?.description, "NNTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3 as String) XCTAssertEqual(protocolType, .pop3) XCTAssertEqual(protocolType?.description, "POP3") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMTP as String) XCTAssertEqual(protocolType, .smtp) XCTAssertEqual(protocolType?.description, "SMTP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSOCKS as String) XCTAssertEqual(protocolType, .socks) XCTAssertEqual(protocolType?.description, "SOCKS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAP as String) XCTAssertEqual(protocolType, .imap) XCTAssertEqual(protocolType?.description, "IMAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAP as String) XCTAssertEqual(protocolType, .ldap) XCTAssertEqual(protocolType?.description, "LDAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAppleTalk as String) XCTAssertEqual(protocolType, .appleTalk) XCTAssertEqual(protocolType?.description, "AppleTalk") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolAFP as String) XCTAssertEqual(protocolType, .afp) XCTAssertEqual(protocolType?.description, "AFP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnet as String) XCTAssertEqual(protocolType, .telnet) XCTAssertEqual(protocolType?.description, "Telnet") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSSH as String) XCTAssertEqual(protocolType, .ssh) XCTAssertEqual(protocolType?.description, "SSH") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPS as String) XCTAssertEqual(protocolType, .ftps) XCTAssertEqual(protocolType?.description, "FTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPS as String) XCTAssertEqual(protocolType, .https) XCTAssertEqual(protocolType?.description, "HTTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPProxy as String) XCTAssertEqual(protocolType, .httpProxy) XCTAssertEqual(protocolType?.description, "HTTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolHTTPSProxy as String) XCTAssertEqual(protocolType, .httpsProxy) XCTAssertEqual(protocolType?.description, "HTTPSProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolFTPProxy as String) XCTAssertEqual(protocolType, .ftpProxy) XCTAssertEqual(protocolType?.description, "FTPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolSMB as String) XCTAssertEqual(protocolType, .smb) XCTAssertEqual(protocolType?.description, "SMB") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSP as String) XCTAssertEqual(protocolType, .rtsp) XCTAssertEqual(protocolType?.description, "RTSP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolRTSPProxy as String) XCTAssertEqual(protocolType, .rtspProxy) XCTAssertEqual(protocolType?.description, "RTSPProxy") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolDAAP as String) XCTAssertEqual(protocolType, .daap) XCTAssertEqual(protocolType?.description, "DAAP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolEPPC as String) XCTAssertEqual(protocolType, .eppc) XCTAssertEqual(protocolType?.description, "EPPC") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIPP as String) XCTAssertEqual(protocolType, .ipp) XCTAssertEqual(protocolType?.description, "IPP") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolNNTPS as String) XCTAssertEqual(protocolType, .nntps) XCTAssertEqual(protocolType?.description, "NNTPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolLDAPS as String) XCTAssertEqual(protocolType, .ldaps) XCTAssertEqual(protocolType?.description, "LDAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolTelnetS as String) XCTAssertEqual(protocolType, .telnetS) XCTAssertEqual(protocolType?.description, "TelnetS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIMAPS as String) XCTAssertEqual(protocolType, .imaps) XCTAssertEqual(protocolType?.description, "IMAPS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolIRCS as String) XCTAssertEqual(protocolType, .ircs) XCTAssertEqual(protocolType?.description, "IRCS") } do { let protocolType = ProtocolType(rawValue: kSecAttrProtocolPOP3S as String) XCTAssertEqual(protocolType, .pop3S) XCTAssertEqual(protocolType?.description, "POP3S") } } func testAuthenticationType() { do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeNTLM as String) XCTAssertEqual(authenticationType, .ntlm) XCTAssertEqual(authenticationType?.description, "NTLM") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeMSN as String) XCTAssertEqual(authenticationType, .msn) XCTAssertEqual(authenticationType?.description, "MSN") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDPA as String) XCTAssertEqual(authenticationType, .dpa) XCTAssertEqual(authenticationType?.description, "DPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeRPA as String) XCTAssertEqual(authenticationType, .rpa) XCTAssertEqual(authenticationType?.description, "RPA") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPBasic as String) XCTAssertEqual(authenticationType, .httpBasic) XCTAssertEqual(authenticationType?.description, "HTTPBasic") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTTPDigest as String) XCTAssertEqual(authenticationType, .httpDigest) XCTAssertEqual(authenticationType?.description, "HTTPDigest") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeHTMLForm as String) XCTAssertEqual(authenticationType, .htmlForm) XCTAssertEqual(authenticationType?.description, "HTMLForm") } do { let authenticationType = AuthenticationType(rawValue: kSecAttrAuthenticationTypeDefault as String) XCTAssertEqual(authenticationType, .default) XCTAssertEqual(authenticationType?.description, "Default") } } func testAccessibility() { guard #available(OSX 10.10, *) else { return } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlocked as String) XCTAssertEqual(accessibility, .whenUnlocked) XCTAssertEqual(accessibility?.description, "WhenUnlocked") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlock as String) XCTAssertEqual(accessibility, .afterFirstUnlock) XCTAssertEqual(accessibility?.description, "AfterFirstUnlock") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlways as String) XCTAssertEqual(accessibility, .always) XCTAssertEqual(accessibility?.description, "Always") } #endif do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenPasscodeSetThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenPasscodeSetThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleWhenUnlockedThisDeviceOnly as String) XCTAssertEqual(accessibility, .whenUnlockedThisDeviceOnly) XCTAssertEqual(accessibility?.description, "WhenUnlockedThisDeviceOnly") } do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly as String) XCTAssertEqual(accessibility, .afterFirstUnlockThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AfterFirstUnlockThisDeviceOnly") } #if !targetEnvironment(macCatalyst) do { let accessibility = Accessibility(rawValue: kSecAttrAccessibleAlwaysThisDeviceOnly as String) XCTAssertEqual(accessibility, .alwaysThisDeviceOnly) XCTAssertEqual(accessibility?.description, "AlwaysThisDeviceOnly") } #endif } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/ErrorTypeTests.swift ================================================ // // ErrorTypeTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 10/12/15. // Copyright © 2015 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import KeychainAccess class ErrorTypeTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func testErrorType() { do { let status = Status(rawValue: errSecSuccess) XCTAssertEqual(status, .success) XCTAssertEqual(status?.description, "No error.") } do { let status = Status(rawValue: errSecUnimplemented) XCTAssertEqual(status, .unimplemented) XCTAssertEqual(status?.description, "Function or operation not implemented.") } #if os(OSX) do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } #endif do { let status = Status(rawValue: errSecIO) XCTAssertEqual(status, .io) XCTAssertEqual(status?.description, "I/O error (bummers)") } #if os(iOS) do { let status = Status(rawValue: errSecOpWr) XCTAssertEqual(status, .opWr) XCTAssertEqual(status?.description, "file already open with with write permission") } #endif do { let status = Status(rawValue: errSecParam) XCTAssertEqual(status, .param) XCTAssertEqual(status?.description, "One or more parameters passed to a function were not valid.") } #if os(OSX) do { let status = Status(rawValue: errSecWrPerm) XCTAssertEqual(status, .wrPerm) XCTAssertEqual(status?.description, "write permissions error") } #endif do { let status = Status(rawValue: errSecAllocate) XCTAssertEqual(status, .allocate) XCTAssertEqual(status?.description, "Failed to allocate memory.") } do { let status = Status(rawValue: errSecUserCanceled) XCTAssertEqual(status, .userCanceled) XCTAssertEqual(status?.description, "User canceled the operation.") } do { let status = Status(rawValue: errSecBadReq) XCTAssertEqual(status, .badReq) XCTAssertEqual(status?.description, "Bad parameter or invalid state for operation.") } do { let status = Status(rawValue: errSecInternalComponent) XCTAssertEqual(status, .internalComponent) XCTAssertEqual(status?.description, "") } do { let status = Status(rawValue: errSecNotAvailable) XCTAssertEqual(status, .notAvailable) XCTAssertEqual(status?.description, "No keychain is available. You may need to restart your computer.") } #if os(OSX) do { let status = Status(rawValue: errSecReadOnly) XCTAssertEqual(status, .readOnly) XCTAssertEqual(status?.description, "This keychain cannot be modified.") } #endif do { let status = Status(rawValue: errSecAuthFailed) XCTAssertEqual(status, .authFailed) XCTAssertEqual(status?.description, "The user name or passphrase you entered is not correct.") } #if os(OSX) do { let status = Status(rawValue: errSecNoSuchKeychain) XCTAssertEqual(status, .noSuchKeychain) XCTAssertEqual(status?.description, "The specified keychain could not be found.") } do { let status = Status(rawValue: errSecInvalidKeychain) XCTAssertEqual(status, .invalidKeychain) XCTAssertEqual(status?.description, "The specified keychain is not a valid keychain file.") } do { let status = Status(rawValue: errSecDuplicateKeychain) XCTAssertEqual(status, .duplicateKeychain) XCTAssertEqual(status?.description, "A keychain with the same name already exists.") } do { let status = Status(rawValue: errSecDuplicateCallback) XCTAssertEqual(status, .duplicateCallback) XCTAssertEqual(status?.description, "The specified callback function is already installed.") } do { let status = Status(rawValue: errSecInvalidCallback) XCTAssertEqual(status, .invalidCallback) XCTAssertEqual(status?.description, "The specified callback function is not valid.") } #endif do { let status = Status(rawValue: errSecDuplicateItem) XCTAssertEqual(status, .duplicateItem) XCTAssertEqual(status?.description, "The specified item already exists in the keychain.") } do { let status = Status(rawValue: errSecItemNotFound) XCTAssertEqual(status, .itemNotFound) XCTAssertEqual(status?.description, "The specified item could not be found in the keychain.") } #if os(OSX) do { let status = Status(rawValue: errSecBufferTooSmall) XCTAssertEqual(status, .bufferTooSmall) XCTAssertEqual(status?.description, "There is not enough memory available to use the specified item.") } do { let status = Status(rawValue: errSecDataTooLarge) XCTAssertEqual(status, .dataTooLarge) XCTAssertEqual(status?.description, "This item contains information which is too large or in a format that cannot be displayed.") } do { let status = Status(rawValue: errSecNoSuchAttr) XCTAssertEqual(status, .noSuchAttr) XCTAssertEqual(status?.description, "The specified attribute does not exist.") } do { let status = Status(rawValue: errSecInvalidItemRef) XCTAssertEqual(status, .invalidItemRef) XCTAssertEqual(status?.description, "The specified item is no longer valid. It may have been deleted from the keychain.") } do { let status = Status(rawValue: errSecInvalidSearchRef) XCTAssertEqual(status, .invalidSearchRef) XCTAssertEqual(status?.description, "Unable to search the current keychain.") } do { let status = Status(rawValue: errSecNoSuchClass) XCTAssertEqual(status, .noSuchClass) XCTAssertEqual(status?.description, "The specified item does not appear to be a valid keychain item.") } do { let status = Status(rawValue: errSecNoDefaultKeychain) XCTAssertEqual(status, .noDefaultKeychain) XCTAssertEqual(status?.description, "A default keychain could not be found.") } #endif do { let status = Status(rawValue: errSecInteractionNotAllowed) XCTAssertEqual(status, .interactionNotAllowed) XCTAssertEqual(status?.description, "User interaction is not allowed.") } #if os(OSX) do { let status = Status(rawValue: errSecReadOnlyAttr) XCTAssertEqual(status, .readOnlyAttr) XCTAssertEqual(status?.description, "The specified attribute could not be modified.") } do { let status = Status(rawValue: errSecWrongSecVersion) XCTAssertEqual(status, .wrongSecVersion) XCTAssertEqual(status?.description, "This keychain was created by a different version of the system software and cannot be opened.") } do { let status = Status(rawValue: errSecKeySizeNotAllowed) XCTAssertEqual(status, .keySizeNotAllowed) XCTAssertEqual(status?.description, "This item specifies a key size which is too large.") } do { let status = Status(rawValue: errSecNoStorageModule) XCTAssertEqual(status, .noStorageModule) XCTAssertEqual(status?.description, "A required component (data storage module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoCertificateModule) XCTAssertEqual(status, .noCertificateModule) XCTAssertEqual(status?.description, "A required component (certificate module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecNoPolicyModule) XCTAssertEqual(status, .noPolicyModule) XCTAssertEqual(status?.description, "A required component (policy module) could not be loaded. You may need to restart your computer.") } do { let status = Status(rawValue: errSecInteractionRequired) XCTAssertEqual(status, .interactionRequired) XCTAssertEqual(status?.description, "User interaction is required, but is currently not allowed.") } do { let status = Status(rawValue: errSecDataNotAvailable) XCTAssertEqual(status, .dataNotAvailable) XCTAssertEqual(status?.description, "The contents of this item cannot be retrieved.") } do { let status = Status(rawValue: errSecDataNotModifiable) XCTAssertEqual(status, .dataNotModifiable) XCTAssertEqual(status?.description, "The contents of this item cannot be modified.") } do { let status = Status(rawValue: errSecCreateChainFailed) XCTAssertEqual(status, .createChainFailed) XCTAssertEqual(status?.description, "One or more certificates required to validate this certificate cannot be found.") } do { let status = Status(rawValue: errSecInvalidPrefsDomain) XCTAssertEqual(status, .invalidPrefsDomain) XCTAssertEqual(status?.description, "The specified preferences domain is not valid.") } do { let status = Status(rawValue: errSecInDarkWake) XCTAssertEqual(status, .inDarkWake) XCTAssertEqual(status?.description, "In dark wake, no UI possible") } do { let status = Status(rawValue: errSecACLNotSimple) XCTAssertEqual(status, .aclNotSimple) XCTAssertEqual(status?.description, "The specified access control list is not in standard (simple) form.") } do { let status = Status(rawValue: errSecPolicyNotFound) XCTAssertEqual(status, .policyNotFound) XCTAssertEqual(status?.description, "The specified policy cannot be found.") } do { let status = Status(rawValue: errSecInvalidTrustSetting) XCTAssertEqual(status, .invalidTrustSetting) XCTAssertEqual(status?.description, "The specified trust setting is invalid.") } do { let status = Status(rawValue: errSecNoAccessForItem) XCTAssertEqual(status, .noAccessForItem) XCTAssertEqual(status?.description, "The specified item has no access control.") } do { let status = Status(rawValue: errSecInvalidOwnerEdit) XCTAssertEqual(status, .invalidOwnerEdit) XCTAssertEqual(status?.description, "Invalid attempt to change the owner of this item.") } do { let status = Status(rawValue: errSecTrustNotAvailable) XCTAssertEqual(status, .trustNotAvailable) XCTAssertEqual(status?.description, "No trust results are available.") } do { let status = Status(rawValue: errSecUnsupportedFormat) XCTAssertEqual(status, .unsupportedFormat) XCTAssertEqual(status?.description, "Import/Export format unsupported.") } do { let status = Status(rawValue: errSecUnknownFormat) XCTAssertEqual(status, .unknownFormat) XCTAssertEqual(status?.description, "Unknown format in import.") } do { let status = Status(rawValue: errSecKeyIsSensitive) XCTAssertEqual(status, .keyIsSensitive) XCTAssertEqual(status?.description, "Key material must be wrapped for export.") } do { let status = Status(rawValue: errSecMultiplePrivKeys) XCTAssertEqual(status, .multiplePrivKeys) XCTAssertEqual(status?.description, "An attempt was made to import multiple private keys.") } do { let status = Status(rawValue: errSecPassphraseRequired) XCTAssertEqual(status, .passphraseRequired) XCTAssertEqual(status?.description, "Passphrase is required for import/export.") } do { let status = Status(rawValue: errSecInvalidPasswordRef) XCTAssertEqual(status, .invalidPasswordRef) XCTAssertEqual(status?.description, "The password reference was invalid.") } do { let status = Status(rawValue: errSecInvalidTrustSettings) XCTAssertEqual(status, .invalidTrustSettings) XCTAssertEqual(status?.description, "The Trust Settings Record was corrupted.") } do { let status = Status(rawValue: errSecNoTrustSettings) XCTAssertEqual(status, .noTrustSettings) XCTAssertEqual(status?.description, "No Trust Settings were found.") } do { let status = Status(rawValue: errSecPkcs12VerifyFailure) XCTAssertEqual(status, .pkcs12VerifyFailure) XCTAssertEqual(status?.description, "MAC verification failed during PKCS12 import (wrong password?)") } do { let errSecInvalidCertificate: OSStatus = -26265 let status = Status(rawValue: errSecInvalidCertificate) XCTAssertEqual(status, .invalidCertificate) XCTAssertEqual(status?.description, "This certificate could not be decoded.") } do { let status = Status(rawValue: errSecNotSigner) XCTAssertEqual(status, .notSigner) XCTAssertEqual(status?.description, "A certificate was not signed by its proposed parent.") } do { let errSecPolicyDenied: OSStatus = -26270 let status = Status(rawValue: errSecPolicyDenied) XCTAssertEqual(status, .policyDenied) XCTAssertEqual(status?.description, "The certificate chain was not trusted due to a policy not accepting it.") } do { let errSecInvalidKey: OSStatus = -26274 let status = Status(rawValue: errSecInvalidKey) XCTAssertEqual(status, .invalidKey) XCTAssertEqual(status?.description, "The provided key material was not valid.") } #endif do { let status = Status(rawValue: errSecDecode) XCTAssertEqual(status, .decode) XCTAssertEqual(status?.description, "Unable to decode the provided data.") } do { let errSecInternal: OSStatus = -26276 let status = Status(rawValue: errSecInternal) XCTAssertEqual(status, .internal) XCTAssertEqual(status?.description, "An internal error occurred in the Security framework.") } #if os(OSX) do { let status = Status(rawValue: errSecServiceNotAvailable) XCTAssertEqual(status, .serviceNotAvailable) XCTAssertEqual(status?.description, "The required service is not available.") } do { let errSecUnsupportedAlgorithm: OSStatus = -26268 let status = Status(rawValue: errSecUnsupportedAlgorithm) XCTAssertEqual(status, .unsupportedAlgorithm) XCTAssertEqual(status?.description, "An unsupported algorithm was encountered.") } do { let errSecUnsupportedOperation: OSStatus = -26271 let status = Status(rawValue: errSecUnsupportedOperation) XCTAssertEqual(status, .unsupportedOperation) XCTAssertEqual(status?.description, "The operation you requested is not supported by this key.") } do { let errSecUnsupportedPadding: OSStatus = -26273 let status = Status(rawValue: errSecUnsupportedPadding) XCTAssertEqual(status, .unsupportedPadding) XCTAssertEqual(status?.description, "The padding you requested is not supported.") } do { let errSecItemInvalidKey: OSStatus = -34000 let status = Status(rawValue: errSecItemInvalidKey) XCTAssertEqual(status, .itemInvalidKey) XCTAssertEqual(status?.description, "A string key in dictionary is not one of the supported keys.") } do { let errSecItemInvalidKeyType: OSStatus = -34001 let status = Status(rawValue: errSecItemInvalidKeyType) XCTAssertEqual(status, .itemInvalidKeyType) XCTAssertEqual(status?.description, "A key in a dictionary is neither a CFStringRef nor a CFNumberRef.") } do { let errSecItemInvalidValue: OSStatus = -34002 let status = Status(rawValue: errSecItemInvalidValue) XCTAssertEqual(status, .itemInvalidValue) XCTAssertEqual(status?.description, "A value in a dictionary is an invalid (or unsupported) CF type.") } do { let errSecItemClassMissing: OSStatus = -34003 let status = Status(rawValue: errSecItemClassMissing) XCTAssertEqual(status, .itemClassMissing) XCTAssertEqual(status?.description, "No kSecItemClass key was specified in a dictionary.") } do { let errSecItemMatchUnsupported: OSStatus = -34004 let status = Status(rawValue: errSecItemMatchUnsupported) XCTAssertEqual(status, .itemMatchUnsupported) XCTAssertEqual(status?.description, "The caller passed one or more kSecMatch keys to a function which does not support matches.") } do { let errSecUseItemListUnsupported: OSStatus = -34005 let status = Status(rawValue: errSecUseItemListUnsupported) XCTAssertEqual(status, .useItemListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseItemList key to a function which does not support it.") } do { let errSecUseKeychainUnsupported: OSStatus = -34006 let status = Status(rawValue: errSecUseKeychainUnsupported) XCTAssertEqual(status, .useKeychainUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychain key to a function which does not support it.") } do { let errSecUseKeychainListUnsupported: OSStatus = -34007 let status = Status(rawValue: errSecUseKeychainListUnsupported) XCTAssertEqual(status, .useKeychainListUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecUseKeychainList key to a function which does not support it.") } do { let errSecReturnDataUnsupported: OSStatus = -34008 let status = Status(rawValue: errSecReturnDataUnsupported) XCTAssertEqual(status, .returnDataUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnData key to a function which does not support it.") } do { let errSecReturnAttributesUnsupported: OSStatus = -34009 let status = Status(rawValue: errSecReturnAttributesUnsupported) XCTAssertEqual(status, .returnAttributesUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnAttributes key to a function which does not support it.") } do { let errSecReturnRefUnsupported: OSStatus = -34010 let status = Status(rawValue: errSecReturnRefUnsupported) XCTAssertEqual(status, .returnRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnRef key to a function which does not support it.") } do { let errSecReturnPersitentRefUnsupported: OSStatus = -34011 let status = Status(rawValue: errSecReturnPersitentRefUnsupported) XCTAssertEqual(status, .returnPersitentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecReturnPersistentRef key to a function which does not support it.") } do { let errSecValueRefUnsupported: OSStatus = -34012 let status = Status(rawValue: errSecValueRefUnsupported) XCTAssertEqual(status, .valueRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValueRef key to a function which does not support it.") } do { let errSecValuePersistentRefUnsupported: OSStatus = -34013 let status = Status(rawValue: errSecValuePersistentRefUnsupported) XCTAssertEqual(status, .valuePersistentRefUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecValuePersistentRef key to a function which does not support it.") } do { let errSecReturnMissingPointer: OSStatus = -34014 let status = Status(rawValue: errSecReturnMissingPointer) XCTAssertEqual(status, .returnMissingPointer) XCTAssertEqual(status?.description, "The caller passed asked for something to be returned but did not pass in a result pointer.") } do { let errSecMatchLimitUnsupported: OSStatus = -34015 let status = Status(rawValue: errSecMatchLimitUnsupported) XCTAssertEqual(status, .matchLimitUnsupported) XCTAssertEqual(status?.description, "The caller passed in a kSecMatchLimit key to a call which does not support limits.") } do { let errSecItemIllegalQuery: OSStatus = -34016 let status = Status(rawValue: errSecItemIllegalQuery) XCTAssertEqual(status, .itemIllegalQuery) XCTAssertEqual(status?.description, "The caller passed in a query which contained too many keys.") } do { let errSecWaitForCallback: OSStatus = -34017 let status = Status(rawValue: errSecWaitForCallback) XCTAssertEqual(status, .waitForCallback) XCTAssertEqual(status?.description, "This operation is incomplete, until the callback is invoked (not an error).") } do { let errSecMissingEntitlement: OSStatus = -34018 let status = Status(rawValue: errSecMissingEntitlement) XCTAssertEqual(status, .missingEntitlement) XCTAssertEqual(status?.description, "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements.") } do { let errSecUpgradePending: OSStatus = -34019 let status = Status(rawValue: errSecUpgradePending) XCTAssertEqual(status, .upgradePending) XCTAssertEqual(status?.description, "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command.") } do { let errSecMPSignatureInvalid: OSStatus = -25327 let status = Status(rawValue: errSecMPSignatureInvalid) XCTAssertEqual(status, .mpSignatureInvalid) XCTAssertEqual(status?.description, "Signature invalid on MP message") } do { let errSecOTRTooOld: OSStatus = -25328 let status = Status(rawValue: errSecOTRTooOld) XCTAssertEqual(status, .otrTooOld) XCTAssertEqual(status?.description, "Message is too old to use") } do { let errSecOTRIDTooNew: OSStatus = -25329 let status = Status(rawValue: errSecOTRIDTooNew) XCTAssertEqual(status, .otrIDTooNew) XCTAssertEqual(status?.description, "Key ID is too new to use! Message from the future?") } do { let status = Status(rawValue: errSecInsufficientClientID) XCTAssertEqual(status, .insufficientClientID) XCTAssertEqual(status?.description, "The client ID is not correct.") } do { let status = Status(rawValue: errSecDeviceReset) XCTAssertEqual(status, .deviceReset) XCTAssertEqual(status?.description, "A device reset has occurred.") } do { let status = Status(rawValue: errSecDeviceFailed) XCTAssertEqual(status, .deviceFailed) XCTAssertEqual(status?.description, "A device failure has occurred.") } do { let status = Status(rawValue: errSecAppleAddAppACLSubject) XCTAssertEqual(status, .appleAddAppACLSubject) XCTAssertEqual(status?.description, "Adding an application ACL subject failed.") } do { let status = Status(rawValue: errSecApplePublicKeyIncomplete) XCTAssertEqual(status, .applePublicKeyIncomplete) XCTAssertEqual(status?.description, "The public key is incomplete.") } do { let status = Status(rawValue: errSecAppleSignatureMismatch) XCTAssertEqual(status, .appleSignatureMismatch) XCTAssertEqual(status?.description, "A signature mismatch has occurred.") } do { let status = Status(rawValue: errSecAppleInvalidKeyStartDate) XCTAssertEqual(status, .appleInvalidKeyStartDate) XCTAssertEqual(status?.description, "The specified key has an invalid start date.") } do { let status = Status(rawValue: errSecAppleInvalidKeyEndDate) XCTAssertEqual(status, .appleInvalidKeyEndDate) XCTAssertEqual(status?.description, "The specified key has an invalid end date.") } do { let status = Status(rawValue: errSecConversionError) XCTAssertEqual(status, .conversionError) XCTAssertEqual(status?.description, "A conversion error has occurred.") } do { let status = Status(rawValue: errSecAppleSSLv2Rollback) XCTAssertEqual(status, .appleSSLv2Rollback) XCTAssertEqual(status?.description, "A SSLv2 rollback error has occurred.") } do { let status = Status(rawValue: errSecDiskFull) XCTAssertEqual(status, .diskFull) XCTAssertEqual(status?.description, "The disk is full.") } do { let status = Status(rawValue: errSecQuotaExceeded) XCTAssertEqual(status, .quotaExceeded) XCTAssertEqual(status?.description, "The quota was exceeded.") } do { let status = Status(rawValue: errSecFileTooBig) XCTAssertEqual(status, .fileTooBig) XCTAssertEqual(status?.description, "The file is too big.") } do { let status = Status(rawValue: errSecInvalidDatabaseBlob) XCTAssertEqual(status, .invalidDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an invalid blob.") } do { let status = Status(rawValue: errSecInvalidKeyBlob) XCTAssertEqual(status, .invalidKeyBlob) XCTAssertEqual(status?.description, "The specified database has an invalid key blob.") } do { let status = Status(rawValue: errSecIncompatibleDatabaseBlob) XCTAssertEqual(status, .incompatibleDatabaseBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible blob.") } do { let status = Status(rawValue: errSecIncompatibleKeyBlob) XCTAssertEqual(status, .incompatibleKeyBlob) XCTAssertEqual(status?.description, "The specified database has an incompatible key blob.") } do { let status = Status(rawValue: errSecHostNameMismatch) XCTAssertEqual(status, .hostNameMismatch) XCTAssertEqual(status?.description, "A host name mismatch has occurred.") } do { let status = Status(rawValue: errSecUnknownCriticalExtensionFlag) XCTAssertEqual(status, .unknownCriticalExtensionFlag) XCTAssertEqual(status?.description, "There is an unknown critical extension flag.") } do { let status = Status(rawValue: errSecNoBasicConstraints) XCTAssertEqual(status, .noBasicConstraints) XCTAssertEqual(status?.description, "No basic constraints were found.") } do { let status = Status(rawValue: errSecNoBasicConstraintsCA) XCTAssertEqual(status, .noBasicConstraintsCA) XCTAssertEqual(status?.description, "No basic CA constraints were found.") } do { let status = Status(rawValue: errSecInvalidAuthorityKeyID) XCTAssertEqual(status, .invalidAuthorityKeyID) XCTAssertEqual(status?.description, "The authority key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidSubjectKeyID) XCTAssertEqual(status, .invalidSubjectKeyID) XCTAssertEqual(status?.description, "The subject key ID is not valid.") } do { let status = Status(rawValue: errSecInvalidKeyUsageForPolicy) XCTAssertEqual(status, .invalidKeyUsageForPolicy) XCTAssertEqual(status?.description, "The key usage is not valid for the specified policy.") } do { let status = Status(rawValue: errSecInvalidExtendedKeyUsage) XCTAssertEqual(status, .invalidExtendedKeyUsage) XCTAssertEqual(status?.description, "The extended key usage is not valid.") } do { let status = Status(rawValue: errSecInvalidIDLinkage) XCTAssertEqual(status, .invalidIDLinkage) XCTAssertEqual(status?.description, "The ID linkage is not valid.") } do { let status = Status(rawValue: errSecPathLengthConstraintExceeded) XCTAssertEqual(status, .pathLengthConstraintExceeded) XCTAssertEqual(status?.description, "The path length constraint was exceeded.") } do { let status = Status(rawValue: errSecInvalidRoot) XCTAssertEqual(status, .invalidRoot) XCTAssertEqual(status?.description, "The root or anchor certificate is not valid.") } do { let status = Status(rawValue: errSecCRLExpired) XCTAssertEqual(status, .crlExpired) XCTAssertEqual(status?.description, "The CRL has expired.") } do { let status = Status(rawValue: errSecCRLNotValidYet) XCTAssertEqual(status, .crlNotValidYet) XCTAssertEqual(status?.description, "The CRL is not yet valid.") } do { let status = Status(rawValue: errSecCRLNotFound) XCTAssertEqual(status, .crlNotFound) XCTAssertEqual(status?.description, "The CRL was not found.") } do { let status = Status(rawValue: errSecCRLServerDown) XCTAssertEqual(status, .crlServerDown) XCTAssertEqual(status?.description, "The CRL server is down.") } do { let status = Status(rawValue: errSecCRLBadURI) XCTAssertEqual(status, .crlBadURI) XCTAssertEqual(status?.description, "The CRL has a bad Uniform Resource Identifier.") } do { let status = Status(rawValue: errSecUnknownCertExtension) XCTAssertEqual(status, .unknownCertExtension) XCTAssertEqual(status?.description, "An unknown certificate extension was encountered.") } do { let status = Status(rawValue: errSecUnknownCRLExtension) XCTAssertEqual(status, .unknownCRLExtension) XCTAssertEqual(status?.description, "An unknown CRL extension was encountered.") } do { let status = Status(rawValue: errSecCRLNotTrusted) XCTAssertEqual(status, .crlNotTrusted) XCTAssertEqual(status?.description, "The CRL is not trusted.") } do { let status = Status(rawValue: errSecCRLPolicyFailed) XCTAssertEqual(status, .crlPolicyFailed) XCTAssertEqual(status?.description, "The CRL policy failed.") } do { let status = Status(rawValue: errSecIDPFailure) XCTAssertEqual(status, .idpFailure) XCTAssertEqual(status?.description, "The issuing distribution point was not valid.") } do { let status = Status(rawValue: errSecSMIMEEmailAddressesNotFound) XCTAssertEqual(status, .smimeEmailAddressesNotFound) XCTAssertEqual(status?.description, "An email address mismatch was encountered.") } do { let status = Status(rawValue: errSecSMIMEBadExtendedKeyUsage) XCTAssertEqual(status, .smimeBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SMIME was not found.") } do { let status = Status(rawValue: errSecSMIMEBadKeyUsage) XCTAssertEqual(status, .smimeBadKeyUsage) XCTAssertEqual(status?.description, "The key usage is not compatible with SMIME.") } do { let status = Status(rawValue: errSecSMIMEKeyUsageNotCritical) XCTAssertEqual(status, .smimeKeyUsageNotCritical) XCTAssertEqual(status?.description, "The key usage extension is not marked as critical.") } do { let status = Status(rawValue: errSecSMIMENoEmailAddress) XCTAssertEqual(status, .smimeNoEmailAddress) XCTAssertEqual(status?.description, "No email address was found in the certificate.") } do { let status = Status(rawValue: errSecSMIMESubjAltNameNotCritical) XCTAssertEqual(status, .smimeSubjAltNameNotCritical) XCTAssertEqual(status?.description, "The subject alternative name extension is not marked as critical.") } do { let status = Status(rawValue: errSecSSLBadExtendedKeyUsage) XCTAssertEqual(status, .sslBadExtendedKeyUsage) XCTAssertEqual(status?.description, "The appropriate extended key usage for SSL was not found.") } do { let status = Status(rawValue: errSecOCSPBadResponse) XCTAssertEqual(status, .ocspBadResponse) XCTAssertEqual(status?.description, "The OCSP response was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPBadRequest) XCTAssertEqual(status, .ocspBadRequest) XCTAssertEqual(status?.description, "The OCSP request was incorrect or could not be parsed.") } do { let status = Status(rawValue: errSecOCSPUnavailable) XCTAssertEqual(status, .ocspUnavailable) XCTAssertEqual(status?.description, "OCSP service is unavailable.") } do { let status = Status(rawValue: errSecOCSPStatusUnrecognized) XCTAssertEqual(status, .ocspStatusUnrecognized) XCTAssertEqual(status?.description, "The OCSP server did not recognize this certificate.") } do { let status = Status(rawValue: errSecEndOfData) XCTAssertEqual(status, .endOfData) XCTAssertEqual(status?.description, "An end-of-data was detected.") } do { let status = Status(rawValue: errSecIncompleteCertRevocationCheck) XCTAssertEqual(status, .incompleteCertRevocationCheck) XCTAssertEqual(status?.description, "An incomplete certificate revocation check occurred.") } do { let status = Status(rawValue: errSecNetworkFailure) XCTAssertEqual(status, .networkFailure) XCTAssertEqual(status?.description, "A network failure occurred.") } do { let status = Status(rawValue: errSecOCSPNotTrustedToAnchor) XCTAssertEqual(status, .ocspNotTrustedToAnchor) XCTAssertEqual(status?.description, "The OCSP response was not trusted to a root or anchor certificate.") } do { let status = Status(rawValue: errSecRecordModified) XCTAssertEqual(status, .recordModified) XCTAssertEqual(status?.description, "The record was modified.") } do { let status = Status(rawValue: errSecOCSPSignatureError) XCTAssertEqual(status, .ocspSignatureError) XCTAssertEqual(status?.description, "The OCSP response had an invalid signature.") } do { let status = Status(rawValue: errSecOCSPNoSigner) XCTAssertEqual(status, .ocspNoSigner) XCTAssertEqual(status?.description, "The OCSP response had no signer.") } do { let status = Status(rawValue: errSecOCSPResponderMalformedReq) XCTAssertEqual(status, .ocspResponderMalformedReq) XCTAssertEqual(status?.description, "The OCSP responder was given a malformed request.") } do { let status = Status(rawValue: errSecOCSPResponderInternalError) XCTAssertEqual(status, .ocspResponderInternalError) XCTAssertEqual(status?.description, "The OCSP responder encountered an internal error.") } do { let status = Status(rawValue: errSecOCSPResponderTryLater) XCTAssertEqual(status, .ocspResponderTryLater) XCTAssertEqual(status?.description, "The OCSP responder is busy, try again later.") } do { let status = Status(rawValue: errSecOCSPResponderSignatureRequired) XCTAssertEqual(status, .ocspResponderSignatureRequired) XCTAssertEqual(status?.description, "The OCSP responder requires a signature.") } do { let status = Status(rawValue: errSecOCSPResponderUnauthorized) XCTAssertEqual(status, .ocspResponderUnauthorized) XCTAssertEqual(status?.description, "The OCSP responder rejected this request as unauthorized.") } do { let status = Status(rawValue: errSecOCSPResponseNonceMismatch) XCTAssertEqual(status, .ocspResponseNonceMismatch) XCTAssertEqual(status?.description, "The OCSP response nonce did not match the request.") } do { let status = Status(rawValue: errSecCodeSigningBadCertChainLength) XCTAssertEqual(status, .codeSigningBadCertChainLength) XCTAssertEqual(status?.description, "Code signing encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecCodeSigningNoBasicConstraints) XCTAssertEqual(status, .codeSigningNoBasicConstraints) XCTAssertEqual(status?.description, "Code signing found no basic constraints.") } do { let status = Status(rawValue: errSecCodeSigningBadPathLengthConstraint) XCTAssertEqual(status, .codeSigningBadPathLengthConstraint) XCTAssertEqual(status?.description, "Code signing encountered an incorrect path length constraint.") } do { let status = Status(rawValue: errSecCodeSigningNoExtendedKeyUsage) XCTAssertEqual(status, .codeSigningNoExtendedKeyUsage) XCTAssertEqual(status?.description, "Code signing found no extended key usage.") } do { let status = Status(rawValue: errSecCodeSigningDevelopment) XCTAssertEqual(status, .codeSigningDevelopment) XCTAssertEqual(status?.description, "Code signing indicated use of a development-only certificate.") } do { let status = Status(rawValue: errSecResourceSignBadCertChainLength) XCTAssertEqual(status, .resourceSignBadCertChainLength) XCTAssertEqual(status?.description, "Resource signing has encountered an incorrect certificate chain length.") } do { let status = Status(rawValue: errSecResourceSignBadExtKeyUsage) XCTAssertEqual(status, .resourceSignBadExtKeyUsage) XCTAssertEqual(status?.description, "Resource signing has encountered an error in the extended key usage.") } do { let status = Status(rawValue: errSecTrustSettingDeny) XCTAssertEqual(status, .trustSettingDeny) XCTAssertEqual(status?.description, "The trust setting for this policy was set to Deny.") } do { let status = Status(rawValue: errSecInvalidSubjectName) XCTAssertEqual(status, .invalidSubjectName) XCTAssertEqual(status?.description, "An invalid certificate subject name was encountered.") } do { let status = Status(rawValue: errSecUnknownQualifiedCertStatement) XCTAssertEqual(status, .unknownQualifiedCertStatement) XCTAssertEqual(status?.description, "An unknown qualified certificate statement was encountered.") } do { let status = Status(rawValue: errSecMobileMeRequestQueued) XCTAssertEqual(status, .mobileMeRequestQueued) XCTAssertEqual(status?.description, "The MobileMe request will be sent during the next connection.") } do { let status = Status(rawValue: errSecMobileMeRequestRedirected) XCTAssertEqual(status, .mobileMeRequestRedirected) XCTAssertEqual(status?.description, "The MobileMe request was redirected.") } do { let status = Status(rawValue: errSecMobileMeServerError) XCTAssertEqual(status, .mobileMeServerError) XCTAssertEqual(status?.description, "A MobileMe server error occurred.") } do { let status = Status(rawValue: errSecMobileMeServerNotAvailable) XCTAssertEqual(status, .mobileMeServerNotAvailable) XCTAssertEqual(status?.description, "The MobileMe server is not available.") } do { let status = Status(rawValue: errSecMobileMeServerAlreadyExists) XCTAssertEqual(status, .mobileMeServerAlreadyExists) XCTAssertEqual(status?.description, "The MobileMe server reported that the item already exists.") } do { let status = Status(rawValue: errSecMobileMeServerServiceErr) XCTAssertEqual(status, .mobileMeServerServiceErr) XCTAssertEqual(status?.description, "A MobileMe service error has occurred.") } do { let status = Status(rawValue: errSecMobileMeRequestAlreadyPending) XCTAssertEqual(status, .mobileMeRequestAlreadyPending) XCTAssertEqual(status?.description, "A MobileMe request is already pending.") } do { let status = Status(rawValue: errSecMobileMeNoRequestPending) XCTAssertEqual(status, .mobileMeNoRequestPending) XCTAssertEqual(status?.description, "MobileMe has no request pending.") } do { let status = Status(rawValue: errSecMobileMeCSRVerifyFailure) XCTAssertEqual(status, .mobileMeCSRVerifyFailure) XCTAssertEqual(status?.description, "A MobileMe CSR verification failure has occurred.") } do { let status = Status(rawValue: errSecMobileMeFailedConsistencyCheck) XCTAssertEqual(status, .mobileMeFailedConsistencyCheck) XCTAssertEqual(status?.description, "MobileMe has found a failed consistency check.") } do { let status = Status(rawValue: errSecNotInitialized) XCTAssertEqual(status, .notInitialized) XCTAssertEqual(status?.description, "A function was called without initializing CSSM.") } do { let status = Status(rawValue: errSecInvalidHandleUsage) XCTAssertEqual(status, .invalidHandleUsage) XCTAssertEqual(status?.description, "The CSSM handle does not match with the service type.") } do { let status = Status(rawValue: errSecPVCReferentNotFound) XCTAssertEqual(status, .pvcReferentNotFound) XCTAssertEqual(status?.description, "A reference to the calling module was not found in the list of authorized callers.") } do { let status = Status(rawValue: errSecFunctionIntegrityFail) XCTAssertEqual(status, .functionIntegrityFail) XCTAssertEqual(status?.description, "A function address was not within the verified module.") } do { let status = Status(rawValue: errSecInternalError) XCTAssertEqual(status, .internalError) XCTAssertEqual(status?.description, "An internal error has occurred.") } do { let status = Status(rawValue: errSecMemoryError) XCTAssertEqual(status, .memoryError) XCTAssertEqual(status?.description, "A memory error has occurred.") } do { let status = Status(rawValue: errSecInvalidData) XCTAssertEqual(status, .invalidData) XCTAssertEqual(status?.description, "Invalid data was encountered.") } do { let status = Status(rawValue: errSecMDSError) XCTAssertEqual(status, .mdsError) XCTAssertEqual(status?.description, "A Module Directory Service error has occurred.") } do { let status = Status(rawValue: errSecInvalidPointer) XCTAssertEqual(status, .invalidPointer) XCTAssertEqual(status?.description, "An invalid pointer was encountered.") } do { let status = Status(rawValue: errSecSelfCheckFailed) XCTAssertEqual(status, .selfCheckFailed) XCTAssertEqual(status?.description, "Self-check has failed.") } do { let status = Status(rawValue: errSecFunctionFailed) XCTAssertEqual(status, .functionFailed) XCTAssertEqual(status?.description, "A function has failed.") } do { let status = Status(rawValue: errSecModuleManifestVerifyFailed) XCTAssertEqual(status, .moduleManifestVerifyFailed) XCTAssertEqual(status?.description, "A module manifest verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidGUID) XCTAssertEqual(status, .invalidGUID) XCTAssertEqual(status?.description, "An invalid GUID was encountered.") } do { let status = Status(rawValue: errSecInvalidHandle) XCTAssertEqual(status, .invalidHandle) XCTAssertEqual(status?.description, "An invalid handle was encountered.") } do { let status = Status(rawValue: errSecInvalidDBList) XCTAssertEqual(status, .invalidDBList) XCTAssertEqual(status?.description, "An invalid DB list was encountered.") } do { let status = Status(rawValue: errSecInvalidPassthroughID) XCTAssertEqual(status, .invalidPassthroughID) XCTAssertEqual(status?.description, "An invalid passthrough ID was encountered.") } do { let status = Status(rawValue: errSecInvalidNetworkAddress) XCTAssertEqual(status, .invalidNetworkAddress) XCTAssertEqual(status?.description, "An invalid network address was encountered.") } do { let status = Status(rawValue: errSecCRLAlreadySigned) XCTAssertEqual(status, .crlAlreadySigned) XCTAssertEqual(status?.description, "The certificate revocation list is already signed.") } do { let status = Status(rawValue: errSecInvalidNumberOfFields) XCTAssertEqual(status, .invalidNumberOfFields) XCTAssertEqual(status?.description, "An invalid number of fields were encountered.") } do { let status = Status(rawValue: errSecVerificationFailure) XCTAssertEqual(status, .verificationFailure) XCTAssertEqual(status?.description, "A verification failure occurred.") } do { let status = Status(rawValue: errSecUnknownTag) XCTAssertEqual(status, .unknownTag) XCTAssertEqual(status?.description, "An unknown tag was encountered.") } do { let status = Status(rawValue: errSecInvalidSignature) XCTAssertEqual(status, .invalidSignature) XCTAssertEqual(status?.description, "An invalid signature was encountered.") } do { let status = Status(rawValue: errSecInvalidName) XCTAssertEqual(status, .invalidName) XCTAssertEqual(status?.description, "An invalid name was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateRef) XCTAssertEqual(status, .invalidCertificateRef) XCTAssertEqual(status?.description, "An invalid certificate reference was encountered.") } do { let status = Status(rawValue: errSecInvalidCertificateGroup) XCTAssertEqual(status, .invalidCertificateGroup) XCTAssertEqual(status?.description, "An invalid certificate group was encountered.") } do { let status = Status(rawValue: errSecTagNotFound) XCTAssertEqual(status, .tagNotFound) XCTAssertEqual(status?.description, "The specified tag was not found.") } do { let status = Status(rawValue: errSecInvalidQuery) XCTAssertEqual(status, .invalidQuery) XCTAssertEqual(status?.description, "The specified query was not valid.") } do { let status = Status(rawValue: errSecInvalidValue) XCTAssertEqual(status, .invalidValue) XCTAssertEqual(status?.description, "An invalid value was detected.") } do { let status = Status(rawValue: errSecCallbackFailed) XCTAssertEqual(status, .callbackFailed) XCTAssertEqual(status?.description, "A callback has failed.") } do { let status = Status(rawValue: errSecACLDeleteFailed) XCTAssertEqual(status, .aclDeleteFailed) XCTAssertEqual(status?.description, "An ACL delete operation has failed.") } do { let status = Status(rawValue: errSecACLReplaceFailed) XCTAssertEqual(status, .aclReplaceFailed) XCTAssertEqual(status?.description, "An ACL replace operation has failed.") } do { let status = Status(rawValue: errSecACLAddFailed) XCTAssertEqual(status, .aclAddFailed) XCTAssertEqual(status?.description, "An ACL add operation has failed.") } do { let status = Status(rawValue: errSecACLChangeFailed) XCTAssertEqual(status, .aclChangeFailed) XCTAssertEqual(status?.description, "An ACL change operation has failed.") } do { let status = Status(rawValue: errSecInvalidAccessCredentials) XCTAssertEqual(status, .invalidAccessCredentials) XCTAssertEqual(status?.description, "Invalid access credentials were encountered.") } do { let status = Status(rawValue: errSecInvalidRecord) XCTAssertEqual(status, .invalidRecord) XCTAssertEqual(status?.description, "An invalid record was encountered.") } do { let status = Status(rawValue: errSecInvalidACL) XCTAssertEqual(status, .invalidACL) XCTAssertEqual(status?.description, "An invalid ACL was encountered.") } do { let status = Status(rawValue: errSecInvalidSampleValue) XCTAssertEqual(status, .invalidSampleValue) XCTAssertEqual(status?.description, "An invalid sample value was encountered.") } do { let status = Status(rawValue: errSecIncompatibleVersion) XCTAssertEqual(status, .incompatibleVersion) XCTAssertEqual(status?.description, "An incompatible version was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotGranted) XCTAssertEqual(status, .privilegeNotGranted) XCTAssertEqual(status?.description, "The privilege was not granted.") } do { let status = Status(rawValue: errSecInvalidScope) XCTAssertEqual(status, .invalidScope) XCTAssertEqual(status?.description, "An invalid scope was encountered.") } do { let status = Status(rawValue: errSecPVCAlreadyConfigured) XCTAssertEqual(status, .pvcAlreadyConfigured) XCTAssertEqual(status?.description, "The PVC is already configured.") } do { let status = Status(rawValue: errSecInvalidPVC) XCTAssertEqual(status, .invalidPVC) XCTAssertEqual(status?.description, "An invalid PVC was encountered.") } do { let status = Status(rawValue: errSecEMMLoadFailed) XCTAssertEqual(status, .emmLoadFailed) XCTAssertEqual(status?.description, "The EMM load has failed.") } do { let status = Status(rawValue: errSecEMMUnloadFailed) XCTAssertEqual(status, .emmUnloadFailed) XCTAssertEqual(status?.description, "The EMM unload has failed.") } do { let status = Status(rawValue: errSecAddinLoadFailed) XCTAssertEqual(status, .addinLoadFailed) XCTAssertEqual(status?.description, "The add-in load operation has failed.") } do { let status = Status(rawValue: errSecInvalidKeyRef) XCTAssertEqual(status, .invalidKeyRef) XCTAssertEqual(status?.description, "An invalid key was encountered.") } do { let status = Status(rawValue: errSecInvalidKeyHierarchy) XCTAssertEqual(status, .invalidKeyHierarchy) XCTAssertEqual(status?.description, "An invalid key hierarchy was encountered.") } do { let status = Status(rawValue: errSecAddinUnloadFailed) XCTAssertEqual(status, .addinUnloadFailed) XCTAssertEqual(status?.description, "The add-in unload operation has failed.") } do { let status = Status(rawValue: errSecLibraryReferenceNotFound) XCTAssertEqual(status, .libraryReferenceNotFound) XCTAssertEqual(status?.description, "A library reference was not found.") } do { let status = Status(rawValue: errSecInvalidAddinFunctionTable) XCTAssertEqual(status, .invalidAddinFunctionTable) XCTAssertEqual(status?.description, "An invalid add-in function table was encountered.") } do { let status = Status(rawValue: errSecInvalidServiceMask) XCTAssertEqual(status, .invalidServiceMask) XCTAssertEqual(status?.description, "An invalid service mask was encountered.") } do { let status = Status(rawValue: errSecModuleNotLoaded) XCTAssertEqual(status, .moduleNotLoaded) XCTAssertEqual(status?.description, "A module was not loaded.") } do { let status = Status(rawValue: errSecInvalidSubServiceID) XCTAssertEqual(status, .invalidSubServiceID) XCTAssertEqual(status?.description, "An invalid subservice ID was encountered.") } do { let status = Status(rawValue: errSecAttributeNotInContext) XCTAssertEqual(status, .attributeNotInContext) XCTAssertEqual(status?.description, "An attribute was not in the context.") } do { let status = Status(rawValue: errSecModuleManagerInitializeFailed) XCTAssertEqual(status, .moduleManagerInitializeFailed) XCTAssertEqual(status?.description, "A module failed to initialize.") } do { let status = Status(rawValue: errSecModuleManagerNotFound) XCTAssertEqual(status, .moduleManagerNotFound) XCTAssertEqual(status?.description, "A module was not found.") } do { let status = Status(rawValue: errSecEventNotificationCallbackNotFound) XCTAssertEqual(status, .eventNotificationCallbackNotFound) XCTAssertEqual(status?.description, "An event notification callback was not found.") } do { let status = Status(rawValue: errSecInputLengthError) XCTAssertEqual(status, .inputLengthError) XCTAssertEqual(status?.description, "An input length error was encountered.") } do { let status = Status(rawValue: errSecOutputLengthError) XCTAssertEqual(status, .outputLengthError) XCTAssertEqual(status?.description, "An output length error was encountered.") } do { let status = Status(rawValue: errSecPrivilegeNotSupported) XCTAssertEqual(status, .privilegeNotSupported) XCTAssertEqual(status?.description, "The privilege is not supported.") } do { let status = Status(rawValue: errSecDeviceError) XCTAssertEqual(status, .deviceError) XCTAssertEqual(status?.description, "A device error was encountered.") } do { let status = Status(rawValue: errSecAttachHandleBusy) XCTAssertEqual(status, .attachHandleBusy) XCTAssertEqual(status?.description, "The CSP handle was busy.") } do { let status = Status(rawValue: errSecNotLoggedIn) XCTAssertEqual(status, .notLoggedIn) XCTAssertEqual(status?.description, "You are not logged in.") } do { let status = Status(rawValue: errSecAlgorithmMismatch) XCTAssertEqual(status, .algorithmMismatch) XCTAssertEqual(status?.description, "An algorithm mismatch was encountered.") } do { let status = Status(rawValue: errSecKeyUsageIncorrect) XCTAssertEqual(status, .keyUsageIncorrect) XCTAssertEqual(status?.description, "The key usage is incorrect.") } do { let status = Status(rawValue: errSecKeyBlobTypeIncorrect) XCTAssertEqual(status, .keyBlobTypeIncorrect) XCTAssertEqual(status?.description, "The key blob type is incorrect.") } do { let status = Status(rawValue: errSecKeyHeaderInconsistent) XCTAssertEqual(status, .keyHeaderInconsistent) XCTAssertEqual(status?.description, "The key header is inconsistent.") } do { let status = Status(rawValue: errSecUnsupportedKeyFormat) XCTAssertEqual(status, .unsupportedKeyFormat) XCTAssertEqual(status?.description, "The key header format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedKeySize) XCTAssertEqual(status, .unsupportedKeySize) XCTAssertEqual(status?.description, "The key size is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyUsageMask) XCTAssertEqual(status, .invalidKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyUsageMask) XCTAssertEqual(status, .unsupportedKeyUsageMask) XCTAssertEqual(status?.description, "The key usage mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyAttributeMask) XCTAssertEqual(status, .invalidKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyAttributeMask) XCTAssertEqual(status, .unsupportedKeyAttributeMask) XCTAssertEqual(status?.description, "The key attribute mask is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyLabel) XCTAssertEqual(status, .invalidKeyLabel) XCTAssertEqual(status?.description, "The key label is not valid.") } do { let status = Status(rawValue: errSecUnsupportedKeyLabel) XCTAssertEqual(status, .unsupportedKeyLabel) XCTAssertEqual(status?.description, "The key label is not supported.") } do { let status = Status(rawValue: errSecInvalidKeyFormat) XCTAssertEqual(status, .invalidKeyFormat) XCTAssertEqual(status?.description, "The key format is not valid.") } do { let status = Status(rawValue: errSecUnsupportedVectorOfBuffers) XCTAssertEqual(status, .unsupportedVectorOfBuffers) XCTAssertEqual(status?.description, "The vector of buffers is not supported.") } do { let status = Status(rawValue: errSecInvalidInputVector) XCTAssertEqual(status, .invalidInputVector) XCTAssertEqual(status?.description, "The input vector is not valid.") } do { let status = Status(rawValue: errSecInvalidOutputVector) XCTAssertEqual(status, .invalidOutputVector) XCTAssertEqual(status?.description, "The output vector is not valid.") } do { let status = Status(rawValue: errSecInvalidContext) XCTAssertEqual(status, .invalidContext) XCTAssertEqual(status?.description, "An invalid context was encountered.") } do { let status = Status(rawValue: errSecInvalidAlgorithm) XCTAssertEqual(status, .invalidAlgorithm) XCTAssertEqual(status?.description, "An invalid algorithm was encountered.") } do { let status = Status(rawValue: errSecInvalidAttributeKey) XCTAssertEqual(status, .invalidAttributeKey) XCTAssertEqual(status?.description, "A key attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKey) XCTAssertEqual(status, .missingAttributeKey) XCTAssertEqual(status?.description, "A key attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeInitVector) XCTAssertEqual(status, .invalidAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeInitVector) XCTAssertEqual(status, .missingAttributeInitVector) XCTAssertEqual(status?.description, "An init vector attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSalt) XCTAssertEqual(status, .invalidAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSalt) XCTAssertEqual(status, .missingAttributeSalt) XCTAssertEqual(status?.description, "A salt attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePadding) XCTAssertEqual(status, .invalidAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePadding) XCTAssertEqual(status, .missingAttributePadding) XCTAssertEqual(status?.description, "A padding attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRandom) XCTAssertEqual(status, .invalidAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRandom) XCTAssertEqual(status, .missingAttributeRandom) XCTAssertEqual(status?.description, "A random number attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSeed) XCTAssertEqual(status, .invalidAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSeed) XCTAssertEqual(status, .missingAttributeSeed) XCTAssertEqual(status?.description, "A seed attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePassphrase) XCTAssertEqual(status, .invalidAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePassphrase) XCTAssertEqual(status, .missingAttributePassphrase) XCTAssertEqual(status?.description, "A passphrase attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyLength) XCTAssertEqual(status, .invalidAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyLength) XCTAssertEqual(status, .missingAttributeKeyLength) XCTAssertEqual(status?.description, "A key length attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBlockSize) XCTAssertEqual(status, .invalidAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBlockSize) XCTAssertEqual(status, .missingAttributeBlockSize) XCTAssertEqual(status?.description, "A block size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeOutputSize) XCTAssertEqual(status, .invalidAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeOutputSize) XCTAssertEqual(status, .missingAttributeOutputSize) XCTAssertEqual(status?.description, "An output size attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeRounds) XCTAssertEqual(status, .invalidAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeRounds) XCTAssertEqual(status, .missingAttributeRounds) XCTAssertEqual(status?.description, "The number of rounds attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAlgorithmParms) XCTAssertEqual(status, .invalidAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAlgorithmParms) XCTAssertEqual(status, .missingAlgorithmParms) XCTAssertEqual(status?.description, "An algorithm parameters attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeLabel) XCTAssertEqual(status, .invalidAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeLabel) XCTAssertEqual(status, .missingAttributeLabel) XCTAssertEqual(status?.description, "A label attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeKeyType) XCTAssertEqual(status, .invalidAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeKeyType) XCTAssertEqual(status, .missingAttributeKeyType) XCTAssertEqual(status?.description, "A key type attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeMode) XCTAssertEqual(status, .invalidAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeMode) XCTAssertEqual(status, .missingAttributeMode) XCTAssertEqual(status?.description, "A mode attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEffectiveBits) XCTAssertEqual(status, .invalidAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEffectiveBits) XCTAssertEqual(status, .missingAttributeEffectiveBits) XCTAssertEqual(status?.description, "An effective bits attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeStartDate) XCTAssertEqual(status, .invalidAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeStartDate) XCTAssertEqual(status, .missingAttributeStartDate) XCTAssertEqual(status?.description, "A start date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeEndDate) XCTAssertEqual(status, .invalidAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeEndDate) XCTAssertEqual(status, .missingAttributeEndDate) XCTAssertEqual(status?.description, "An end date attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeVersion) XCTAssertEqual(status, .invalidAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeVersion) XCTAssertEqual(status, .missingAttributeVersion) XCTAssertEqual(status?.description, "A version attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrime) XCTAssertEqual(status, .invalidAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrime) XCTAssertEqual(status, .missingAttributePrime) XCTAssertEqual(status?.description, "A prime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeBase) XCTAssertEqual(status, .invalidAttributeBase) XCTAssertEqual(status?.description, "A base attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeBase) XCTAssertEqual(status, .missingAttributeBase) XCTAssertEqual(status?.description, "A base attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSubprime) XCTAssertEqual(status, .invalidAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSubprime) XCTAssertEqual(status, .missingAttributeSubprime) XCTAssertEqual(status?.description, "A subprime attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeIterationCount) XCTAssertEqual(status, .invalidAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeIterationCount) XCTAssertEqual(status, .missingAttributeIterationCount) XCTAssertEqual(status?.description, "An iteration count attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeDLDBHandle) XCTAssertEqual(status, .invalidAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeDLDBHandle) XCTAssertEqual(status, .missingAttributeDLDBHandle) XCTAssertEqual(status?.description, "A database handle attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeAccessCredentials) XCTAssertEqual(status, .invalidAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeAccessCredentials) XCTAssertEqual(status, .missingAttributeAccessCredentials) XCTAssertEqual(status?.description, "An access credentials attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePublicKeyFormat) XCTAssertEqual(status, .invalidAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePublicKeyFormat) XCTAssertEqual(status, .missingAttributePublicKeyFormat) XCTAssertEqual(status?.description, "A public key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributePrivateKeyFormat) XCTAssertEqual(status, .invalidAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributePrivateKeyFormat) XCTAssertEqual(status, .missingAttributePrivateKeyFormat) XCTAssertEqual(status?.description, "A private key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeSymmetricKeyFormat) XCTAssertEqual(status, .invalidAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeSymmetricKeyFormat) XCTAssertEqual(status, .missingAttributeSymmetricKeyFormat) XCTAssertEqual(status?.description, "A symmetric key format attribute was missing.") } do { let status = Status(rawValue: errSecInvalidAttributeWrappedKeyFormat) XCTAssertEqual(status, .invalidAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was not valid.") } do { let status = Status(rawValue: errSecMissingAttributeWrappedKeyFormat) XCTAssertEqual(status, .missingAttributeWrappedKeyFormat) XCTAssertEqual(status?.description, "A wrapped key format attribute was missing.") } do { let status = Status(rawValue: errSecStagedOperationInProgress) XCTAssertEqual(status, .stagedOperationInProgress) XCTAssertEqual(status?.description, "A staged operation is in progress.") } do { let status = Status(rawValue: errSecStagedOperationNotStarted) XCTAssertEqual(status, .stagedOperationNotStarted) XCTAssertEqual(status?.description, "A staged operation was not started.") } do { let status = Status(rawValue: errSecVerifyFailed) XCTAssertEqual(status, .verifyFailed) XCTAssertEqual(status?.description, "A cryptographic verification failure has occurred.") } do { let status = Status(rawValue: errSecQuerySizeUnknown) XCTAssertEqual(status, .querySizeUnknown) XCTAssertEqual(status?.description, "The query size is unknown.") } do { let status = Status(rawValue: errSecBlockSizeMismatch) XCTAssertEqual(status, .blockSizeMismatch) XCTAssertEqual(status?.description, "A block size mismatch occurred.") } do { let status = Status(rawValue: errSecPublicKeyInconsistent) XCTAssertEqual(status, .publicKeyInconsistent) XCTAssertEqual(status?.description, "The public key was inconsistent.") } do { let status = Status(rawValue: errSecDeviceVerifyFailed) XCTAssertEqual(status, .deviceVerifyFailed) XCTAssertEqual(status?.description, "A device verification failure has occurred.") } do { let status = Status(rawValue: errSecInvalidLoginName) XCTAssertEqual(status, .invalidLoginName) XCTAssertEqual(status?.description, "An invalid login name was detected.") } do { let status = Status(rawValue: errSecAlreadyLoggedIn) XCTAssertEqual(status, .alreadyLoggedIn) XCTAssertEqual(status?.description, "The user is already logged in.") } do { let status = Status(rawValue: errSecInvalidDigestAlgorithm) XCTAssertEqual(status, .invalidDigestAlgorithm) XCTAssertEqual(status?.description, "An invalid digest algorithm was detected.") } do { let status = Status(rawValue: errSecInvalidCRLGroup) XCTAssertEqual(status, .invalidCRLGroup) XCTAssertEqual(status?.description, "An invalid CRL group was detected.") } do { let status = Status(rawValue: errSecCertificateCannotOperate) XCTAssertEqual(status, .certificateCannotOperate) XCTAssertEqual(status?.description, "The certificate cannot operate.") } do { let status = Status(rawValue: errSecCertificateExpired) XCTAssertEqual(status, .certificateExpired) XCTAssertEqual(status?.description, "An expired certificate was detected.") } do { let status = Status(rawValue: errSecCertificateNotValidYet) XCTAssertEqual(status, .certificateNotValidYet) XCTAssertEqual(status?.description, "The certificate is not yet valid.") } do { let status = Status(rawValue: errSecCertificateRevoked) XCTAssertEqual(status, .certificateRevoked) XCTAssertEqual(status?.description, "The certificate was revoked.") } do { let status = Status(rawValue: errSecCertificateSuspended) XCTAssertEqual(status, .certificateSuspended) XCTAssertEqual(status?.description, "The certificate was suspended.") } do { let status = Status(rawValue: errSecInsufficientCredentials) XCTAssertEqual(status, .insufficientCredentials) XCTAssertEqual(status?.description, "Insufficient credentials were detected.") } do { let status = Status(rawValue: errSecInvalidAction) XCTAssertEqual(status, .invalidAction) XCTAssertEqual(status?.description, "The action was not valid.") } do { let status = Status(rawValue: errSecInvalidAuthority) XCTAssertEqual(status, .invalidAuthority) XCTAssertEqual(status?.description, "The authority was not valid.") } do { let status = Status(rawValue: errSecVerifyActionFailed) XCTAssertEqual(status, .verifyActionFailed) XCTAssertEqual(status?.description, "A verify action has failed.") } do { let status = Status(rawValue: errSecInvalidCertAuthority) XCTAssertEqual(status, .invalidCertAuthority) XCTAssertEqual(status?.description, "The certificate authority was not valid.") } do { let status = Status(rawValue: errSecInvaldCRLAuthority) XCTAssertEqual(status, .invaldCRLAuthority) XCTAssertEqual(status?.description, "The CRL authority was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLEncoding) XCTAssertEqual(status, .invalidCRLEncoding) XCTAssertEqual(status?.description, "The CRL encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLType) XCTAssertEqual(status, .invalidCRLType) XCTAssertEqual(status?.description, "The CRL type was not valid.") } do { let status = Status(rawValue: errSecInvalidCRL) XCTAssertEqual(status, .invalidCRL) XCTAssertEqual(status?.description, "The CRL was not valid.") } do { let status = Status(rawValue: errSecInvalidFormType) XCTAssertEqual(status, .invalidFormType) XCTAssertEqual(status?.description, "The form type was not valid.") } do { let status = Status(rawValue: errSecInvalidID) XCTAssertEqual(status, .invalidID) XCTAssertEqual(status?.description, "The ID was not valid.") } do { let status = Status(rawValue: errSecInvalidIdentifier) XCTAssertEqual(status, .invalidIdentifier) XCTAssertEqual(status?.description, "The identifier was not valid.") } do { let status = Status(rawValue: errSecInvalidIndex) XCTAssertEqual(status, .invalidIndex) XCTAssertEqual(status?.description, "The index was not valid.") } do { let status = Status(rawValue: errSecInvalidPolicyIdentifiers) XCTAssertEqual(status, .invalidPolicyIdentifiers) XCTAssertEqual(status?.description, "The policy identifiers are not valid.") } do { let status = Status(rawValue: errSecInvalidTimeString) XCTAssertEqual(status, .invalidTimeString) XCTAssertEqual(status?.description, "The time specified was not valid.") } do { let status = Status(rawValue: errSecInvalidReason) XCTAssertEqual(status, .invalidReason) XCTAssertEqual(status?.description, "The trust policy reason was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestInputs) XCTAssertEqual(status, .invalidRequestInputs) XCTAssertEqual(status?.description, "The request inputs are not valid.") } do { let status = Status(rawValue: errSecInvalidResponseVector) XCTAssertEqual(status, .invalidResponseVector) XCTAssertEqual(status?.description, "The response vector was not valid.") } do { let status = Status(rawValue: errSecInvalidStopOnPolicy) XCTAssertEqual(status, .invalidStopOnPolicy) XCTAssertEqual(status?.description, "The stop-on policy was not valid.") } do { let status = Status(rawValue: errSecInvalidTuple) XCTAssertEqual(status, .invalidTuple) XCTAssertEqual(status?.description, "The tuple was not valid.") } do { let status = Status(rawValue: errSecMultipleValuesUnsupported) XCTAssertEqual(status, .multipleValuesUnsupported) XCTAssertEqual(status?.description, "Multiple values are not supported.") } do { let status = Status(rawValue: errSecNotTrusted) XCTAssertEqual(status, .notTrusted) XCTAssertEqual(status?.description, "The trust policy was not trusted.") } do { let status = Status(rawValue: errSecNoDefaultAuthority) XCTAssertEqual(status, .noDefaultAuthority) XCTAssertEqual(status?.description, "No default authority was detected.") } do { let status = Status(rawValue: errSecRejectedForm) XCTAssertEqual(status, .rejectedForm) XCTAssertEqual(status?.description, "The trust policy had a rejected form.") } do { let status = Status(rawValue: errSecRequestLost) XCTAssertEqual(status, .requestLost) XCTAssertEqual(status?.description, "The request was lost.") } do { let status = Status(rawValue: errSecRequestRejected) XCTAssertEqual(status, .requestRejected) XCTAssertEqual(status?.description, "The request was rejected.") } do { let status = Status(rawValue: errSecUnsupportedAddressType) XCTAssertEqual(status, .unsupportedAddressType) XCTAssertEqual(status?.description, "The address type is not supported.") } do { let status = Status(rawValue: errSecUnsupportedService) XCTAssertEqual(status, .unsupportedService) XCTAssertEqual(status?.description, "The service is not supported.") } do { let status = Status(rawValue: errSecInvalidTupleGroup) XCTAssertEqual(status, .invalidTupleGroup) XCTAssertEqual(status?.description, "The tuple group was not valid.") } do { let status = Status(rawValue: errSecInvalidBaseACLs) XCTAssertEqual(status, .invalidBaseACLs) XCTAssertEqual(status?.description, "The base ACLs are not valid.") } do { let status = Status(rawValue: errSecInvalidTupleCredendtials) XCTAssertEqual(status, .invalidTupleCredendtials) XCTAssertEqual(status?.description, "The tuple credentials are not valid.") } do { let status = Status(rawValue: errSecInvalidEncoding) XCTAssertEqual(status, .invalidEncoding) XCTAssertEqual(status?.description, "The encoding was not valid.") } do { let status = Status(rawValue: errSecInvalidValidityPeriod) XCTAssertEqual(status, .invalidValidityPeriod) XCTAssertEqual(status?.description, "The validity period was not valid.") } do { let status = Status(rawValue: errSecInvalidRequestor) XCTAssertEqual(status, .invalidRequestor) XCTAssertEqual(status?.description, "The requestor was not valid.") } do { let status = Status(rawValue: errSecRequestDescriptor) XCTAssertEqual(status, .requestDescriptor) XCTAssertEqual(status?.description, "The request descriptor was not valid.") } do { let status = Status(rawValue: errSecInvalidBundleInfo) XCTAssertEqual(status, .invalidBundleInfo) XCTAssertEqual(status?.description, "The bundle information was not valid.") } do { let status = Status(rawValue: errSecInvalidCRLIndex) XCTAssertEqual(status, .invalidCRLIndex) XCTAssertEqual(status?.description, "The CRL index was not valid.") } do { let status = Status(rawValue: errSecNoFieldValues) XCTAssertEqual(status, .noFieldValues) XCTAssertEqual(status?.description, "No field values were detected.") } do { let status = Status(rawValue: errSecUnsupportedFieldFormat) XCTAssertEqual(status, .unsupportedFieldFormat) XCTAssertEqual(status?.description, "The field format is not supported.") } do { let status = Status(rawValue: errSecUnsupportedIndexInfo) XCTAssertEqual(status, .unsupportedIndexInfo) XCTAssertEqual(status?.description, "The index information is not supported.") } do { let status = Status(rawValue: errSecUnsupportedLocality) XCTAssertEqual(status, .unsupportedLocality) XCTAssertEqual(status?.description, "The locality is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumAttributes) XCTAssertEqual(status, .unsupportedNumAttributes) XCTAssertEqual(status?.description, "The number of attributes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumIndexes) XCTAssertEqual(status, .unsupportedNumIndexes) XCTAssertEqual(status?.description, "The number of indexes is not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumRecordTypes) XCTAssertEqual(status, .unsupportedNumRecordTypes) XCTAssertEqual(status?.description, "The number of record types is not supported.") } do { let status = Status(rawValue: errSecFieldSpecifiedMultiple) XCTAssertEqual(status, .fieldSpecifiedMultiple) XCTAssertEqual(status?.description, "Too many fields were specified.") } do { let status = Status(rawValue: errSecIncompatibleFieldFormat) XCTAssertEqual(status, .incompatibleFieldFormat) XCTAssertEqual(status?.description, "The field format was incompatible.") } do { let status = Status(rawValue: errSecInvalidParsingModule) XCTAssertEqual(status, .invalidParsingModule) XCTAssertEqual(status?.description, "The parsing module was not valid.") } do { let status = Status(rawValue: errSecDatabaseLocked) XCTAssertEqual(status, .databaseLocked) XCTAssertEqual(status?.description, "The database is locked.") } do { let status = Status(rawValue: errSecDatastoreIsOpen) XCTAssertEqual(status, .datastoreIsOpen) XCTAssertEqual(status?.description, "The data store is open.") } do { let status = Status(rawValue: errSecMissingValue) XCTAssertEqual(status, .missingValue) XCTAssertEqual(status?.description, "A missing value was detected.") } do { let status = Status(rawValue: errSecUnsupportedQueryLimits) XCTAssertEqual(status, .unsupportedQueryLimits) XCTAssertEqual(status?.description, "The query limits are not supported.") } do { let status = Status(rawValue: errSecUnsupportedNumSelectionPreds) XCTAssertEqual(status, .unsupportedNumSelectionPreds) XCTAssertEqual(status?.description, "The number of selection predicates is not supported.") } do { let status = Status(rawValue: errSecUnsupportedOperator) XCTAssertEqual(status, .unsupportedOperator) XCTAssertEqual(status?.description, "The operator is not supported.") } do { let status = Status(rawValue: errSecInvalidDBLocation) XCTAssertEqual(status, .invalidDBLocation) XCTAssertEqual(status?.description, "The database location is not valid.") } do { let status = Status(rawValue: errSecInvalidAccessRequest) XCTAssertEqual(status, .invalidAccessRequest) XCTAssertEqual(status?.description, "The access request is not valid.") } do { let status = Status(rawValue: errSecInvalidIndexInfo) XCTAssertEqual(status, .invalidIndexInfo) XCTAssertEqual(status?.description, "The index information is not valid.") } do { let status = Status(rawValue: errSecInvalidNewOwner) XCTAssertEqual(status, .invalidNewOwner) XCTAssertEqual(status?.description, "The new owner is not valid.") } do { let status = Status(rawValue: errSecInvalidModifyMode) XCTAssertEqual(status, .invalidModifyMode) XCTAssertEqual(status?.description, "The modify mode is not valid.") } do { let status = Status(rawValue: errSecMissingRequiredExtension) XCTAssertEqual(status, .missingRequiredExtension) XCTAssertEqual(status?.description, "A required certificate extension is missing.") } do { let status = Status(rawValue: errSecExtendedKeyUsageNotCritical) XCTAssertEqual(status, .extendedKeyUsageNotCritical) XCTAssertEqual(status?.description, "The extended key usage extension was not marked critical.") } do { let status = Status(rawValue: errSecTimestampMissing) XCTAssertEqual(status, .timestampMissing) XCTAssertEqual(status?.description, "A timestamp was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampInvalid) XCTAssertEqual(status, .timestampInvalid) XCTAssertEqual(status?.description, "The timestamp was not valid.") } do { let status = Status(rawValue: errSecTimestampNotTrusted) XCTAssertEqual(status, .timestampNotTrusted) XCTAssertEqual(status?.description, "The timestamp was not trusted.") } do { let status = Status(rawValue: errSecTimestampServiceNotAvailable) XCTAssertEqual(status, .timestampServiceNotAvailable) XCTAssertEqual(status?.description, "The timestamp service is not available.") } do { let status = Status(rawValue: errSecTimestampBadAlg) XCTAssertEqual(status, .timestampBadAlg) XCTAssertEqual(status?.description, "An unrecognized or unsupported Algorithm Identifier in timestamp.") } do { let status = Status(rawValue: errSecTimestampBadRequest) XCTAssertEqual(status, .timestampBadRequest) XCTAssertEqual(status?.description, "The timestamp transaction is not permitted or supported.") } do { let status = Status(rawValue: errSecTimestampBadDataFormat) XCTAssertEqual(status, .timestampBadDataFormat) XCTAssertEqual(status?.description, "The timestamp data submitted has the wrong format.") } do { let status = Status(rawValue: errSecTimestampTimeNotAvailable) XCTAssertEqual(status, .timestampTimeNotAvailable) XCTAssertEqual(status?.description, "The time source for the Timestamp Authority is not available.") } do { let status = Status(rawValue: errSecTimestampUnacceptedPolicy) XCTAssertEqual(status, .timestampUnacceptedPolicy) XCTAssertEqual(status?.description, "The requested policy is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampUnacceptedExtension) XCTAssertEqual(status, .timestampUnacceptedExtension) XCTAssertEqual(status?.description, "The requested extension is not supported by the Timestamp Authority.") } do { let status = Status(rawValue: errSecTimestampAddInfoNotAvailable) XCTAssertEqual(status, .timestampAddInfoNotAvailable) XCTAssertEqual(status?.description, "The additional information requested is not available.") } do { let status = Status(rawValue: errSecTimestampSystemFailure) XCTAssertEqual(status, .timestampSystemFailure) XCTAssertEqual(status?.description, "The timestamp request cannot be handled due to system failure.") } do { let status = Status(rawValue: errSecSigningTimeMissing) XCTAssertEqual(status, .signingTimeMissing) XCTAssertEqual(status?.description, "A signing time was expected but was not found.") } do { let status = Status(rawValue: errSecTimestampRejection) XCTAssertEqual(status, .timestampRejection) XCTAssertEqual(status?.description, "A timestamp transaction was rejected.") } do { let status = Status(rawValue: errSecTimestampWaiting) XCTAssertEqual(status, .timestampWaiting) XCTAssertEqual(status?.description, "A timestamp transaction is waiting.") } do { let status = Status(rawValue: errSecTimestampRevocationWarning) XCTAssertEqual(status, .timestampRevocationWarning) XCTAssertEqual(status?.description, "A timestamp authority revocation warning was issued.") } do { let status = Status(rawValue: errSecTimestampRevocationNotification) XCTAssertEqual(status, .timestampRevocationNotification) XCTAssertEqual(status?.description, "A timestamp authority revocation notification was issued.") } #endif } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/KeychainAccessTests-MacCatalyst/KeychainAccessTests.swift ================================================ // // KeychainAccessTests.swift // KeychainAccessTests // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import XCTest import KeychainAccess class KeychainAccessTests: XCTestCase { override func setUp() { super.setUp() do { try Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").removeAll() } catch {} do { try Keychain(service: "Twitter").removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com")!, protocolType: .https).removeAll() } catch {} do { try Keychain(server: URL(string: "https://example.com:443")!, protocolType: .https).removeAll() } catch {} do { try Keychain().removeAll() } catch {} } override func tearDown() { super.tearDown() } // MARK: func testGenericPassword() { do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testGenericPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testGenericPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(service: "Twitter").synchronizable(true) let keychainWithAccessGroup = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testInternetPassword() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") let password = try! keychain.get("password") XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} let username = try! keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") let password = try! keychain.get("password") XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordSubscripting() { do { // Add Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "kishikawa_katsumi" keychain["password"] = "password_1234" let username = keychain["username"] XCTAssertEqual(username, "kishikawa_katsumi") let password = keychain["password"] XCTAssertEqual(password, "password_1234") } do { // Update Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = "katsumi_kishikawa" keychain["password"] = "1234_password" let username = keychain["username"] XCTAssertEqual(username, "katsumi_kishikawa") let password = keychain["password"] XCTAssertEqual(password, "1234_password") } do { // Remove Keychain items let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) keychain["username"] = nil keychain["password"] = nil XCTAssertNil(keychain["username"]) XCTAssertNil(keychain["password"]) } } func testInternetPasswordWithAccessGroup1() { do { // Add Keychain items // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. // https://developer.apple.com/documentation/security/ksecattraccessgroup let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychainWithAccessGroup.remove("username") } catch {} do { try keychainWithAccessGroup.remove("password") } catch {} XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) XCTAssertNotNil(try! keychain.get("username")) XCTAssertNotNil(try! keychain.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) } } func testInternetPasswordWithAccessGroup2() { do { // Add Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("kishikawa_katsumi", key: "username") } catch {} do { try keychain.set("password_1234", key: "password") } catch {} do { try keychainWithAccessGroup.set("kishikawa_katsumi_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("password_1234_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawa_katsumi") XCTAssertEqual(try! keychain.get("password"), "password_1234") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "kishikawa_katsumi_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "password_1234_access_group") } do { // Update Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) do { try keychain.set("katsumi_kishikawa", key: "username") } catch {} do { try keychain.set("1234_password", key: "password") } catch {} do { try keychainWithAccessGroup.set("katsumi_kishikawa_access_group", key: "username") } catch {} do { try keychainWithAccessGroup.set("1234_password_access_group", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "katsumi_kishikawa") XCTAssertEqual(try! keychain.get("password"), "1234_password") XCTAssertEqual(try! keychainWithAccessGroup.get("username"), "katsumi_kishikawa_access_group") XCTAssertEqual(try! keychainWithAccessGroup.get("password"), "1234_password_access_group") } do { // Remove Keychain items let keychain = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https).synchronizable(true) let keychainWithAccessGroup = Keychain(server: "https://kishikawakatsumi.com", protocolType: .https, accessGroup: "27AEDK3C9F.shared").synchronizable(true) XCTAssertNotNil(try! keychainWithAccessGroup.get("username")) XCTAssertNotNil(try! keychainWithAccessGroup.get("password")) do { try keychain.remove("username") } catch {} do { try keychain.remove("password") } catch {} // If the access group is empty, the query will match all access group. So delete all values in other access groups. XCTAssertNil(try! keychain.get("username")) XCTAssertNil(try! keychain.get("password")) XCTAssertNil(try! keychainWithAccessGroup.get("username")) XCTAssertNil(try! keychainWithAccessGroup.get("password")) } } // MARK: func testDefaultInitializer() { let keychain = Keychain() XCTAssertEqual(keychain.service, Bundle.main.bundleIdentifier) let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertNil(keychain.accessGroup) } func testInitializerWithService() { let keychain = Keychain(service: "com.example.github-token") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertNil(keychain.accessGroup) } func testInitializerWithAccessGroup() { let keychain = Keychain(accessGroup: "27AEDK3C9F.shared") let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(keychain.service, service) XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServiceAndAccessGroup() { let keychain = Keychain(service: "com.example.github-token", accessGroup: "27AEDK3C9F.shared") XCTAssertEqual(keychain.service, "com.example.github-token") XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared") } func testInitializerWithServer() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } do { let keychain = Keychain(server: url, protocolType: .https) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.default) } } func testInitializerWithServerAndAuthenticationType() { let server = "https://kishikawakatsumi.com" let url = URL(string: server)! do { let keychain = Keychain(server: server, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } do { let keychain = Keychain(server: url, protocolType: .https, authenticationType: .htmlForm) XCTAssertEqual(keychain.server, url) XCTAssertEqual(keychain.protocolType, ProtocolType.https) XCTAssertEqual(keychain.authenticationType, AuthenticationType.htmlForm) } } // MARK: func testContains() { let keychain = Keychain(service: "Twitter") XCTAssertFalse(try! keychain.contains("username"), "not stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertFalse(try! keychain.contains("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertTrue(try! keychain.contains("username"), "stored username") XCTAssertTrue(try! keychain.contains("password"), "stored password") } // MARK: func testSetString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") } func testSetStringWithLabel() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") } catch { XCTFail("error occurred") } } func testSetStringWithComment() { let keychain = Keychain(service: "Twitter") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetStringWithLabelAndComment() { let keychain = Keychain(service: "Twitter") .label("Twitter Account") .comment("Kishikawa Katsumi") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertNil(label) let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertNil(comment) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let label = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.label } XCTAssertEqual(label, "Twitter Account") let comment = try keychain.get("kishikawakatsumi") { (attributes) -> String? in return attributes?.comment } XCTAssertEqual(comment, "Kishikawa Katsumi") } catch { XCTFail("error occurred") } } func testSetData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") } func testStringConversionError() { let keychain = Keychain(service: "Twitter") let length = 256 let data = NSMutableData(length: length)! let bytes = data.mutableBytes.bindMemory(to: UInt8.self, capacity: length) _ = SecRandomCopyBytes(kSecRandomDefault, length, bytes) do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch let error as NSError { XCTAssertEqual(error.domain, KeychainAccessErrorDomain) XCTAssertEqual(error.code, Int(Status.conversionError.rawValue)) XCTAssertEqual(error.userInfo[NSLocalizedDescriptionKey] as! String, Status.conversionError.localizedDescription) } catch { XCTFail("unexpected error occurred") } do { try keychain.set(data as Data, key: "RandomData") let _ = try keychain.getString("RandomData") XCTFail("no error occurred") } catch Status.conversionError { XCTAssertTrue(true) } catch { XCTFail("unexpected error occurred") } } func testGetPersistentRef() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNil(persistentRef) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let persistentRef = try keychain.get("kishikawakatsumi") { $0?.persistentRef } XCTAssertNotNil(persistentRef) } catch { XCTFail("error occurred") } } #if os(iOS) || os(tvOS) func testSetAttributes() { do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true let keychain = Keychain(service: "Twitter") .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if targetEnvironment(macCatalyst) XCTAssertNotNil(attributes?.accessControl) #else if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 3, patchVersion: 0)) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertEqual(attributes?.service, "Twitter") XCTAssertNil(attributes?.generic) XCTAssertNil(attributes?.securityDomain) XCTAssertNil(attributes?.server) XCTAssertNil(attributes?.`protocol`) XCTAssertNil(attributes?.authenticationType) XCTAssertNil(attributes?.port) XCTAssertNil(attributes?.path) XCTAssertEqual(attributes?[String(kSecClass)] as? String, ItemClass.genericPassword.rawValue) XCTAssertEqual(attributes?[String(kSecValueData)] as? Data, "password1234".data(using: .utf8)) } catch { XCTFail("error occurred") } } do { var attributes = [String: Any]() attributes[String(kSecAttrDescription)] = "Description Test" attributes[String(kSecAttrComment)] = "Comment Test" attributes[String(kSecAttrCreator)] = "Creator Test" attributes[String(kSecAttrType)] = "Type Test" attributes[String(kSecAttrLabel)] = "Label Test" attributes[String(kSecAttrIsInvisible)] = true attributes[String(kSecAttrIsNegative)] = true attributes[String(kSecAttrSecurityDomain)] = "securitydomain" let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes(attributes) XCTAssertNil(keychain["kishikawakatsumi"], "not stored password") do { let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertNil(attributes) } catch { XCTFail("error occurred") } do { keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password") keychain["kishikawakatsumi"] = "1234password" XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "updated password") let attributes = try keychain.get("kishikawakatsumi") { $0 } XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "1234password".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Description Test") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://example.com:443/api/login/")!, protocolType: .https) .attributes([String(kSecAttrDescription): "Updated Description"]) XCTAssertEqual(keychain["kishikawakatsumi"], "1234password", "stored password") keychain["kishikawakatsumi"] = "password1234" XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "updated password") let attributes = keychain[attributes: "kishikawakatsumi"] XCTAssertEqual(attributes?.`class`, ItemClass.internetPassword.rawValue) XCTAssertEqual(attributes?.data, "password1234".data(using: .utf8)) XCTAssertNil(attributes?.ref) XCTAssertNotNil(attributes?.persistentRef) XCTAssertEqual(attributes?.accessible, Accessibility.afterFirstUnlock.rawValue) #if os(iOS) if #available(iOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else if #available(iOS 9.0, *) { XCTAssertNil(attributes?.accessControl) } else { XCTAssertNotNil(attributes?.accessControl) } #else if #available(tvOS 11.3, *) { XCTAssertNotNil(attributes?.accessControl) } else { XCTAssertNil(attributes?.accessControl) } #endif let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(attributes?.accessGroup, accessGroup) XCTAssertNotNil(attributes?.synchronizable) XCTAssertNotNil(attributes?.creationDate) XCTAssertNotNil(attributes?.modificationDate) XCTAssertEqual(attributes?.attributeDescription, "Updated Description") XCTAssertEqual(attributes?.comment, "Comment Test") XCTAssertEqual(attributes?.creator, "Creator Test") XCTAssertEqual(attributes?.type, "Type Test") XCTAssertEqual(attributes?.label, "Label Test") XCTAssertEqual(attributes?.isInvisible, true) XCTAssertEqual(attributes?.isNegative, true) XCTAssertEqual(attributes?.account, "kishikawakatsumi") XCTAssertNil(attributes?.service) XCTAssertNil(attributes?.generic) XCTAssertEqual(attributes?.securityDomain, "securitydomain") XCTAssertEqual(attributes?.server, "example.com") XCTAssertEqual(attributes?.`protocol`, ProtocolType.https.rawValue) XCTAssertEqual(attributes?.authenticationType, AuthenticationType.default.rawValue) XCTAssertEqual(attributes?.port, 443) XCTAssertEqual(attributes?.path, "") } } } #endif func testRemoveString() { let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertEqual(try! keychain.get("password"), "password1234", "left password") do { try keychain.remove("password") } catch {} XCTAssertNil(try! keychain.get("username"), "removed username") XCTAssertNil(try! keychain.get("password"), "removed password") } func testRemoveData() { let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) let keychain = Keychain(service: "Twitter") XCTAssertNil(try! keychain.getData("JSONData"), "not stored JSON data") do { try keychain.set(JSONData, key: "JSONData") } catch {} XCTAssertEqual(try! keychain.getData("JSONData"), JSONData, "stored JSON data") do { try keychain.remove("JSONData") } catch {} XCTAssertNil(try! keychain.getData("JSONData"), "removed JSON data") } // MARK: func testSubscripting() { let keychain = Keychain(service: "Twitter") XCTAssertNil(keychain["username"], "not stored username") XCTAssertNil(keychain["password"], "not stored password") XCTAssertNil(keychain[string: "username"], "not stored username") XCTAssertNil(keychain[string: "password"], "not stored password") keychain["username"] = "kishikawakatsumi" XCTAssertEqual(keychain["username"], "kishikawakatsumi", "stored username") XCTAssertEqual(keychain[string: "username"], "kishikawakatsumi", "stored username") keychain["password"] = "password1234" XCTAssertEqual(keychain["password"], "password1234", "stored password") XCTAssertEqual(keychain[string: "password"], "password1234", "stored password") keychain[string: "username"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertEqual(keychain["password"], "password1234", "left password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertEqual(keychain[string: "password"], "password1234", "left password") keychain[string: "password"] = nil XCTAssertNil(keychain["username"], "removed username") XCTAssertNil(keychain["password"], "removed password") XCTAssertNil(keychain[string: "username"], "removed username") XCTAssertNil(keychain[string: "password"], "removed password") let JSONObject = ["username": "kishikawakatsumi", "password": "password1234"] let JSONData = try! JSONSerialization.data(withJSONObject: JSONObject, options: []) XCTAssertNil(keychain[data:"JSONData"], "not stored JSON data") keychain[data: "JSONData"] = JSONData XCTAssertEqual(keychain[data: "JSONData"], JSONData, "stored JSON data") keychain[data: "JSONData"] = nil XCTAssertNil(keychain[data:"JSONData"], "removed JSON data") } // MARK: func testErrorHandling() { do { let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(service: "Twitter") try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain(server: URL(string: "https://kishikawakatsumi.com")!, protocolType: .https) try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let keychain = Keychain() try keychain.removeAll() XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { // Add Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("kishikawa_katsumi", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("password_1234", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "kishikawa_katsumi") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "password_1234") } catch { XCTFail("error occurred") } } do { // Update Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.set("katsumi_kishikawa", key: "username") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { try keychain.set("1234_password", key: "password") XCTAssertTrue(true, "no error occurred") } catch { XCTFail("error occurred") } do { let username = try keychain.get("username") XCTAssertEqual(username, "katsumi_kishikawa") } catch { XCTFail("error occurred") } do { let password = try keychain.get("password") XCTAssertEqual(password, "1234_password") } catch { XCTFail("error occurred") } } do { // Remove Keychain items let keychain = Keychain(service: "Twitter") do { try keychain.remove("username") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } do { try keychain.remove("password") XCTAssertNil(try! keychain.get("username")) } catch { XCTFail("error occurred") } } } // MARK: func testSetStringWithCustomService() { let username_1 = "kishikawakatsumi" let password_1 = "password1234" let username_2 = "kishikawa_katsumi" let password_2 = "password_1234" let username_3 = "k_katsumi" let password_3 = "12341234" let service_1 = "" let service_2 = "com.kishikawakatsumi.KeychainAccess" let service_3 = "example.com" do { try Keychain().removeAll() } catch {} do { try Keychain(service: service_1).removeAll() } catch {} do { try Keychain(service: service_2).removeAll() } catch {} do { try Keychain(service: service_3).removeAll() } catch {} XCTAssertNil(try! Keychain().get("username"), "not stored username") XCTAssertNil(try! Keychain().get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_1).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain().set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_1).set(username_1, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_2).set(username_2, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username") do { try Keychain(service: service_3).set(username_3, key: "username") } catch {} XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "stored username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "stored username") do { try Keychain().set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_1).set(password_1, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_2).set(password_2, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password") do { try Keychain(service: service_3).set(password_3, key: "password") } catch {} XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "stored password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "stored password") do { try Keychain().remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "left username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_1).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_2).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username") do { try Keychain(service: service_3).remove("username") } catch {} XCTAssertNil(try! Keychain().get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_2).get("username"), "removed username") XCTAssertNil(try! Keychain(service: service_3).get("username"), "removed username") do { try Keychain().remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "left password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_1).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_2).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password") do { try Keychain(service: service_3).remove("password") } catch {} XCTAssertNil(try! Keychain().get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") XCTAssertNil(try! Keychain(service: service_2).get("password"), "removed password") } // MARK: func testProperties() { guard #available(OSX 10.10, *) else { return } let keychain = Keychain() XCTAssertEqual(keychain.synchronizable, false) XCTAssertEqual(keychain.synchronizable(true).synchronizable, true) XCTAssertEqual(keychain.synchronizable(false).synchronizable, false) XCTAssertEqual(keychain.accessibility(.afterFirstUnlock).accessibility, Accessibility.afterFirstUnlock) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).accessibility, Accessibility.whenPasscodeSetThisDeviceOnly) XCTAssertEqual(keychain.accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: .userPresence).authenticationPolicy, AuthenticationPolicy.userPresence) XCTAssertNil(keychain.label) XCTAssertEqual(keychain.label("Label").label, "Label") XCTAssertNil(keychain.comment) XCTAssertEqual(keychain.comment("Comment").comment, "Comment") XCTAssertEqual(keychain.authenticationPrompt("Prompt").authenticationPrompt, "Prompt") } // MARK: func testAllKeys() { do { let keychain = Keychain() keychain["key1"] = "value1" keychain["key2"] = "value2" keychain["key3"] = "value3" let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 3) XCTAssertEqual(allKeys.sorted(), ["key1", "key2", "key3"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 3) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) let service: String let accessGroup: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["service"] as? String, service) XCTAssertEqual(sortedItems[0]["value"] as? String, "value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, service) XCTAssertEqual(sortedItems[1]["value"] as? String, "value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[2]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[2]["service"] as? String, service) XCTAssertEqual(sortedItems[2]["value"] as? String, "value3") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[0]["key"] as? String, "key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[1]["key"] as? String, "key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.KeychainAccess.TestHost") XCTAssertEqual(sortedItems[2]["key"] as? String, "key3") XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(service: "service1") try! keychain .synchronizable(true) .accessibility(.whenUnlockedThisDeviceOnly) .set("service1_value1", key: "service1_key1") try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("service1_value2", key: "service1_key2") let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["service1_key1", "service1_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) let accessGroup: String #if targetEnvironment(macCatalyst) accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else accessGroup = "27AEDK3C9F.com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["value"] as? String, "service1_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "WhenUnlockedThisDeviceOnly") XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, accessGroup) XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["value"] as? String, "service1_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["service"] as? String, "service1") XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1") XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword") XCTAssertEqual(sortedItems[1]["service"] as? String, "service1") XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2") XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword") #endif } do { let keychain = Keychain(server: "https://google.com", protocolType: .https) #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(false) .accessibility(.alwaysThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #else try! keychain .synchronizable(false) .accessibility(.afterFirstUnlockThisDeviceOnly) .set("google.com_value1", key: "google.com_key1") #endif #if !targetEnvironment(macCatalyst) try! keychain .synchronizable(true) .accessibility(.always) .set("google.com_value2", key: "google.com_key2") #else try! keychain .synchronizable(true) .accessibility(.afterFirstUnlock) .set("google.com_value2", key: "google.com_key2") #endif let allKeys = keychain.allKeys() XCTAssertEqual(allKeys.count, 2) XCTAssertEqual(allKeys.sorted(), ["google.com_key1", "google.com_key2"]) let allItems = keychain.allItems() XCTAssertEqual(allItems.count, 2) let sortedItems = allItems.sorted { (item1, item2) -> Bool in let key1 = item1["key"] as! String let key2 = item2["key"] as! String return key1.compare(key2) == .orderedAscending || key1.compare(key2) == .orderedSame } #if !os(OSX) XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false") XCTAssertEqual(sortedItems[0]["value"] as? String, "google.com_value1") XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly") #else XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AlwaysThisDeviceOnly") #endif XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "true") XCTAssertEqual(sortedItems[1]["value"] as? String, "google.com_value2") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #if targetEnvironment(macCatalyst) XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock") #else XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "Always") #endif #else XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1") XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS") XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2") XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com") XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword") XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default") XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS") #endif } #if !os(OSX) do { let allKeys = Keychain.allKeys(.genericPassword) XCTAssertEqual(allKeys.count, 5) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } let service: String #if targetEnvironment(macCatalyst) service = "maccatalyst.com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst" #else service = "com.kishikawakatsumi.KeychainAccess.TestHost" #endif XCTAssertEqual(sortedKeys[0].0, service) XCTAssertEqual(sortedKeys[0].1, "key1") XCTAssertEqual(sortedKeys[1].0, service) XCTAssertEqual(sortedKeys[1].1, "key2") XCTAssertEqual(sortedKeys[2].0, service) XCTAssertEqual(sortedKeys[2].1, "key3") XCTAssertEqual(sortedKeys[3].0, "service1") XCTAssertEqual(sortedKeys[3].1, "service1_key1") XCTAssertEqual(sortedKeys[4].0, "service1") XCTAssertEqual(sortedKeys[4].1, "service1_key2") } do { let allKeys = Keychain.allKeys(.internetPassword) XCTAssertEqual(allKeys.count, 2) let sortedKeys = allKeys.sorted { (key1, key2) -> Bool in return key1.1.compare(key2.1) == .orderedAscending || key1.1.compare(key2.1) == .orderedSame } XCTAssertEqual(sortedKeys[0].0, "google.com") XCTAssertEqual(sortedKeys[0].1, "google.com_key1") XCTAssertEqual(sortedKeys[1].0, "google.com") XCTAssertEqual(sortedKeys[1].1, "google.com_key2") } #endif } func testDescription() { do { let keychain = Keychain() XCTAssertEqual(keychain.description, "[]") XCTAssertEqual(keychain.debugDescription, "[]") } } // MARK: func testAuthenticationPolicy() { guard #available(iOS 9.0, OSX 10.11, *) else { return } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #if os(iOS) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDCurrentSet, .applicationPassword, .privateKeyUsage] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .or, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.touchIDAny, .and, .devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertTrue(accessControl != nil) } #endif #if os(OSX) do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.userPresence] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } do { let accessibility: Accessibility = .whenPasscodeSetThisDeviceOnly let policy: AuthenticationPolicy = [.devicePasscode] let flags = SecAccessControlCreateFlags(rawValue: policy.rawValue) var error: Unmanaged? let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, flags, &error) XCTAssertNil(error) XCTAssertNotNil(accessControl) } #endif } func testIgnoringAttributeSynchronizable() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") do { try keychain.set("password1234", key: "password", ignoringAttributeSynchronizable: false) } catch {} do { try keychainSynchronizable.set("password1234_synchronizable", key: "password", ignoringAttributeSynchronizable: false) } catch {} XCTAssertEqual(try! keychain.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "kishikawakatsumi_synchronizable", "stored username") do { try keychainSynchronizable.remove("username", ignoringAttributeSynchronizable: false) } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertEqual(try! keychain.get("password", ignoringAttributeSynchronizable: false), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username", ignoringAttributeSynchronizable: false), "not stored username") XCTAssertNil(try! keychain.get("password", ignoringAttributeSynchronizable: false), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password", ignoringAttributeSynchronizable: false), "not stored password") } func testIgnoringAttributeSynchronizableBackwardCompatibility() { let keychain = Keychain(service: "Twitter").synchronizable(false) let keychainSynchronizable = Keychain(service: "Twitter").synchronizable(true) XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("kishikawakatsumi", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi", "stored username") do { try keychainSynchronizable.set("kishikawakatsumi_synchronizable", key: "username") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") do { try keychain.set("password1234", key: "password") } catch {} XCTAssertEqual(try! keychain.get("password"), "password1234", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234", "stored password") do { try keychainSynchronizable.set("password1234_synchronizable", key: "password") } catch {} XCTAssertEqual(try! keychain.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychainSynchronizable.get("username"), "kishikawakatsumi_synchronizable", "stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.remove("username") } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertEqual(try! keychain.get("password"), "password1234_synchronizable", "stored password") XCTAssertEqual(try! keychainSynchronizable.get("password"), "password1234_synchronizable", "stored password") do { try keychain.removeAll() } catch {} XCTAssertNil(try! keychain.get("username"), "not stored username") XCTAssertNil(try! keychainSynchronizable.get("username"), "not stored username") XCTAssertNil(try! keychain.get("password"), "not stored password") XCTAssertNil(try! keychainSynchronizable.get("password"), "not stored password") } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/AppDelegate.swift ================================================ // // AppDelegate.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Base.lproj/Main.storyboard ================================================ ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UIApplicationSceneManifest UIApplicationSupportsMultipleScenes UISceneConfigurations UIWindowSceneSessionRoleApplication UISceneConfigurationName Default Configuration UISceneDelegateClassName $(PRODUCT_MODULE_NAME).SceneDelegate UISceneStoryboardFile Main UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/SceneDelegate.swift ================================================ // // SceneDelegate.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements ================================================ com.apple.security.app-sandbox com.apple.security.network.client keychain-access-groups $(AppIdentifierPrefix)com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst $(AppIdentifierPrefix)shared ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst/ViewController.swift ================================================ // // ViewController.swift // TestHost-MacCatalyst // // Created by Kishikawa Katsumi on 2019/10/21. // Copyright © 2019 Kishikawa Katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C4E235D284F00BBB893 /* AppDelegate.swift */; }; 14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C50235D284F00BBB893 /* SceneDelegate.swift */; }; 14A98C53235D284F00BBB893 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C52235D284F00BBB893 /* ViewController.swift */; }; 14A98C56235D284F00BBB893 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C54235D284F00BBB893 /* Main.storyboard */; }; 14A98C58235D285000BBB893 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C57235D285000BBB893 /* Assets.xcassets */; }; 14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */; }; 14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; }; 14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14A98C64235D286B00BBB893 /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */; }; 14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */; }; 14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A98C87235D2AFA00BBB893 /* EnumTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 14A98C82235D293300BBB893 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 14A98C43235D284F00BBB893 /* Project object */; proxyType = 1; remoteGlobalIDString = 14A98C4A235D284F00BBB893; remoteInfo = "TestHost-MacCatalyst"; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 14A98C67235D286B00BBB893 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 14A98C66235D286B00BBB893 /* KeychainAccess.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TestHost-MacCatalyst.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C4E235D284F00BBB893 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 14A98C50235D284F00BBB893 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 14A98C52235D284F00BBB893 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 14A98C55235D284F00BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 14A98C57235D285000BBB893 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 14A98C5A235D285000BBB893 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 14A98C5C235D285000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestHost-MacCatalyst.entitlements"; sourceTree = ""; }; 14A98C64235D286B00BBB893 /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "KeychainAccessTests-MacCatalyst.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 14A98C7E235D292000BBB893 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorTypeTests.swift; sourceTree = ""; }; 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainAccessTests.swift; sourceTree = ""; }; 14A98C87235D2AFA00BBB893 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 14A98C48235D284F00BBB893 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 14A98C65235D286B00BBB893 /* KeychainAccess.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C77235D292000BBB893 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 14A98C42235D284F00BBB893 = { isa = PBXGroup; children = ( 14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */, 14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */, 14A98C4C235D284F00BBB893 /* Products */, 14A98C63235D286B00BBB893 /* Frameworks */, ); sourceTree = ""; }; 14A98C4C235D284F00BBB893 /* Products */ = { isa = PBXGroup; children = ( 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */, 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */, ); name = Products; sourceTree = ""; }; 14A98C4D235D284F00BBB893 /* TestHost-MacCatalyst */ = { isa = PBXGroup; children = ( 14A98C4E235D284F00BBB893 /* AppDelegate.swift */, 14A98C50235D284F00BBB893 /* SceneDelegate.swift */, 14A98C52235D284F00BBB893 /* ViewController.swift */, 14A98C54235D284F00BBB893 /* Main.storyboard */, 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */, 14A98C57235D285000BBB893 /* Assets.xcassets */, 14A98C5C235D285000BBB893 /* Info.plist */, 14A98C62235D286500BBB893 /* TestHost-MacCatalyst.entitlements */, ); path = "TestHost-MacCatalyst"; sourceTree = ""; }; 14A98C63235D286B00BBB893 /* Frameworks */ = { isa = PBXGroup; children = ( 14A98C64235D286B00BBB893 /* KeychainAccess.framework */, ); name = Frameworks; sourceTree = ""; }; 14A98C7B235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = { isa = PBXGroup; children = ( 14A98C86235D2AFA00BBB893 /* KeychainAccessTests.swift */, 14A98C87235D2AFA00BBB893 /* EnumTests.swift */, 14A98C84235D2AFA00BBB893 /* ErrorTypeTests.swift */, 14A98C7E235D292000BBB893 /* Info.plist */, ); path = "KeychainAccessTests-MacCatalyst"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */ = { isa = PBXNativeTarget; buildConfigurationList = 14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */; buildPhases = ( 14A98C47235D284F00BBB893 /* Sources */, 14A98C48235D284F00BBB893 /* Frameworks */, 14A98C49235D284F00BBB893 /* Resources */, 14A98C67235D286B00BBB893 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = "TestHost-MacCatalyst"; productName = "TestHost-MacCatalyst"; productReference = 14A98C4B235D284F00BBB893 /* TestHost-MacCatalyst.app */; productType = "com.apple.product-type.application"; }; 14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */ = { isa = PBXNativeTarget; buildConfigurationList = 14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */; buildPhases = ( 14A98C76235D292000BBB893 /* Sources */, 14A98C77235D292000BBB893 /* Frameworks */, 14A98C78235D292000BBB893 /* Resources */, ); buildRules = ( ); dependencies = ( 14A98C83235D293300BBB893 /* PBXTargetDependency */, ); name = "KeychainAccessTests-MacCatalyst"; productName = "KeychainAccessTests-MacCatalyst"; productReference = 14A98C7A235D292000BBB893 /* KeychainAccessTests-MacCatalyst.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 14A98C43235D284F00BBB893 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1100; LastUpgradeCheck = 1200; ORGANIZATIONNAME = "Kishikawa Katsumi"; TargetAttributes = { 14A98C4A235D284F00BBB893 = { CreatedOnToolsVersion = 11.0; }; 14A98C79235D292000BBB893 = { CreatedOnToolsVersion = 11.0; LastSwiftMigration = 1100; TestTargetID = 14A98C4A235D284F00BBB893; }; }; }; buildConfigurationList = 14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 14A98C42235D284F00BBB893; productRefGroup = 14A98C4C235D284F00BBB893 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */, 14A98C79235D292000BBB893 /* KeychainAccessTests-MacCatalyst */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 14A98C49235D284F00BBB893 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C5B235D285000BBB893 /* LaunchScreen.storyboard in Resources */, 14A98C58235D285000BBB893 /* Assets.xcassets in Resources */, 14A98C56235D284F00BBB893 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C78235D292000BBB893 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 14A98C47235D284F00BBB893 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C53235D284F00BBB893 /* ViewController.swift in Sources */, 14A98C4F235D284F00BBB893 /* AppDelegate.swift in Sources */, 14A98C51235D284F00BBB893 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 14A98C76235D292000BBB893 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 14A98C88235D2AFA00BBB893 /* ErrorTypeTests.swift in Sources */, 14A98C8B235D2AFA00BBB893 /* EnumTests.swift in Sources */, 14A98C8A235D2AFA00BBB893 /* KeychainAccessTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 14A98C83235D293300BBB893 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 14A98C4A235D284F00BBB893 /* TestHost-MacCatalyst */; targetProxy = 14A98C82235D293300BBB893 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ 14A98C54235D284F00BBB893 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 14A98C55235D284F00BBB893 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 14A98C59235D285000BBB893 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 14A98C5A235D285000BBB893 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 14A98C5D235D285000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 14A98C5E235D285000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 14A98C60235D285000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements"; CODE_SIGN_STYLE = Automatic; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 14A98C61235D285000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "TestHost-MacCatalyst/TestHost-MacCatalyst.entitlements"; CODE_SIGN_STYLE = Automatic; DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "TestHost-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccess.TestHost-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 14A98C80235D292000BBB893 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst"; }; name = Debug; }; 14A98C81235D292000BBB893 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 27AEDK3C9F; INFOPLIST_FILE = "KeychainAccessTests-MacCatalyst/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "com.kishikawakatsumi.KeychainAccessTests-MacCatalyst"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestHost-MacCatalyst.app/TestHost-MacCatalyst"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 14A98C46235D284F00BBB893 /* Build configuration list for PBXProject "TestHost-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C5D235D285000BBB893 /* Debug */, 14A98C5E235D285000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A98C5F235D285000BBB893 /* Build configuration list for PBXNativeTarget "TestHost-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C60235D285000BBB893 /* Debug */, 14A98C61235D285000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 14A98C7F235D292000BBB893 /* Build configuration list for PBXNativeTarget "KeychainAccessTests-MacCatalyst" */ = { isa = XCConfigurationList; buildConfigurations = ( 14A98C80235D292000BBB893 /* Debug */, 14A98C81235D292000BBB893 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 14A98C43235D284F00BBB893 /* Project object */; } ================================================ FILE: External/KeychainAccess/Lib/TestHost-MacCatalyst/TestHost-MacCatalyst.xcodeproj/xcshareddata/xcschemes/TestHost-MacCatalyst.xcscheme ================================================ ================================================ FILE: External/KeychainAccess/Package.swift ================================================ // swift-tools-version:5.0 // Package.swift // KeychainAccess // // Created by kishikawa katsumi on 2015/12/4. // Copyright (c) 2015 kishikawa katsumi. All rights reserved. // import PackageDescription let package = Package( name: "KeychainAccess", platforms: [ .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) ], products: [ .library(name: "KeychainAccess", targets: ["KeychainAccess"]) ], targets: [ .target(name: "KeychainAccess", path: "Lib/KeychainAccess") ] ) ================================================ FILE: External/KeychainAccess/Package@swift-5.3.swift ================================================ // swift-tools-version:5.3 // Package.swift // KeychainAccess // // Created by kishikawa katsumi on 2015/12/4. // Copyright (c) 2015 kishikawa katsumi. All rights reserved. // import PackageDescription let package = Package( name: "KeychainAccess", platforms: [ .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) ], products: [ .library(name: "KeychainAccess", targets: ["KeychainAccess"]) ], targets: [ .target(name: "KeychainAccess", path: "Lib/KeychainAccess", exclude:["Info.plist"]) ] ) ================================================ FILE: External/KeychainAccess/README.md ================================================ # KeychainAccess [![Build Status](https://travis-ci.com/kishikawakatsumi/KeychainAccess.svg?branch=master)](https://travis-ci.com/kishikawakatsumi/KeychainAccess) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![SPM supported](https://img.shields.io/badge/SPM-supported-DE5C43.svg?style=flat)](https://swift.org/package-manager) [![Version](https://img.shields.io/cocoapods/v/KeychainAccess.svg)](http://cocoadocs.org/docsets/KeychainAccess) [![Platform](https://img.shields.io/cocoapods/p/KeychainAccess.svg)](http://cocoadocs.org/docsets/KeychainAccess) KeychainAccess is a simple Swift wrapper for Keychain that works on iOS and OS X. Makes using Keychain APIs extremely easy and much more palatable to use in Swift. ## :bulb: Features - Simple interface - Support access group - [Support accessibility](#accessibility) - [Support iCloud sharing](#icloud_sharing) - **[Support TouchID and Keychain integration (iOS 8+)](#touch_id_integration)** - **[Support Shared Web Credentials (iOS 8+)](#shared_web_credentials)** - [Works on both iOS & macOS](#requirements) - [watchOS and tvOS are supported](#requirements) - **[Mac Catalyst is supported](#requirements)** - **[Swift 3, 4 and 5 compatible](#requirements)** ## :book: Usage ##### :eyes: See also: - [:link: iOS Example Project](https://github.com/kishikawakatsumi/KeychainAccess/tree/master/Examples/Example-iOS) ### :key: Basics #### Saving Application Password ```swift let keychain = Keychain(service: "com.example.github-token") keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` #### Saving Internet Password ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ### :key: Instantiation #### Create Keychain for Application Password ```swift let keychain = Keychain(service: "com.example.github-token") ``` ```swift let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared") ``` #### Create Keychain for Internet Password ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) ``` ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https, authenticationType: .htmlForm) ``` ### :key: Adding an item #### subscripting ##### for String ```swift keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ```swift keychain[string: "kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ##### for NSData ```swift keychain[data: "secret"] = NSData(contentsOfFile: "secret.bin") ``` #### set method ```swift keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") ``` #### error handling ```swift do { try keychain.set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print(error) } ``` ### :key: Obtaining an item #### subscripting ##### for String (If the value is NSData, attempt to convert to String) ```swift let token = keychain["kishikawakatsumi"] ``` ```swift let token = keychain[string: "kishikawakatsumi"] ``` ##### for NSData ```swift let secretData = keychain[data: "secret"] ``` #### get methods ##### as String ```swift let token = try? keychain.get("kishikawakatsumi") ``` ```swift let token = try? keychain.getString("kishikawakatsumi") ``` ##### as NSData ```swift let data = try? keychain.getData("kishikawakatsumi") ``` ### :key: Removing an item #### subscripting ```swift keychain["kishikawakatsumi"] = nil ``` #### remove method ```swift do { try keychain.remove("kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :key: Set Label and Comment ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) do { try keychain .label("github.com (kishikawakatsumi)") .comment("github access token") .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :key: Obtaining Other Attributes #### PersistentRef ```swift let keychain = Keychain() let persistentRef = keychain[attributes: "kishikawakatsumi"]?.persistentRef ... ``` #### Creation Date ```swift let keychain = Keychain() let creationDate = keychain[attributes: "kishikawakatsumi"]?.creationDate ... ``` #### All Attributes ```swift let keychain = Keychain() do { let attributes = try keychain.get("kishikawakatsumi") { $0 } print(attributes?.comment) print(attributes?.label) print(attributes?.creator) ... } catch let error { print("error: \(error)") } ``` ##### subscripting ```swift let keychain = Keychain() if let attributes = keychain[attributes: "kishikawakatsumi"] { print(attributes.comment) print(attributes.label) print(attributes.creator) } ``` ### :key: Configuration (Accessibility, Sharing, iCloud Sync) **Provides fluent interfaces** ```swift let keychain = Keychain(service: "com.example.github-token") .label("github.com (kishikawakatsumi)") .synchronizable(true) .accessibility(.afterFirstUnlock) ``` #### Accessibility ##### Default accessibility matches background application (=kSecAttrAccessibleAfterFirstUnlock) ```swift let keychain = Keychain(service: "com.example.github-token") ``` ##### For background application ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .accessibility(.afterFirstUnlock) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .accessibility(.afterFirstUnlock) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ##### For foreground application ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .accessibility(.whenUnlocked) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .accessibility(.whenUnlocked) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` #### :couple: Sharing Keychain items ```swift let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared") ``` #### :arrows_counterclockwise: Synchronizing Keychain items with iCloud ###### Creating instance ```swift let keychain = Keychain(service: "com.example.github-token") .synchronizable(true) keychain["kishikawakatsumi"] = "01234567-89ab-cdef-0123-456789abcdef" ``` ###### One-shot ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain .synchronizable(true) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { print("error: \(error)") } ``` ### :cyclone: Touch ID (Face ID) integration **Any Operation that require authentication must be run in the background thread.** **If you run in the main thread, UI thread will lock for the system to try to display the authentication dialog.** **To use Face ID, add `NSFaceIDUsageDescription` key to your `Info.plist`** #### :closed_lock_with_key: Adding a Touch ID (Face ID) protected item If you want to store the Touch ID protected Keychain item, specify `accessibility` and `authenticationPolicy` attributes. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { // Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked` try keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny]) .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Updating a Touch ID (Face ID) protected item The same way as when adding. **Do not run in the main thread if there is a possibility that the item you are trying to add already exists, and protected.** **Because updating protected items requires authentication.** Additionally, you want to show custom authentication prompt message when updating, specify an `authenticationPrompt` attribute. If the item not protected, the `authenticationPrompt` parameter just be ignored. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { // Should be the secret invalidated when passcode is removed? If not then use `.WhenUnlocked` try keychain .accessibility(.whenPasscodeSetThisDeviceOnly, authenticationPolicy: [.biometryAny]) .authenticationPrompt("Authenticate to update your access token") .set("01234567-89ab-cdef-0123-456789abcdef", key: "kishikawakatsumi") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Obtaining a Touch ID (Face ID) protected item The same way as when you get a normal item. It will be displayed automatically Touch ID or passcode authentication If the item you try to get is protected. If you want to show custom authentication prompt message, specify an `authenticationPrompt` attribute. If the item not protected, the `authenticationPrompt` parameter just be ignored. ```swift let keychain = Keychain(service: "com.example.github-token") DispatchQueue.global().async { do { let password = try keychain .authenticationPrompt("Authenticate to login to server") .get("kishikawakatsumi") print("password: \(password)") } catch let error { // Error handling if needed... } } ``` #### :closed_lock_with_key: Removing a Touch ID (Face ID) protected item The same way as when you remove a normal item. There is no way to show Touch ID or passcode authentication when removing Keychain items. ```swift let keychain = Keychain(service: "com.example.github-token") do { try keychain.remove("kishikawakatsumi") } catch let error { // Error handling if needed... } ``` ### :key: Shared Web Credentials > Shared web credentials is a programming interface that enables native iOS apps to share credentials with their website counterparts. For example, a user may log in to a website in Safari, entering a user name and password, and save those credentials using the iCloud Keychain. Later, the user may run a native app from the same developer, and instead of the app requiring the user to reenter a user name and password, shared web credentials gives it access to the credentials that were entered earlier in Safari. The user can also create new accounts, update passwords, or delete her account from within the app. These changes are then saved and used by Safari. ```swift let keychain = Keychain(server: "https://www.kishikawakatsumi.com", protocolType: .HTTPS) let username = "kishikawakatsumi@mac.com" // First, check the credential in the app's Keychain if let password = try? keychain.get(username) { // If found password in the Keychain, // then log into the server } else { // If not found password in the Keychain, // try to read from Shared Web Credentials keychain.getSharedPassword(username) { (password, error) -> () in if password != nil { // If found password in the Shared Web Credentials, // then log into the server // and save the password to the Keychain keychain[username] = password } else { // If not found password either in the Keychain also Shared Web Credentials, // prompt for username and password // Log into server // If the login is successful, // save the credentials to both the Keychain and the Shared Web Credentials. keychain[username] = inputPassword keychain.setSharedPassword(inputPassword, account: username) } } } ``` #### Request all associated domain's credentials ```swift Keychain.requestSharedWebCredential { (credentials, error) -> () in } ``` #### Generate strong random password Generate strong random password that is in the same format used by Safari autofill (xxx-xxx-xxx-xxx). ```swift let password = Keychain.generatePassword() // => Nhu-GKm-s3n-pMx ``` #### How to set up Shared Web Credentials > 1. Add a com.apple.developer.associated-domains entitlement to your app. This entitlement must include all the domains with which you want to share credentials. > > 2. Add an apple-app-site-association file to your website. This file must include application identifiers for all the apps with which the site wants to share credentials, and it must be properly signed. > > 3. When the app is installed, the system downloads and verifies the site association file for each of its associated domains. If the verification is successful, the app is associated with the domain. **More details:** ### :mag: Debugging #### Display all stored items if print keychain object ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) print("\(keychain)") ``` ``` => [ [authenticationType: default, key: kishikawakatsumi, server: github.com, class: internetPassword, protocol: https] [authenticationType: default, key: hirohamada, server: github.com, class: internetPassword, protocol: https] [authenticationType: default, key: honeylemon, server: github.com, class: internetPassword, protocol: https] ] ``` #### Obtaining all stored keys ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) let keys = keychain.allKeys() for key in keys { print("key: \(key)") } ``` ``` => key: kishikawakatsumi key: hirohamada key: honeylemon ``` #### Obtaining all stored items ```swift let keychain = Keychain(server: "https://github.com", protocolType: .https) let items = keychain.allItems() for item in items { print("item: \(item)") } ``` ``` => item: [authenticationType: Default, key: kishikawakatsumi, server: github.com, class: InternetPassword, protocol: https] item: [authenticationType: Default, key: hirohamada, server: github.com, class: InternetPassword, protocol: https] item: [authenticationType: Default, key: honeylemon, server: github.com, class: InternetPassword, protocol: https] ``` ## Keychain sharing capability If you encounter the error below, you need to add an `Keychain.entitlements`. ``` OSStatus error:[-34018] Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements. ``` Screen Shot 2019-10-27 at 8 08 50 ## Requirements | | OS | Swift | |------------|------------------------------------------------------------|--------------------| | **v1.1.x** | iOS 7+, macOS 10.9+ | 1.1 | | **v1.2.x** | iOS 7+, macOS 10.9+ | 1.2 | | **v2.0.x** | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 | | **v2.1.x** | iOS 7+, macOS 10.9+, watchOS 2+ | 2.0 | | **v2.2.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1 | | **v2.3.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.0, 2.1, 2.2 | | **v2.4.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 2.2, 2.3 | | **v3.0.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 3.x | | **v3.1.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2 | | **v3.2.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.0 | | **v4.0.x** | iOS 8+, macOS 10.9+, watchOS 2+, tvOS 9+ | 4.0, 4.1, 4.2, 5.1 | | **v4.1.x** | iOS 8+, macOS 10.9+, watchOS 3+, tvOS 9+, Mac Catalyst 13+ | 4.0, 4.1, 4.2, 5.1 | ## Installation ### CocoaPods KeychainAccess is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following lines to your Podfile: ```ruby use_frameworks! pod 'KeychainAccess' ``` ### Carthage KeychainAccess is available through [Carthage](https://github.com/Carthage/Carthage). To install it, simply add the following line to your Cartfile: `github "kishikawakatsumi/KeychainAccess"` ### Swift Package Manager KeychainAccess is also available through [Swift Package Manager](https://github.com/apple/swift-package-manager/). #### Xcode Select `File > Add Packages... > Add Package Dependency...`, #### CLI First, create `Package.swift` that its package declaration includes: ```swift // swift-tools-version:5.0 import PackageDescription let package = Package( name: "MyLibrary", products: [ .library(name: "MyLibrary", targets: ["MyLibrary"]), ], dependencies: [ .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "3.0.0"), ], targets: [ .target(name: "MyLibrary", dependencies: ["KeychainAccess"]), ] ) ``` Then, type ```shell $ swift build ``` ### To manually add to your project 1. Add `Lib/KeychainAccess.xcodeproj` to your project 2. Link `KeychainAccess.framework` with your target 3. Add `Copy Files Build Phase` to include the framework to your application bundle _See [iOS Example Project](https://github.com/kishikawakatsumi/KeychainAccess/tree/master/Examples/Example-iOS) as reference._ ## Author kishikawa katsumi, kishikawakatsumi@mac.com ## License KeychainAccess is available under the MIT license. See the LICENSE file for more info. ================================================ FILE: External/KeychainAccess/Sources/Keychain.swift ================================================ // // Keychain.swift // KeychainAccess // // Created by kishikawa katsumi on 2014/12/24. // Copyright (c) 2014 kishikawa katsumi. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation import Security #if os(iOS) || os(OSX) import LocalAuthentication #endif public let KeychainAccessErrorDomain = "com.kishikawakatsumi.KeychainAccess.error" public enum ItemClass { case genericPassword case internetPassword } public enum ProtocolType { case ftp case ftpAccount case http case irc case nntp case pop3 case smtp case socks case imap case ldap case appleTalk case afp case telnet case ssh case ftps case https case httpProxy case httpsProxy case ftpProxy case smb case rtsp case rtspProxy case daap case eppc case ipp case nntps case ldaps case telnetS case imaps case ircs case pop3S } public enum AuthenticationType { case ntlm case msn case dpa case rpa case httpBasic case httpDigest case htmlForm case `default` } public enum Accessibility { /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will migrate to a new device when using encrypted backups. */ case whenUnlocked /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accesible by background applications. Items with this attribute will migrate to a new device when using encrypted backups. */ case afterFirstUnlock /** Item data can always be accessed regardless of the lock state of the device. This is not recommended for anything except system use. Items with this attribute will migrate to a new device when using encrypted backups. */ @available(macCatalyst, unavailable) case always /** Item data can only be accessed while the device is unlocked. This class is only available if a passcode is set on the device. This is recommended for items that only need to be accessible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. No items can be stored in this class on devices without a passcode. Disabling the device passcode will cause all items in this class to be deleted. */ @available(iOS 8.0, OSX 10.10, *) case whenPasscodeSetThisDeviceOnly /** Item data can only be accessed while the device is unlocked. This is recommended for items that only need be accesible while the application is in the foreground. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ case whenUnlockedThisDeviceOnly /** Item data can only be accessed once the device has been unlocked after a restart. This is recommended for items that need to be accessible by background applications. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device these items will be missing. */ case afterFirstUnlockThisDeviceOnly /** Item data can always be accessed regardless of the lock state of the device. This option is not recommended for anything except system use. Items with this attribute will never migrate to a new device, so after a backup is restored to a new device, these items will be missing. */ @available(macCatalyst, unavailable) case alwaysThisDeviceOnly } /** Predefined item attribute constants used to get or set values in a dictionary. The kSecUseAuthenticationUI constant is the key and its value is one of the constants defined here. If the key kSecUseAuthenticationUI not provided then kSecUseAuthenticationUIAllow is used as default. */ public enum AuthenticationUI { /** Specifies that authenticate UI can appear. */ case allow /** Specifies that the error errSecInteractionNotAllowed will be returned if an item needs to authenticate with UI */ case fail /** Specifies that all items which need to authenticate with UI will be silently skipped. This value can be used only with SecItemCopyMatching. */ case skip } @available(iOS 9.0, OSX 10.11, *) extension AuthenticationUI { public var rawValue: String { switch self { case .allow: return UseAuthenticationUIAllow case .fail: return UseAuthenticationUIFail case .skip: return UseAuthenticationUISkip } } public var description: String { switch self { case .allow: return "allow" case .fail: return "fail" case .skip: return "skip" } } } public struct AuthenticationPolicy: OptionSet { /** User presence policy using Touch ID or Passcode. Touch ID does not have to be available or enrolled. Item is still accessible by Touch ID even if fingers are added or removed. */ @available(iOS 8.0, OSX 10.10, watchOS 2.0, tvOS 8.0, *) public static let userPresence = AuthenticationPolicy(rawValue: 1 << 0) /** Constraint: Touch ID (any finger) or Face ID. Touch ID or Face ID must be available. With Touch ID at least one finger must be enrolled. With Face ID user has to be enrolled. Item is still accessible by Touch ID even if fingers are added or removed. Item is still accessible by Face ID if user is re-enrolled. */ @available(iOS 11.3, OSX 10.13.4, watchOS 4.3, tvOS 11.3, *) public static let biometryAny = AuthenticationPolicy(rawValue: 1 << 1) /** Deprecated, please use biometryAny instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryAny") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryAny") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryAny") public static let touchIDAny = AuthenticationPolicy(rawValue: 1 << 1) /** Constraint: Touch ID from the set of currently enrolled fingers. Touch ID must be available and at least one finger must be enrolled. When fingers are added or removed, the item is invalidated. When Face ID is re-enrolled this item is invalidated. */ @available(iOS 11.3, OSX 10.13, watchOS 4.3, tvOS 11.3, *) public static let biometryCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Deprecated, please use biometryCurrentSet instead. */ @available(iOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") @available(OSX, introduced: 10.12.1, deprecated: 10.13.4, renamed: "biometryCurrentSet") @available(watchOS, introduced: 2.0, deprecated: 4.3, renamed: "biometryCurrentSet") @available(tvOS, introduced: 9.0, deprecated: 11.3, renamed: "biometryCurrentSet") public static let touchIDCurrentSet = AuthenticationPolicy(rawValue: 1 << 3) /** Constraint: Device passcode */ @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) public static let devicePasscode = AuthenticationPolicy(rawValue: 1 << 4) /** Constraint: Watch */ @available(iOS, unavailable) @available(OSX 10.15, *) @available(watchOS, unavailable) @available(tvOS, unavailable) public static let watch = AuthenticationPolicy(rawValue: 1 << 5) /** Constraint logic operation: when using more than one constraint, at least one of them must be satisfied. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let or = AuthenticationPolicy(rawValue: 1 << 14) /** Constraint logic operation: when using more than one constraint, all must be satisfied. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let and = AuthenticationPolicy(rawValue: 1 << 15) /** Create access control for private key operations (i.e. sign operation) */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let privateKeyUsage = AuthenticationPolicy(rawValue: 1 << 30) /** Security: Application provided password for data encryption key generation. This is not a constraint but additional item encryption mechanism. */ @available(iOS 9.0, OSX 10.12.1, watchOS 2.0, tvOS 9.0, *) public static let applicationPassword = AuthenticationPolicy(rawValue: 1 << 31) #if swift(>=2.3) public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } #else public let rawValue: Int public init(rawValue: Int) { self.rawValue = rawValue } #endif } public struct Attributes { public var `class`: String? { return attributes[Class] as? String } public var data: Data? { return attributes[ValueData] as? Data } public var ref: Data? { return attributes[ValueRef] as? Data } public var persistentRef: Data? { return attributes[ValuePersistentRef] as? Data } public var accessible: String? { return attributes[AttributeAccessible] as? String } public var accessControl: SecAccessControl? { if #available(OSX 10.10, *) { if let accessControl = attributes[AttributeAccessControl] { return (accessControl as! SecAccessControl) } return nil } else { return nil } } public var accessGroup: String? { return attributes[AttributeAccessGroup] as? String } public var synchronizable: Bool? { return attributes[AttributeSynchronizable] as? Bool } public var creationDate: Date? { return attributes[AttributeCreationDate] as? Date } public var modificationDate: Date? { return attributes[AttributeModificationDate] as? Date } public var attributeDescription: String? { return attributes[AttributeDescription] as? String } public var comment: String? { return attributes[AttributeComment] as? String } public var creator: String? { return attributes[AttributeCreator] as? String } public var type: String? { return attributes[AttributeType] as? String } public var label: String? { return attributes[AttributeLabel] as? String } public var isInvisible: Bool? { return attributes[AttributeIsInvisible] as? Bool } public var isNegative: Bool? { return attributes[AttributeIsNegative] as? Bool } public var account: String? { return attributes[AttributeAccount] as? String } public var service: String? { return attributes[AttributeService] as? String } public var generic: Data? { return attributes[AttributeGeneric] as? Data } public var securityDomain: String? { return attributes[AttributeSecurityDomain] as? String } public var server: String? { return attributes[AttributeServer] as? String } public var `protocol`: String? { return attributes[AttributeProtocol] as? String } public var authenticationType: String? { return attributes[AttributeAuthenticationType] as? String } public var port: Int? { return attributes[AttributePort] as? Int } public var path: String? { return attributes[AttributePath] as? String } fileprivate let attributes: [String: Any] init(attributes: [String: Any]) { self.attributes = attributes } public subscript(key: String) -> Any? { get { return attributes[key] } } } public final class Keychain { public var itemClass: ItemClass { return options.itemClass } public var service: String { return options.service } // This attribute (kSecAttrAccessGroup) applies to macOS keychain items only if you also set a value of true for the // kSecUseDataProtectionKeychain key, the kSecAttrSynchronizable key, or both. public var accessGroup: String? { return options.accessGroup } public var server: URL { return options.server } public var protocolType: ProtocolType { return options.protocolType } public var authenticationType: AuthenticationType { return options.authenticationType } public var accessibility: Accessibility { return options.accessibility } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public var authenticationPolicy: AuthenticationPolicy? { return options.authenticationPolicy } public var synchronizable: Bool { return options.synchronizable } public var label: String? { return options.label } public var comment: String? { return options.comment } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public var authenticationPrompt: String? { return options.authenticationPrompt } @available(iOS 9.0, OSX 10.11, *) public var authenticationUI: AuthenticationUI { return options.authenticationUI ?? .allow } #if os(iOS) || os(OSX) @available(iOS 9.0, OSX 10.11, *) public var authenticationContext: LAContext? { return options.authenticationContext as? LAContext } #endif fileprivate let options: Options // MARK: public convenience init() { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } self.init(options) } public convenience init(service: String) { var options = Options() options.service = service self.init(options) } public convenience init(accessGroup: String) { var options = Options() if let bundleIdentifier = Bundle.main.bundleIdentifier { options.service = bundleIdentifier } options.accessGroup = accessGroup self.init(options) } public convenience init(service: String, accessGroup: String) { var options = Options() options.service = service options.accessGroup = accessGroup self.init(options) } public convenience init(server: String, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { self.init(server: URL(string: server)!, protocolType: protocolType, accessGroup: accessGroup, authenticationType: authenticationType) } public convenience init(server: URL, protocolType: ProtocolType, accessGroup: String? = nil, authenticationType: AuthenticationType = .default) { var options = Options() options.itemClass = .internetPassword options.server = server options.protocolType = protocolType options.accessGroup = accessGroup options.authenticationType = authenticationType self.init(options) } fileprivate init(_ opts: Options) { options = opts } // MARK: public func accessibility(_ accessibility: Accessibility) -> Keychain { var options = self.options options.accessibility = accessibility return Keychain(options) } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public func accessibility(_ accessibility: Accessibility, authenticationPolicy: AuthenticationPolicy) -> Keychain { var options = self.options options.accessibility = accessibility options.authenticationPolicy = authenticationPolicy return Keychain(options) } public func synchronizable(_ synchronizable: Bool) -> Keychain { var options = self.options options.synchronizable = synchronizable return Keychain(options) } public func label(_ label: String) -> Keychain { var options = self.options options.label = label return Keychain(options) } public func comment(_ comment: String) -> Keychain { var options = self.options options.comment = comment return Keychain(options) } public func attributes(_ attributes: [String: Any]) -> Keychain { var options = self.options attributes.forEach { options.attributes.updateValue($1, forKey: $0) } return Keychain(options) } @available(iOS 8.0, OSX 10.10, *) @available(watchOS, unavailable) public func authenticationPrompt(_ authenticationPrompt: String) -> Keychain { var options = self.options options.authenticationPrompt = authenticationPrompt return Keychain(options) } @available(iOS 9.0, OSX 10.11, *) public func authenticationUI(_ authenticationUI: AuthenticationUI) -> Keychain { var options = self.options options.authenticationUI = authenticationUI return Keychain(options) } #if os(iOS) || os(OSX) @available(iOS 9.0, OSX 10.11, *) public func authenticationContext(_ authenticationContext: LAContext) -> Keychain { var options = self.options options.authenticationContext = authenticationContext return Keychain(options) } #endif // MARK: public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { return try getString(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func getString(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> String? { guard let data = try getData(key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) else { return nil } guard let string = String(data: data, encoding: .utf8) else { print("failed to convert data to string") throw Status.conversionError } return string } public func getData(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws -> Data? { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let data = result as? Data else { throw Status.unexpectedError } return data case errSecItemNotFound: return nil default: throw securityError(status: status) } } public func get(_ key: String, ignoringAttributeSynchronizable: Bool = true, handler: (Attributes?) -> T) throws -> T { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[MatchLimit] = MatchLimitOne query[ReturnData] = kCFBooleanTrue query[ReturnAttributes] = kCFBooleanTrue query[ReturnRef] = kCFBooleanTrue query[ReturnPersistentRef] = kCFBooleanTrue query[AttributeAccount] = key var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: guard let attributes = result as? [String: Any] else { throw Status.unexpectedError } return handler(Attributes(attributes: attributes)) case errSecItemNotFound: return handler(nil) default: throw securityError(status: status) } } // MARK: public func set(_ value: String, key: String, ignoringAttributeSynchronizable: Bool = true) throws { guard let data = value.data(using: .utf8, allowLossyConversion: false) else { print("failed to convert string to data") throw Status.conversionError } try set(data, key: key, ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) } public func set(_ value: Data, key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key #if os(iOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #elseif os(OSX) query[ReturnData] = kCFBooleanTrue if #available(OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } #else if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } #endif var status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess, errSecInteractionNotAllowed: var query = options.query() query[AttributeAccount] = key var (attributes, error) = options.attributes(key: nil, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } #if os(iOS) if status == errSecInteractionNotAllowed && floor(NSFoundationVersionNumber) <= floor(NSFoundationVersionNumber_iOS_8_0) { try remove(key) try set(value, key: key) } else { status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } } #else status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) if status != errSecSuccess { throw securityError(status: status) } #endif case errSecItemNotFound: var (attributes, error) = options.attributes(key: key, value: value) if let error = error { print(error.localizedDescription) throw error } options.attributes.forEach { attributes.updateValue($1, forKey: $0) } status = SecItemAdd(attributes as CFDictionary, nil) if status != errSecSuccess { throw securityError(status: status) } default: throw securityError(status: status) } } public subscript(key: String) -> String? { get { #if swift(>=5.0) return try? get(key) #else return (try? get(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(string key: String) -> String? { get { return self[key] } set { self[key] = newValue } } public subscript(data key: String) -> Data? { get { #if swift(>=5.0) return try? getData(key) #else return (try? getData(key)).flatMap { $0 } #endif } set { if let value = newValue { do { try set(value, key: key) } catch {} } else { do { try remove(key) } catch {} } } } public subscript(attributes key: String) -> Attributes? { get { #if swift(>=5.0) return try? get(key) { $0 } #else return (try? get(key) { $0 }).flatMap { $0 } #endif } } // MARK: public func remove(_ key: String, ignoringAttributeSynchronizable: Bool = true) throws { var query = options.query(ignoringAttributeSynchronizable: ignoringAttributeSynchronizable) query[AttributeAccount] = key let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } public func removeAll() throws { var query = options.query() #if !os(iOS) && !os(watchOS) && !os(tvOS) query[MatchLimit] = MatchLimitAll #endif let status = SecItemDelete(query as CFDictionary) if status != errSecSuccess && status != errSecItemNotFound { throw securityError(status: status) } } // MARK: public func contains(_ key: String, withoutAuthenticationUI: Bool = false) throws -> Bool { var query = options.query() query[AttributeAccount] = key if withoutAuthenticationUI { #if os(iOS) || os(watchOS) || os(tvOS) if #available(iOS 9.0, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else { query[UseNoAuthenticationUI] = kCFBooleanTrue } #else if #available(OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } else { query[UseAuthenticationUI] = UseAuthenticationUIFail } } else if #available(OSX 10.10, *) { query[UseNoAuthenticationUI] = kCFBooleanTrue } #endif } else { if #available(iOS 9.0, OSX 10.11, *) { if let authenticationUI = options.authenticationUI { query[UseAuthenticationUI] = authenticationUI.rawValue } } } let status = SecItemCopyMatching(query as CFDictionary, nil) switch status { case errSecSuccess: return true case errSecInteractionNotAllowed: if withoutAuthenticationUI { return true } return false case errSecItemNotFound: return false default: throw securityError(status: status) } } // MARK: public class func allKeys(_ itemClass: ItemClass) -> [(String, String)] { var query = [String: Any]() query[Class] = itemClass.rawValue query[AttributeSynchronizable] = SynchronizableAny query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items).map { switch itemClass { case .genericPassword: return (($0["service"] ?? "") as! String, ($0["key"] ?? "") as! String) case .internetPassword: return (($0["server"] ?? "") as! String, ($0["key"] ?? "") as! String) } } } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allKeys() -> [String] { let allItems = type(of: self).prettify(itemClass: itemClass, items: items()) let filter: ([String: Any]) -> String? = { $0["key"] as? String } #if swift(>=4.1) return allItems.compactMap(filter) #else return allItems.flatMap(filter) #endif } public class func allItems(_ itemClass: ItemClass) -> [[String: Any]] { var query = [String: Any]() query[Class] = itemClass.rawValue query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return prettify(itemClass: itemClass, items: items) } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } public func allItems() -> [[String: Any]] { return type(of: self).prettify(itemClass: itemClass, items: items()) } #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ completion: @escaping (_ account: String?, _ password: String?, _ error: Error?) -> () = { account, password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: nil) { (credentials, error) -> () in if let credential = credentials.first { let account = credential["account"] let password = credential["password"] completion(account, password, error) } else { completion(nil, nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func getSharedPassword(_ account: String, completion: @escaping (_ password: String?, _ error: Error?) -> () = { password, error -> () in }) { if let domain = server.host { type(of: self).requestSharedWebCredential(domain: domain, account: account) { (credentials, error) -> () in if let credential = credentials.first { if let password = credential["password"] { completion(password, error) } else { completion(nil, error) } } else { completion(nil, error) } } } else { let error = securityError(status: Status.param.rawValue) completion(nil, error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func setSharedPassword(_ password: String, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(password as String?, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate func setSharedPassword(_ password: String?, account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { if let domain = server.host { SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { error -> () in if let error = error { completion(error.error) } else { completion(nil) } } } else { let error = securityError(status: Status.param.rawValue) completion(error) } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public func removeSharedPassword(_ account: String, completion: @escaping (_ error: Error?) -> () = { e -> () in }) { setSharedPassword(nil, account: account, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(_ completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: nil, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: domain, account: nil, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) public class func requestSharedWebCredential(domain: String, account: String, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> () = { credentials, error -> () in }) { requestSharedWebCredential(domain: Optional(domain), account: Optional(account)!, completion: completion) } #endif #if os(iOS) && !targetEnvironment(macCatalyst) @available(iOS 8.0, *) fileprivate class func requestSharedWebCredential(domain: String?, account: String?, completion: @escaping (_ credentials: [[String: String]], _ error: Error?) -> ()) { SecRequestSharedWebCredential(domain as CFString?, account as CFString?) { (credentials, error) -> () in var remoteError: NSError? if let error = error { remoteError = error.error if remoteError?.code != Int(errSecItemNotFound) { print("error:[\(remoteError!.code)] \(remoteError!.localizedDescription)") } } if let credentials = credentials { let credentials = (credentials as NSArray).map { credentials -> [String: String] in var credential = [String: String]() if let credentials = credentials as? [String: String] { if let server = credentials[AttributeServer] { credential["server"] = server } if let account = credentials[AttributeAccount] { credential["account"] = account } if let password = credentials[SharedPassword] { credential["password"] = password } } return credential } completion(credentials, remoteError) } else { completion([], remoteError) } } } #endif #if os(iOS) && !targetEnvironment(macCatalyst) /** @abstract Returns a randomly generated password. @return String password in the form xxx-xxx-xxx-xxx where x is taken from the sets "abcdefghkmnopqrstuvwxy", "ABCDEFGHJKLMNPQRSTUVWXYZ", "3456789" with at least one character from each set being present. */ @available(iOS 8.0, *) public class func generatePassword() -> String { return SecCreateSharedWebCredentialPassword()! as String } #endif // MARK: fileprivate func items() -> [[String: Any]] { var query = options.query() query[MatchLimit] = MatchLimitAll query[ReturnAttributes] = kCFBooleanTrue #if os(iOS) || os(watchOS) || os(tvOS) query[ReturnData] = kCFBooleanTrue #endif var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) switch status { case errSecSuccess: if let items = result as? [[String: Any]] { return items } case errSecItemNotFound: return [] default: () } securityError(status: status) return [] } fileprivate class func prettify(itemClass: ItemClass, items: [[String: Any]]) -> [[String: Any]] { let items = items.map { attributes -> [String: Any] in var item = [String: Any]() item["class"] = itemClass.description if let accessGroup = attributes[AttributeAccessGroup] as? String { item["accessGroup"] = accessGroup } switch itemClass { case .genericPassword: if let service = attributes[AttributeService] as? String { item["service"] = service } case .internetPassword: if let server = attributes[AttributeServer] as? String { item["server"] = server } if let proto = attributes[AttributeProtocol] as? String { if let protocolType = ProtocolType(rawValue: proto) { item["protocol"] = protocolType.description } } if let auth = attributes[AttributeAuthenticationType] as? String { if let authenticationType = AuthenticationType(rawValue: auth) { item["authenticationType"] = authenticationType.description } } } if let key = attributes[AttributeAccount] as? String { item["key"] = key } if let data = attributes[ValueData] as? Data { if let text = String(data: data, encoding: .utf8) { item["value"] = text } else { item["value"] = data } } if let accessible = attributes[AttributeAccessible] as? String { if let accessibility = Accessibility(rawValue: accessible) { item["accessibility"] = accessibility.description } } if let synchronizable = attributes[AttributeSynchronizable] as? Bool { item["synchronizable"] = synchronizable ? "true" : "false" } return item } return items } // MARK: @discardableResult fileprivate class func securityError(status: OSStatus) -> Error { let error = Status(status: status) if error != .userCanceled { print("OSStatus error:[\(error.errorCode)] \(error.description)") } return error } @discardableResult fileprivate func securityError(status: OSStatus) -> Error { return type(of: self).securityError(status: status) } } struct Options { var itemClass: ItemClass = .genericPassword var service: String = "" var accessGroup: String? = nil var server: URL! var protocolType: ProtocolType! var authenticationType: AuthenticationType = .default var accessibility: Accessibility = .afterFirstUnlock var authenticationPolicy: AuthenticationPolicy? var synchronizable: Bool = false var label: String? var comment: String? var authenticationPrompt: String? var authenticationUI: AuthenticationUI? var authenticationContext: AnyObject? var attributes = [String: Any]() } /** Class Key Constant */ private let Class = String(kSecClass) /** Attribute Key Constants */ private let AttributeAccessible = String(kSecAttrAccessible) @available(iOS 8.0, OSX 10.10, *) private let AttributeAccessControl = String(kSecAttrAccessControl) private let AttributeAccessGroup = String(kSecAttrAccessGroup) private let AttributeSynchronizable = String(kSecAttrSynchronizable) private let AttributeCreationDate = String(kSecAttrCreationDate) private let AttributeModificationDate = String(kSecAttrModificationDate) private let AttributeDescription = String(kSecAttrDescription) private let AttributeComment = String(kSecAttrComment) private let AttributeCreator = String(kSecAttrCreator) private let AttributeType = String(kSecAttrType) private let AttributeLabel = String(kSecAttrLabel) private let AttributeIsInvisible = String(kSecAttrIsInvisible) private let AttributeIsNegative = String(kSecAttrIsNegative) private let AttributeAccount = String(kSecAttrAccount) private let AttributeService = String(kSecAttrService) private let AttributeGeneric = String(kSecAttrGeneric) private let AttributeSecurityDomain = String(kSecAttrSecurityDomain) private let AttributeServer = String(kSecAttrServer) private let AttributeProtocol = String(kSecAttrProtocol) private let AttributeAuthenticationType = String(kSecAttrAuthenticationType) private let AttributePort = String(kSecAttrPort) private let AttributePath = String(kSecAttrPath) private let SynchronizableAny = kSecAttrSynchronizableAny /** Search Constants */ private let MatchLimit = String(kSecMatchLimit) private let MatchLimitOne = kSecMatchLimitOne private let MatchLimitAll = kSecMatchLimitAll /** Return Type Key Constants */ private let ReturnData = String(kSecReturnData) private let ReturnAttributes = String(kSecReturnAttributes) private let ReturnRef = String(kSecReturnRef) private let ReturnPersistentRef = String(kSecReturnPersistentRef) /** Value Type Key Constants */ private let ValueData = String(kSecValueData) private let ValueRef = String(kSecValueRef) private let ValuePersistentRef = String(kSecValuePersistentRef) /** Other Constants */ @available(iOS 8.0, OSX 10.10, tvOS 8.0, *) private let UseOperationPrompt = String(kSecUseOperationPrompt) @available(iOS, introduced: 8.0, deprecated: 9.0, message: "Use a UseAuthenticationUI instead.") @available(OSX, introduced: 10.10, deprecated: 10.11, message: "Use UseAuthenticationUI instead.") @available(watchOS, introduced: 2.0, deprecated: 2.0, message: "Use UseAuthenticationUI instead.") @available(tvOS, introduced: 8.0, deprecated: 9.0, message: "Use UseAuthenticationUI instead.") private let UseNoAuthenticationUI = String(kSecUseNoAuthenticationUI) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUI = String(kSecUseAuthenticationUI) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationContext = String(kSecUseAuthenticationContext) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIAllow = String(kSecUseAuthenticationUIAllow) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUIFail = String(kSecUseAuthenticationUIFail) @available(iOS 9.0, OSX 10.11, watchOS 2.0, tvOS 9.0, *) private let UseAuthenticationUISkip = String(kSecUseAuthenticationUISkip) #if os(iOS) && !targetEnvironment(macCatalyst) /** Credential Key Constants */ private let SharedPassword = String(kSecSharedPassword) #endif extension Keychain: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { let items = allItems() if items.isEmpty { return "[]" } var description = "[\n" for item in items { description += " " description += "\(item)\n" } description += "]" return description } public var debugDescription: String { return "\(items())" } } extension Options { func query(ignoringAttributeSynchronizable: Bool = true) -> [String: Any] { var query = [String: Any]() query[Class] = itemClass.rawValue if let accessGroup = self.accessGroup { query[AttributeAccessGroup] = accessGroup } if ignoringAttributeSynchronizable { query[AttributeSynchronizable] = SynchronizableAny } else { query[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse } switch itemClass { case .genericPassword: query[AttributeService] = service case .internetPassword: query[AttributeServer] = server.host query[AttributePort] = server.port query[AttributeProtocol] = protocolType.rawValue query[AttributeAuthenticationType] = authenticationType.rawValue } if #available(OSX 10.10, *) { if authenticationPrompt != nil { query[UseOperationPrompt] = authenticationPrompt } } #if !os(watchOS) if #available(iOS 9.0, OSX 10.11, *) { if authenticationContext != nil { query[UseAuthenticationContext] = authenticationContext } } #endif return query } func attributes(key: String?, value: Data) -> ([String: Any], Error?) { var attributes: [String: Any] if key != nil { attributes = query() attributes[AttributeAccount] = key } else { attributes = [String: Any]() } attributes[ValueData] = value if label != nil { attributes[AttributeLabel] = label } if comment != nil { attributes[AttributeComment] = comment } if let policy = authenticationPolicy { if #available(OSX 10.10, *) { var error: Unmanaged? guard let accessControl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility.rawValue as CFTypeRef, SecAccessControlCreateFlags(rawValue: CFOptionFlags(policy.rawValue)), &error) else { if let error = error?.takeUnretainedValue() { return (attributes, error.error) } return (attributes, Status.unexpectedError) } attributes[AttributeAccessControl] = accessControl } else { print("Unavailable 'Touch ID integration' on OS X versions prior to 10.10.") } } else { attributes[AttributeAccessible] = accessibility.rawValue } attributes[AttributeSynchronizable] = synchronizable ? kCFBooleanTrue : kCFBooleanFalse return (attributes, nil) } } // MARK: extension Attributes: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { return "\(attributes)" } public var debugDescription: String { return description } } extension ItemClass: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecClassGenericPassword): self = .genericPassword case String(kSecClassInternetPassword): self = .internetPassword default: return nil } } public var rawValue: String { switch self { case .genericPassword: return String(kSecClassGenericPassword) case .internetPassword: return String(kSecClassInternetPassword) } } public var description: String { switch self { case .genericPassword: return "GenericPassword" case .internetPassword: return "InternetPassword" } } } extension ProtocolType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrProtocolFTP): self = .ftp case String(kSecAttrProtocolFTPAccount): self = .ftpAccount case String(kSecAttrProtocolHTTP): self = .http case String(kSecAttrProtocolIRC): self = .irc case String(kSecAttrProtocolNNTP): self = .nntp case String(kSecAttrProtocolPOP3): self = .pop3 case String(kSecAttrProtocolSMTP): self = .smtp case String(kSecAttrProtocolSOCKS): self = .socks case String(kSecAttrProtocolIMAP): self = .imap case String(kSecAttrProtocolLDAP): self = .ldap case String(kSecAttrProtocolAppleTalk): self = .appleTalk case String(kSecAttrProtocolAFP): self = .afp case String(kSecAttrProtocolTelnet): self = .telnet case String(kSecAttrProtocolSSH): self = .ssh case String(kSecAttrProtocolFTPS): self = .ftps case String(kSecAttrProtocolHTTPS): self = .https case String(kSecAttrProtocolHTTPProxy): self = .httpProxy case String(kSecAttrProtocolHTTPSProxy): self = .httpsProxy case String(kSecAttrProtocolFTPProxy): self = .ftpProxy case String(kSecAttrProtocolSMB): self = .smb case String(kSecAttrProtocolRTSP): self = .rtsp case String(kSecAttrProtocolRTSPProxy): self = .rtspProxy case String(kSecAttrProtocolDAAP): self = .daap case String(kSecAttrProtocolEPPC): self = .eppc case String(kSecAttrProtocolIPP): self = .ipp case String(kSecAttrProtocolNNTPS): self = .nntps case String(kSecAttrProtocolLDAPS): self = .ldaps case String(kSecAttrProtocolTelnetS): self = .telnetS case String(kSecAttrProtocolIMAPS): self = .imaps case String(kSecAttrProtocolIRCS): self = .ircs case String(kSecAttrProtocolPOP3S): self = .pop3S default: return nil } } public var rawValue: String { switch self { case .ftp: return String(kSecAttrProtocolFTP) case .ftpAccount: return String(kSecAttrProtocolFTPAccount) case .http: return String(kSecAttrProtocolHTTP) case .irc: return String(kSecAttrProtocolIRC) case .nntp: return String(kSecAttrProtocolNNTP) case .pop3: return String(kSecAttrProtocolPOP3) case .smtp: return String(kSecAttrProtocolSMTP) case .socks: return String(kSecAttrProtocolSOCKS) case .imap: return String(kSecAttrProtocolIMAP) case .ldap: return String(kSecAttrProtocolLDAP) case .appleTalk: return String(kSecAttrProtocolAppleTalk) case .afp: return String(kSecAttrProtocolAFP) case .telnet: return String(kSecAttrProtocolTelnet) case .ssh: return String(kSecAttrProtocolSSH) case .ftps: return String(kSecAttrProtocolFTPS) case .https: return String(kSecAttrProtocolHTTPS) case .httpProxy: return String(kSecAttrProtocolHTTPProxy) case .httpsProxy: return String(kSecAttrProtocolHTTPSProxy) case .ftpProxy: return String(kSecAttrProtocolFTPProxy) case .smb: return String(kSecAttrProtocolSMB) case .rtsp: return String(kSecAttrProtocolRTSP) case .rtspProxy: return String(kSecAttrProtocolRTSPProxy) case .daap: return String(kSecAttrProtocolDAAP) case .eppc: return String(kSecAttrProtocolEPPC) case .ipp: return String(kSecAttrProtocolIPP) case .nntps: return String(kSecAttrProtocolNNTPS) case .ldaps: return String(kSecAttrProtocolLDAPS) case .telnetS: return String(kSecAttrProtocolTelnetS) case .imaps: return String(kSecAttrProtocolIMAPS) case .ircs: return String(kSecAttrProtocolIRCS) case .pop3S: return String(kSecAttrProtocolPOP3S) } } public var description: String { switch self { case .ftp: return "FTP" case .ftpAccount: return "FTPAccount" case .http: return "HTTP" case .irc: return "IRC" case .nntp: return "NNTP" case .pop3: return "POP3" case .smtp: return "SMTP" case .socks: return "SOCKS" case .imap: return "IMAP" case .ldap: return "LDAP" case .appleTalk: return "AppleTalk" case .afp: return "AFP" case .telnet: return "Telnet" case .ssh: return "SSH" case .ftps: return "FTPS" case .https: return "HTTPS" case .httpProxy: return "HTTPProxy" case .httpsProxy: return "HTTPSProxy" case .ftpProxy: return "FTPProxy" case .smb: return "SMB" case .rtsp: return "RTSP" case .rtspProxy: return "RTSPProxy" case .daap: return "DAAP" case .eppc: return "EPPC" case .ipp: return "IPP" case .nntps: return "NNTPS" case .ldaps: return "LDAPS" case .telnetS: return "TelnetS" case .imaps: return "IMAPS" case .ircs: return "IRCS" case .pop3S: return "POP3S" } } } extension AuthenticationType: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { switch rawValue { case String(kSecAttrAuthenticationTypeNTLM): self = .ntlm case String(kSecAttrAuthenticationTypeMSN): self = .msn case String(kSecAttrAuthenticationTypeDPA): self = .dpa case String(kSecAttrAuthenticationTypeRPA): self = .rpa case String(kSecAttrAuthenticationTypeHTTPBasic): self = .httpBasic case String(kSecAttrAuthenticationTypeHTTPDigest): self = .httpDigest case String(kSecAttrAuthenticationTypeHTMLForm): self = .htmlForm case String(kSecAttrAuthenticationTypeDefault): self = .`default` default: return nil } } public var rawValue: String { switch self { case .ntlm: return String(kSecAttrAuthenticationTypeNTLM) case .msn: return String(kSecAttrAuthenticationTypeMSN) case .dpa: return String(kSecAttrAuthenticationTypeDPA) case .rpa: return String(kSecAttrAuthenticationTypeRPA) case .httpBasic: return String(kSecAttrAuthenticationTypeHTTPBasic) case .httpDigest: return String(kSecAttrAuthenticationTypeHTTPDigest) case .htmlForm: return String(kSecAttrAuthenticationTypeHTMLForm) case .`default`: return String(kSecAttrAuthenticationTypeDefault) } } public var description: String { switch self { case .ntlm: return "NTLM" case .msn: return "MSN" case .dpa: return "DPA" case .rpa: return "RPA" case .httpBasic: return "HTTPBasic" case .httpDigest: return "HTTPDigest" case .htmlForm: return "HTMLForm" case .`default`: return "Default" } } } extension Accessibility: RawRepresentable, CustomStringConvertible { public init?(rawValue: String) { if #available(OSX 10.10, *) { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly): self = .whenPasscodeSetThisDeviceOnly case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } else { switch rawValue { case String(kSecAttrAccessibleWhenUnlocked): self = .whenUnlocked case String(kSecAttrAccessibleAfterFirstUnlock): self = .afterFirstUnlock #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlways): self = .always #endif case String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly): self = .whenUnlockedThisDeviceOnly case String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly): self = .afterFirstUnlockThisDeviceOnly #if !targetEnvironment(macCatalyst) case String(kSecAttrAccessibleAlwaysThisDeviceOnly): self = .alwaysThisDeviceOnly #endif default: return nil } } } public var rawValue: String { switch self { case .whenUnlocked: return String(kSecAttrAccessibleWhenUnlocked) case .afterFirstUnlock: return String(kSecAttrAccessibleAfterFirstUnlock) #if !targetEnvironment(macCatalyst) case .always: return String(kSecAttrAccessibleAlways) #endif case .whenPasscodeSetThisDeviceOnly: if #available(OSX 10.10, *) { return String(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) } else { fatalError("'Accessibility.WhenPasscodeSetThisDeviceOnly' is not available on this version of OS.") } case .whenUnlockedThisDeviceOnly: return String(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) case .afterFirstUnlockThisDeviceOnly: return String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return String(kSecAttrAccessibleAlwaysThisDeviceOnly) #endif } } public var description: String { switch self { case .whenUnlocked: return "WhenUnlocked" case .afterFirstUnlock: return "AfterFirstUnlock" #if !targetEnvironment(macCatalyst) case .always: return "Always" #endif case .whenPasscodeSetThisDeviceOnly: return "WhenPasscodeSetThisDeviceOnly" case .whenUnlockedThisDeviceOnly: return "WhenUnlockedThisDeviceOnly" case .afterFirstUnlockThisDeviceOnly: return "AfterFirstUnlockThisDeviceOnly" #if !targetEnvironment(macCatalyst) case .alwaysThisDeviceOnly: return "AlwaysThisDeviceOnly" #endif } } } extension CFError { var error: NSError { let domain = CFErrorGetDomain(self) as String let code = CFErrorGetCode(self) let userInfo = CFErrorCopyUserInfo(self) as! [String: Any] return NSError(domain: domain, code: code, userInfo: userInfo) } } public enum Status: OSStatus, Error { case success = 0 case unimplemented = -4 case diskFull = -34 case io = -36 case opWr = -49 case param = -50 case wrPerm = -61 case allocate = -108 case userCanceled = -128 case badReq = -909 case internalComponent = -2070 case notAvailable = -25291 case readOnly = -25292 case authFailed = -25293 case noSuchKeychain = -25294 case invalidKeychain = -25295 case duplicateKeychain = -25296 case duplicateCallback = -25297 case invalidCallback = -25298 case duplicateItem = -25299 case itemNotFound = -25300 case bufferTooSmall = -25301 case dataTooLarge = -25302 case noSuchAttr = -25303 case invalidItemRef = -25304 case invalidSearchRef = -25305 case noSuchClass = -25306 case noDefaultKeychain = -25307 case interactionNotAllowed = -25308 case readOnlyAttr = -25309 case wrongSecVersion = -25310 case keySizeNotAllowed = -25311 case noStorageModule = -25312 case noCertificateModule = -25313 case noPolicyModule = -25314 case interactionRequired = -25315 case dataNotAvailable = -25316 case dataNotModifiable = -25317 case createChainFailed = -25318 case invalidPrefsDomain = -25319 case inDarkWake = -25320 case aclNotSimple = -25240 case policyNotFound = -25241 case invalidTrustSetting = -25242 case noAccessForItem = -25243 case invalidOwnerEdit = -25244 case trustNotAvailable = -25245 case unsupportedFormat = -25256 case unknownFormat = -25257 case keyIsSensitive = -25258 case multiplePrivKeys = -25259 case passphraseRequired = -25260 case invalidPasswordRef = -25261 case invalidTrustSettings = -25262 case noTrustSettings = -25263 case pkcs12VerifyFailure = -25264 case invalidCertificate = -26265 case notSigner = -26267 case policyDenied = -26270 case invalidKey = -26274 case decode = -26275 case `internal` = -26276 case unsupportedAlgorithm = -26268 case unsupportedOperation = -26271 case unsupportedPadding = -26273 case itemInvalidKey = -34000 case itemInvalidKeyType = -34001 case itemInvalidValue = -34002 case itemClassMissing = -34003 case itemMatchUnsupported = -34004 case useItemListUnsupported = -34005 case useKeychainUnsupported = -34006 case useKeychainListUnsupported = -34007 case returnDataUnsupported = -34008 case returnAttributesUnsupported = -34009 case returnRefUnsupported = -34010 case returnPersitentRefUnsupported = -34011 case valueRefUnsupported = -34012 case valuePersistentRefUnsupported = -34013 case returnMissingPointer = -34014 case matchLimitUnsupported = -34015 case itemIllegalQuery = -34016 case waitForCallback = -34017 case missingEntitlement = -34018 case upgradePending = -34019 case mpSignatureInvalid = -25327 case otrTooOld = -25328 case otrIDTooNew = -25329 case serviceNotAvailable = -67585 case insufficientClientID = -67586 case deviceReset = -67587 case deviceFailed = -67588 case appleAddAppACLSubject = -67589 case applePublicKeyIncomplete = -67590 case appleSignatureMismatch = -67591 case appleInvalidKeyStartDate = -67592 case appleInvalidKeyEndDate = -67593 case conversionError = -67594 case appleSSLv2Rollback = -67595 case quotaExceeded = -67596 case fileTooBig = -67597 case invalidDatabaseBlob = -67598 case invalidKeyBlob = -67599 case incompatibleDatabaseBlob = -67600 case incompatibleKeyBlob = -67601 case hostNameMismatch = -67602 case unknownCriticalExtensionFlag = -67603 case noBasicConstraints = -67604 case noBasicConstraintsCA = -67605 case invalidAuthorityKeyID = -67606 case invalidSubjectKeyID = -67607 case invalidKeyUsageForPolicy = -67608 case invalidExtendedKeyUsage = -67609 case invalidIDLinkage = -67610 case pathLengthConstraintExceeded = -67611 case invalidRoot = -67612 case crlExpired = -67613 case crlNotValidYet = -67614 case crlNotFound = -67615 case crlServerDown = -67616 case crlBadURI = -67617 case unknownCertExtension = -67618 case unknownCRLExtension = -67619 case crlNotTrusted = -67620 case crlPolicyFailed = -67621 case idpFailure = -67622 case smimeEmailAddressesNotFound = -67623 case smimeBadExtendedKeyUsage = -67624 case smimeBadKeyUsage = -67625 case smimeKeyUsageNotCritical = -67626 case smimeNoEmailAddress = -67627 case smimeSubjAltNameNotCritical = -67628 case sslBadExtendedKeyUsage = -67629 case ocspBadResponse = -67630 case ocspBadRequest = -67631 case ocspUnavailable = -67632 case ocspStatusUnrecognized = -67633 case endOfData = -67634 case incompleteCertRevocationCheck = -67635 case networkFailure = -67636 case ocspNotTrustedToAnchor = -67637 case recordModified = -67638 case ocspSignatureError = -67639 case ocspNoSigner = -67640 case ocspResponderMalformedReq = -67641 case ocspResponderInternalError = -67642 case ocspResponderTryLater = -67643 case ocspResponderSignatureRequired = -67644 case ocspResponderUnauthorized = -67645 case ocspResponseNonceMismatch = -67646 case codeSigningBadCertChainLength = -67647 case codeSigningNoBasicConstraints = -67648 case codeSigningBadPathLengthConstraint = -67649 case codeSigningNoExtendedKeyUsage = -67650 case codeSigningDevelopment = -67651 case resourceSignBadCertChainLength = -67652 case resourceSignBadExtKeyUsage = -67653 case trustSettingDeny = -67654 case invalidSubjectName = -67655 case unknownQualifiedCertStatement = -67656 case mobileMeRequestQueued = -67657 case mobileMeRequestRedirected = -67658 case mobileMeServerError = -67659 case mobileMeServerNotAvailable = -67660 case mobileMeServerAlreadyExists = -67661 case mobileMeServerServiceErr = -67662 case mobileMeRequestAlreadyPending = -67663 case mobileMeNoRequestPending = -67664 case mobileMeCSRVerifyFailure = -67665 case mobileMeFailedConsistencyCheck = -67666 case notInitialized = -67667 case invalidHandleUsage = -67668 case pvcReferentNotFound = -67669 case functionIntegrityFail = -67670 case internalError = -67671 case memoryError = -67672 case invalidData = -67673 case mdsError = -67674 case invalidPointer = -67675 case selfCheckFailed = -67676 case functionFailed = -67677 case moduleManifestVerifyFailed = -67678 case invalidGUID = -67679 case invalidHandle = -67680 case invalidDBList = -67681 case invalidPassthroughID = -67682 case invalidNetworkAddress = -67683 case crlAlreadySigned = -67684 case invalidNumberOfFields = -67685 case verificationFailure = -67686 case unknownTag = -67687 case invalidSignature = -67688 case invalidName = -67689 case invalidCertificateRef = -67690 case invalidCertificateGroup = -67691 case tagNotFound = -67692 case invalidQuery = -67693 case invalidValue = -67694 case callbackFailed = -67695 case aclDeleteFailed = -67696 case aclReplaceFailed = -67697 case aclAddFailed = -67698 case aclChangeFailed = -67699 case invalidAccessCredentials = -67700 case invalidRecord = -67701 case invalidACL = -67702 case invalidSampleValue = -67703 case incompatibleVersion = -67704 case privilegeNotGranted = -67705 case invalidScope = -67706 case pvcAlreadyConfigured = -67707 case invalidPVC = -67708 case emmLoadFailed = -67709 case emmUnloadFailed = -67710 case addinLoadFailed = -67711 case invalidKeyRef = -67712 case invalidKeyHierarchy = -67713 case addinUnloadFailed = -67714 case libraryReferenceNotFound = -67715 case invalidAddinFunctionTable = -67716 case invalidServiceMask = -67717 case moduleNotLoaded = -67718 case invalidSubServiceID = -67719 case attributeNotInContext = -67720 case moduleManagerInitializeFailed = -67721 case moduleManagerNotFound = -67722 case eventNotificationCallbackNotFound = -67723 case inputLengthError = -67724 case outputLengthError = -67725 case privilegeNotSupported = -67726 case deviceError = -67727 case attachHandleBusy = -67728 case notLoggedIn = -67729 case algorithmMismatch = -67730 case keyUsageIncorrect = -67731 case keyBlobTypeIncorrect = -67732 case keyHeaderInconsistent = -67733 case unsupportedKeyFormat = -67734 case unsupportedKeySize = -67735 case invalidKeyUsageMask = -67736 case unsupportedKeyUsageMask = -67737 case invalidKeyAttributeMask = -67738 case unsupportedKeyAttributeMask = -67739 case invalidKeyLabel = -67740 case unsupportedKeyLabel = -67741 case invalidKeyFormat = -67742 case unsupportedVectorOfBuffers = -67743 case invalidInputVector = -67744 case invalidOutputVector = -67745 case invalidContext = -67746 case invalidAlgorithm = -67747 case invalidAttributeKey = -67748 case missingAttributeKey = -67749 case invalidAttributeInitVector = -67750 case missingAttributeInitVector = -67751 case invalidAttributeSalt = -67752 case missingAttributeSalt = -67753 case invalidAttributePadding = -67754 case missingAttributePadding = -67755 case invalidAttributeRandom = -67756 case missingAttributeRandom = -67757 case invalidAttributeSeed = -67758 case missingAttributeSeed = -67759 case invalidAttributePassphrase = -67760 case missingAttributePassphrase = -67761 case invalidAttributeKeyLength = -67762 case missingAttributeKeyLength = -67763 case invalidAttributeBlockSize = -67764 case missingAttributeBlockSize = -67765 case invalidAttributeOutputSize = -67766 case missingAttributeOutputSize = -67767 case invalidAttributeRounds = -67768 case missingAttributeRounds = -67769 case invalidAlgorithmParms = -67770 case missingAlgorithmParms = -67771 case invalidAttributeLabel = -67772 case missingAttributeLabel = -67773 case invalidAttributeKeyType = -67774 case missingAttributeKeyType = -67775 case invalidAttributeMode = -67776 case missingAttributeMode = -67777 case invalidAttributeEffectiveBits = -67778 case missingAttributeEffectiveBits = -67779 case invalidAttributeStartDate = -67780 case missingAttributeStartDate = -67781 case invalidAttributeEndDate = -67782 case missingAttributeEndDate = -67783 case invalidAttributeVersion = -67784 case missingAttributeVersion = -67785 case invalidAttributePrime = -67786 case missingAttributePrime = -67787 case invalidAttributeBase = -67788 case missingAttributeBase = -67789 case invalidAttributeSubprime = -67790 case missingAttributeSubprime = -67791 case invalidAttributeIterationCount = -67792 case missingAttributeIterationCount = -67793 case invalidAttributeDLDBHandle = -67794 case missingAttributeDLDBHandle = -67795 case invalidAttributeAccessCredentials = -67796 case missingAttributeAccessCredentials = -67797 case invalidAttributePublicKeyFormat = -67798 case missingAttributePublicKeyFormat = -67799 case invalidAttributePrivateKeyFormat = -67800 case missingAttributePrivateKeyFormat = -67801 case invalidAttributeSymmetricKeyFormat = -67802 case missingAttributeSymmetricKeyFormat = -67803 case invalidAttributeWrappedKeyFormat = -67804 case missingAttributeWrappedKeyFormat = -67805 case stagedOperationInProgress = -67806 case stagedOperationNotStarted = -67807 case verifyFailed = -67808 case querySizeUnknown = -67809 case blockSizeMismatch = -67810 case publicKeyInconsistent = -67811 case deviceVerifyFailed = -67812 case invalidLoginName = -67813 case alreadyLoggedIn = -67814 case invalidDigestAlgorithm = -67815 case invalidCRLGroup = -67816 case certificateCannotOperate = -67817 case certificateExpired = -67818 case certificateNotValidYet = -67819 case certificateRevoked = -67820 case certificateSuspended = -67821 case insufficientCredentials = -67822 case invalidAction = -67823 case invalidAuthority = -67824 case verifyActionFailed = -67825 case invalidCertAuthority = -67826 case invaldCRLAuthority = -67827 case invalidCRLEncoding = -67828 case invalidCRLType = -67829 case invalidCRL = -67830 case invalidFormType = -67831 case invalidID = -67832 case invalidIdentifier = -67833 case invalidIndex = -67834 case invalidPolicyIdentifiers = -67835 case invalidTimeString = -67836 case invalidReason = -67837 case invalidRequestInputs = -67838 case invalidResponseVector = -67839 case invalidStopOnPolicy = -67840 case invalidTuple = -67841 case multipleValuesUnsupported = -67842 case notTrusted = -67843 case noDefaultAuthority = -67844 case rejectedForm = -67845 case requestLost = -67846 case requestRejected = -67847 case unsupportedAddressType = -67848 case unsupportedService = -67849 case invalidTupleGroup = -67850 case invalidBaseACLs = -67851 case invalidTupleCredendtials = -67852 case invalidEncoding = -67853 case invalidValidityPeriod = -67854 case invalidRequestor = -67855 case requestDescriptor = -67856 case invalidBundleInfo = -67857 case invalidCRLIndex = -67858 case noFieldValues = -67859 case unsupportedFieldFormat = -67860 case unsupportedIndexInfo = -67861 case unsupportedLocality = -67862 case unsupportedNumAttributes = -67863 case unsupportedNumIndexes = -67864 case unsupportedNumRecordTypes = -67865 case fieldSpecifiedMultiple = -67866 case incompatibleFieldFormat = -67867 case invalidParsingModule = -67868 case databaseLocked = -67869 case datastoreIsOpen = -67870 case missingValue = -67871 case unsupportedQueryLimits = -67872 case unsupportedNumSelectionPreds = -67873 case unsupportedOperator = -67874 case invalidDBLocation = -67875 case invalidAccessRequest = -67876 case invalidIndexInfo = -67877 case invalidNewOwner = -67878 case invalidModifyMode = -67879 case missingRequiredExtension = -67880 case extendedKeyUsageNotCritical = -67881 case timestampMissing = -67882 case timestampInvalid = -67883 case timestampNotTrusted = -67884 case timestampServiceNotAvailable = -67885 case timestampBadAlg = -67886 case timestampBadRequest = -67887 case timestampBadDataFormat = -67888 case timestampTimeNotAvailable = -67889 case timestampUnacceptedPolicy = -67890 case timestampUnacceptedExtension = -67891 case timestampAddInfoNotAvailable = -67892 case timestampSystemFailure = -67893 case signingTimeMissing = -67894 case timestampRejection = -67895 case timestampWaiting = -67896 case timestampRevocationWarning = -67897 case timestampRevocationNotification = -67898 case unexpectedError = -99999 } extension Status: RawRepresentable, CustomStringConvertible { public init(status: OSStatus) { if let mappedStatus = Status(rawValue: status) { self = mappedStatus } else { self = .unexpectedError } } public var description: String { switch self { case .success: return "No error." case .unimplemented: return "Function or operation not implemented." case .diskFull: return "The disk is full." case .io: return "I/O error (bummers)" case .opWr: return "file already open with with write permission" case .param: return "One or more parameters passed to a function were not valid." case .wrPerm: return "write permissions error" case .allocate: return "Failed to allocate memory." case .userCanceled: return "User canceled the operation." case .badReq: return "Bad parameter or invalid state for operation." case .internalComponent: return "" case .notAvailable: return "No keychain is available. You may need to restart your computer." case .readOnly: return "This keychain cannot be modified." case .authFailed: return "The user name or passphrase you entered is not correct." case .noSuchKeychain: return "The specified keychain could not be found." case .invalidKeychain: return "The specified keychain is not a valid keychain file." case .duplicateKeychain: return "A keychain with the same name already exists." case .duplicateCallback: return "The specified callback function is already installed." case .invalidCallback: return "The specified callback function is not valid." case .duplicateItem: return "The specified item already exists in the keychain." case .itemNotFound: return "The specified item could not be found in the keychain." case .bufferTooSmall: return "There is not enough memory available to use the specified item." case .dataTooLarge: return "This item contains information which is too large or in a format that cannot be displayed." case .noSuchAttr: return "The specified attribute does not exist." case .invalidItemRef: return "The specified item is no longer valid. It may have been deleted from the keychain." case .invalidSearchRef: return "Unable to search the current keychain." case .noSuchClass: return "The specified item does not appear to be a valid keychain item." case .noDefaultKeychain: return "A default keychain could not be found." case .interactionNotAllowed: return "User interaction is not allowed." case .readOnlyAttr: return "The specified attribute could not be modified." case .wrongSecVersion: return "This keychain was created by a different version of the system software and cannot be opened." case .keySizeNotAllowed: return "This item specifies a key size which is too large." case .noStorageModule: return "A required component (data storage module) could not be loaded. You may need to restart your computer." case .noCertificateModule: return "A required component (certificate module) could not be loaded. You may need to restart your computer." case .noPolicyModule: return "A required component (policy module) could not be loaded. You may need to restart your computer." case .interactionRequired: return "User interaction is required, but is currently not allowed." case .dataNotAvailable: return "The contents of this item cannot be retrieved." case .dataNotModifiable: return "The contents of this item cannot be modified." case .createChainFailed: return "One or more certificates required to validate this certificate cannot be found." case .invalidPrefsDomain: return "The specified preferences domain is not valid." case .inDarkWake: return "In dark wake, no UI possible" case .aclNotSimple: return "The specified access control list is not in standard (simple) form." case .policyNotFound: return "The specified policy cannot be found." case .invalidTrustSetting: return "The specified trust setting is invalid." case .noAccessForItem: return "The specified item has no access control." case .invalidOwnerEdit: return "Invalid attempt to change the owner of this item." case .trustNotAvailable: return "No trust results are available." case .unsupportedFormat: return "Import/Export format unsupported." case .unknownFormat: return "Unknown format in import." case .keyIsSensitive: return "Key material must be wrapped for export." case .multiplePrivKeys: return "An attempt was made to import multiple private keys." case .passphraseRequired: return "Passphrase is required for import/export." case .invalidPasswordRef: return "The password reference was invalid." case .invalidTrustSettings: return "The Trust Settings Record was corrupted." case .noTrustSettings: return "No Trust Settings were found." case .pkcs12VerifyFailure: return "MAC verification failed during PKCS12 import (wrong password?)" case .invalidCertificate: return "This certificate could not be decoded." case .notSigner: return "A certificate was not signed by its proposed parent." case .policyDenied: return "The certificate chain was not trusted due to a policy not accepting it." case .invalidKey: return "The provided key material was not valid." case .decode: return "Unable to decode the provided data." case .`internal`: return "An internal error occurred in the Security framework." case .unsupportedAlgorithm: return "An unsupported algorithm was encountered." case .unsupportedOperation: return "The operation you requested is not supported by this key." case .unsupportedPadding: return "The padding you requested is not supported." case .itemInvalidKey: return "A string key in dictionary is not one of the supported keys." case .itemInvalidKeyType: return "A key in a dictionary is neither a CFStringRef nor a CFNumberRef." case .itemInvalidValue: return "A value in a dictionary is an invalid (or unsupported) CF type." case .itemClassMissing: return "No kSecItemClass key was specified in a dictionary." case .itemMatchUnsupported: return "The caller passed one or more kSecMatch keys to a function which does not support matches." case .useItemListUnsupported: return "The caller passed in a kSecUseItemList key to a function which does not support it." case .useKeychainUnsupported: return "The caller passed in a kSecUseKeychain key to a function which does not support it." case .useKeychainListUnsupported: return "The caller passed in a kSecUseKeychainList key to a function which does not support it." case .returnDataUnsupported: return "The caller passed in a kSecReturnData key to a function which does not support it." case .returnAttributesUnsupported: return "The caller passed in a kSecReturnAttributes key to a function which does not support it." case .returnRefUnsupported: return "The caller passed in a kSecReturnRef key to a function which does not support it." case .returnPersitentRefUnsupported: return "The caller passed in a kSecReturnPersistentRef key to a function which does not support it." case .valueRefUnsupported: return "The caller passed in a kSecValueRef key to a function which does not support it." case .valuePersistentRefUnsupported: return "The caller passed in a kSecValuePersistentRef key to a function which does not support it." case .returnMissingPointer: return "The caller passed asked for something to be returned but did not pass in a result pointer." case .matchLimitUnsupported: return "The caller passed in a kSecMatchLimit key to a call which does not support limits." case .itemIllegalQuery: return "The caller passed in a query which contained too many keys." case .waitForCallback: return "This operation is incomplete, until the callback is invoked (not an error)." case .missingEntitlement: return "Internal error when a required entitlement isn't present, client has neither application-identifier nor keychain-access-groups entitlements." case .upgradePending: return "Error returned if keychain database needs a schema migration but the device is locked, clients should wait for a device unlock notification and retry the command." case .mpSignatureInvalid: return "Signature invalid on MP message" case .otrTooOld: return "Message is too old to use" case .otrIDTooNew: return "Key ID is too new to use! Message from the future?" case .serviceNotAvailable: return "The required service is not available." case .insufficientClientID: return "The client ID is not correct." case .deviceReset: return "A device reset has occurred." case .deviceFailed: return "A device failure has occurred." case .appleAddAppACLSubject: return "Adding an application ACL subject failed." case .applePublicKeyIncomplete: return "The public key is incomplete." case .appleSignatureMismatch: return "A signature mismatch has occurred." case .appleInvalidKeyStartDate: return "The specified key has an invalid start date." case .appleInvalidKeyEndDate: return "The specified key has an invalid end date." case .conversionError: return "A conversion error has occurred." case .appleSSLv2Rollback: return "A SSLv2 rollback error has occurred." case .quotaExceeded: return "The quota was exceeded." case .fileTooBig: return "The file is too big." case .invalidDatabaseBlob: return "The specified database has an invalid blob." case .invalidKeyBlob: return "The specified database has an invalid key blob." case .incompatibleDatabaseBlob: return "The specified database has an incompatible blob." case .incompatibleKeyBlob: return "The specified database has an incompatible key blob." case .hostNameMismatch: return "A host name mismatch has occurred." case .unknownCriticalExtensionFlag: return "There is an unknown critical extension flag." case .noBasicConstraints: return "No basic constraints were found." case .noBasicConstraintsCA: return "No basic CA constraints were found." case .invalidAuthorityKeyID: return "The authority key ID is not valid." case .invalidSubjectKeyID: return "The subject key ID is not valid." case .invalidKeyUsageForPolicy: return "The key usage is not valid for the specified policy." case .invalidExtendedKeyUsage: return "The extended key usage is not valid." case .invalidIDLinkage: return "The ID linkage is not valid." case .pathLengthConstraintExceeded: return "The path length constraint was exceeded." case .invalidRoot: return "The root or anchor certificate is not valid." case .crlExpired: return "The CRL has expired." case .crlNotValidYet: return "The CRL is not yet valid." case .crlNotFound: return "The CRL was not found." case .crlServerDown: return "The CRL server is down." case .crlBadURI: return "The CRL has a bad Uniform Resource Identifier." case .unknownCertExtension: return "An unknown certificate extension was encountered." case .unknownCRLExtension: return "An unknown CRL extension was encountered." case .crlNotTrusted: return "The CRL is not trusted." case .crlPolicyFailed: return "The CRL policy failed." case .idpFailure: return "The issuing distribution point was not valid." case .smimeEmailAddressesNotFound: return "An email address mismatch was encountered." case .smimeBadExtendedKeyUsage: return "The appropriate extended key usage for SMIME was not found." case .smimeBadKeyUsage: return "The key usage is not compatible with SMIME." case .smimeKeyUsageNotCritical: return "The key usage extension is not marked as critical." case .smimeNoEmailAddress: return "No email address was found in the certificate." case .smimeSubjAltNameNotCritical: return "The subject alternative name extension is not marked as critical." case .sslBadExtendedKeyUsage: return "The appropriate extended key usage for SSL was not found." case .ocspBadResponse: return "The OCSP response was incorrect or could not be parsed." case .ocspBadRequest: return "The OCSP request was incorrect or could not be parsed." case .ocspUnavailable: return "OCSP service is unavailable." case .ocspStatusUnrecognized: return "The OCSP server did not recognize this certificate." case .endOfData: return "An end-of-data was detected." case .incompleteCertRevocationCheck: return "An incomplete certificate revocation check occurred." case .networkFailure: return "A network failure occurred." case .ocspNotTrustedToAnchor: return "The OCSP response was not trusted to a root or anchor certificate." case .recordModified: return "The record was modified." case .ocspSignatureError: return "The OCSP response had an invalid signature." case .ocspNoSigner: return "The OCSP response had no signer." case .ocspResponderMalformedReq: return "The OCSP responder was given a malformed request." case .ocspResponderInternalError: return "The OCSP responder encountered an internal error." case .ocspResponderTryLater: return "The OCSP responder is busy, try again later." case .ocspResponderSignatureRequired: return "The OCSP responder requires a signature." case .ocspResponderUnauthorized: return "The OCSP responder rejected this request as unauthorized." case .ocspResponseNonceMismatch: return "The OCSP response nonce did not match the request." case .codeSigningBadCertChainLength: return "Code signing encountered an incorrect certificate chain length." case .codeSigningNoBasicConstraints: return "Code signing found no basic constraints." case .codeSigningBadPathLengthConstraint: return "Code signing encountered an incorrect path length constraint." case .codeSigningNoExtendedKeyUsage: return "Code signing found no extended key usage." case .codeSigningDevelopment: return "Code signing indicated use of a development-only certificate." case .resourceSignBadCertChainLength: return "Resource signing has encountered an incorrect certificate chain length." case .resourceSignBadExtKeyUsage: return "Resource signing has encountered an error in the extended key usage." case .trustSettingDeny: return "The trust setting for this policy was set to Deny." case .invalidSubjectName: return "An invalid certificate subject name was encountered." case .unknownQualifiedCertStatement: return "An unknown qualified certificate statement was encountered." case .mobileMeRequestQueued: return "The MobileMe request will be sent during the next connection." case .mobileMeRequestRedirected: return "The MobileMe request was redirected." case .mobileMeServerError: return "A MobileMe server error occurred." case .mobileMeServerNotAvailable: return "The MobileMe server is not available." case .mobileMeServerAlreadyExists: return "The MobileMe server reported that the item already exists." case .mobileMeServerServiceErr: return "A MobileMe service error has occurred." case .mobileMeRequestAlreadyPending: return "A MobileMe request is already pending." case .mobileMeNoRequestPending: return "MobileMe has no request pending." case .mobileMeCSRVerifyFailure: return "A MobileMe CSR verification failure has occurred." case .mobileMeFailedConsistencyCheck: return "MobileMe has found a failed consistency check." case .notInitialized: return "A function was called without initializing CSSM." case .invalidHandleUsage: return "The CSSM handle does not match with the service type." case .pvcReferentNotFound: return "A reference to the calling module was not found in the list of authorized callers." case .functionIntegrityFail: return "A function address was not within the verified module." case .internalError: return "An internal error has occurred." case .memoryError: return "A memory error has occurred." case .invalidData: return "Invalid data was encountered." case .mdsError: return "A Module Directory Service error has occurred." case .invalidPointer: return "An invalid pointer was encountered." case .selfCheckFailed: return "Self-check has failed." case .functionFailed: return "A function has failed." case .moduleManifestVerifyFailed: return "A module manifest verification failure has occurred." case .invalidGUID: return "An invalid GUID was encountered." case .invalidHandle: return "An invalid handle was encountered." case .invalidDBList: return "An invalid DB list was encountered." case .invalidPassthroughID: return "An invalid passthrough ID was encountered." case .invalidNetworkAddress: return "An invalid network address was encountered." case .crlAlreadySigned: return "The certificate revocation list is already signed." case .invalidNumberOfFields: return "An invalid number of fields were encountered." case .verificationFailure: return "A verification failure occurred." case .unknownTag: return "An unknown tag was encountered." case .invalidSignature: return "An invalid signature was encountered." case .invalidName: return "An invalid name was encountered." case .invalidCertificateRef: return "An invalid certificate reference was encountered." case .invalidCertificateGroup: return "An invalid certificate group was encountered." case .tagNotFound: return "The specified tag was not found." case .invalidQuery: return "The specified query was not valid." case .invalidValue: return "An invalid value was detected." case .callbackFailed: return "A callback has failed." case .aclDeleteFailed: return "An ACL delete operation has failed." case .aclReplaceFailed: return "An ACL replace operation has failed." case .aclAddFailed: return "An ACL add operation has failed." case .aclChangeFailed: return "An ACL change operation has failed." case .invalidAccessCredentials: return "Invalid access credentials were encountered." case .invalidRecord: return "An invalid record was encountered." case .invalidACL: return "An invalid ACL was encountered." case .invalidSampleValue: return "An invalid sample value was encountered." case .incompatibleVersion: return "An incompatible version was encountered." case .privilegeNotGranted: return "The privilege was not granted." case .invalidScope: return "An invalid scope was encountered." case .pvcAlreadyConfigured: return "The PVC is already configured." case .invalidPVC: return "An invalid PVC was encountered." case .emmLoadFailed: return "The EMM load has failed." case .emmUnloadFailed: return "The EMM unload has failed." case .addinLoadFailed: return "The add-in load operation has failed." case .invalidKeyRef: return "An invalid key was encountered." case .invalidKeyHierarchy: return "An invalid key hierarchy was encountered." case .addinUnloadFailed: return "The add-in unload operation has failed." case .libraryReferenceNotFound: return "A library reference was not found." case .invalidAddinFunctionTable: return "An invalid add-in function table was encountered." case .invalidServiceMask: return "An invalid service mask was encountered." case .moduleNotLoaded: return "A module was not loaded." case .invalidSubServiceID: return "An invalid subservice ID was encountered." case .attributeNotInContext: return "An attribute was not in the context." case .moduleManagerInitializeFailed: return "A module failed to initialize." case .moduleManagerNotFound: return "A module was not found." case .eventNotificationCallbackNotFound: return "An event notification callback was not found." case .inputLengthError: return "An input length error was encountered." case .outputLengthError: return "An output length error was encountered." case .privilegeNotSupported: return "The privilege is not supported." case .deviceError: return "A device error was encountered." case .attachHandleBusy: return "The CSP handle was busy." case .notLoggedIn: return "You are not logged in." case .algorithmMismatch: return "An algorithm mismatch was encountered." case .keyUsageIncorrect: return "The key usage is incorrect." case .keyBlobTypeIncorrect: return "The key blob type is incorrect." case .keyHeaderInconsistent: return "The key header is inconsistent." case .unsupportedKeyFormat: return "The key header format is not supported." case .unsupportedKeySize: return "The key size is not supported." case .invalidKeyUsageMask: return "The key usage mask is not valid." case .unsupportedKeyUsageMask: return "The key usage mask is not supported." case .invalidKeyAttributeMask: return "The key attribute mask is not valid." case .unsupportedKeyAttributeMask: return "The key attribute mask is not supported." case .invalidKeyLabel: return "The key label is not valid." case .unsupportedKeyLabel: return "The key label is not supported." case .invalidKeyFormat: return "The key format is not valid." case .unsupportedVectorOfBuffers: return "The vector of buffers is not supported." case .invalidInputVector: return "The input vector is not valid." case .invalidOutputVector: return "The output vector is not valid." case .invalidContext: return "An invalid context was encountered." case .invalidAlgorithm: return "An invalid algorithm was encountered." case .invalidAttributeKey: return "A key attribute was not valid." case .missingAttributeKey: return "A key attribute was missing." case .invalidAttributeInitVector: return "An init vector attribute was not valid." case .missingAttributeInitVector: return "An init vector attribute was missing." case .invalidAttributeSalt: return "A salt attribute was not valid." case .missingAttributeSalt: return "A salt attribute was missing." case .invalidAttributePadding: return "A padding attribute was not valid." case .missingAttributePadding: return "A padding attribute was missing." case .invalidAttributeRandom: return "A random number attribute was not valid." case .missingAttributeRandom: return "A random number attribute was missing." case .invalidAttributeSeed: return "A seed attribute was not valid." case .missingAttributeSeed: return "A seed attribute was missing." case .invalidAttributePassphrase: return "A passphrase attribute was not valid." case .missingAttributePassphrase: return "A passphrase attribute was missing." case .invalidAttributeKeyLength: return "A key length attribute was not valid." case .missingAttributeKeyLength: return "A key length attribute was missing." case .invalidAttributeBlockSize: return "A block size attribute was not valid." case .missingAttributeBlockSize: return "A block size attribute was missing." case .invalidAttributeOutputSize: return "An output size attribute was not valid." case .missingAttributeOutputSize: return "An output size attribute was missing." case .invalidAttributeRounds: return "The number of rounds attribute was not valid." case .missingAttributeRounds: return "The number of rounds attribute was missing." case .invalidAlgorithmParms: return "An algorithm parameters attribute was not valid." case .missingAlgorithmParms: return "An algorithm parameters attribute was missing." case .invalidAttributeLabel: return "A label attribute was not valid." case .missingAttributeLabel: return "A label attribute was missing." case .invalidAttributeKeyType: return "A key type attribute was not valid." case .missingAttributeKeyType: return "A key type attribute was missing." case .invalidAttributeMode: return "A mode attribute was not valid." case .missingAttributeMode: return "A mode attribute was missing." case .invalidAttributeEffectiveBits: return "An effective bits attribute was not valid." case .missingAttributeEffectiveBits: return "An effective bits attribute was missing." case .invalidAttributeStartDate: return "A start date attribute was not valid." case .missingAttributeStartDate: return "A start date attribute was missing." case .invalidAttributeEndDate: return "An end date attribute was not valid." case .missingAttributeEndDate: return "An end date attribute was missing." case .invalidAttributeVersion: return "A version attribute was not valid." case .missingAttributeVersion: return "A version attribute was missing." case .invalidAttributePrime: return "A prime attribute was not valid." case .missingAttributePrime: return "A prime attribute was missing." case .invalidAttributeBase: return "A base attribute was not valid." case .missingAttributeBase: return "A base attribute was missing." case .invalidAttributeSubprime: return "A subprime attribute was not valid." case .missingAttributeSubprime: return "A subprime attribute was missing." case .invalidAttributeIterationCount: return "An iteration count attribute was not valid." case .missingAttributeIterationCount: return "An iteration count attribute was missing." case .invalidAttributeDLDBHandle: return "A database handle attribute was not valid." case .missingAttributeDLDBHandle: return "A database handle attribute was missing." case .invalidAttributeAccessCredentials: return "An access credentials attribute was not valid." case .missingAttributeAccessCredentials: return "An access credentials attribute was missing." case .invalidAttributePublicKeyFormat: return "A public key format attribute was not valid." case .missingAttributePublicKeyFormat: return "A public key format attribute was missing." case .invalidAttributePrivateKeyFormat: return "A private key format attribute was not valid." case .missingAttributePrivateKeyFormat: return "A private key format attribute was missing." case .invalidAttributeSymmetricKeyFormat: return "A symmetric key format attribute was not valid." case .missingAttributeSymmetricKeyFormat: return "A symmetric key format attribute was missing." case .invalidAttributeWrappedKeyFormat: return "A wrapped key format attribute was not valid." case .missingAttributeWrappedKeyFormat: return "A wrapped key format attribute was missing." case .stagedOperationInProgress: return "A staged operation is in progress." case .stagedOperationNotStarted: return "A staged operation was not started." case .verifyFailed: return "A cryptographic verification failure has occurred." case .querySizeUnknown: return "The query size is unknown." case .blockSizeMismatch: return "A block size mismatch occurred." case .publicKeyInconsistent: return "The public key was inconsistent." case .deviceVerifyFailed: return "A device verification failure has occurred." case .invalidLoginName: return "An invalid login name was detected." case .alreadyLoggedIn: return "The user is already logged in." case .invalidDigestAlgorithm: return "An invalid digest algorithm was detected." case .invalidCRLGroup: return "An invalid CRL group was detected." case .certificateCannotOperate: return "The certificate cannot operate." case .certificateExpired: return "An expired certificate was detected." case .certificateNotValidYet: return "The certificate is not yet valid." case .certificateRevoked: return "The certificate was revoked." case .certificateSuspended: return "The certificate was suspended." case .insufficientCredentials: return "Insufficient credentials were detected." case .invalidAction: return "The action was not valid." case .invalidAuthority: return "The authority was not valid." case .verifyActionFailed: return "A verify action has failed." case .invalidCertAuthority: return "The certificate authority was not valid." case .invaldCRLAuthority: return "The CRL authority was not valid." case .invalidCRLEncoding: return "The CRL encoding was not valid." case .invalidCRLType: return "The CRL type was not valid." case .invalidCRL: return "The CRL was not valid." case .invalidFormType: return "The form type was not valid." case .invalidID: return "The ID was not valid." case .invalidIdentifier: return "The identifier was not valid." case .invalidIndex: return "The index was not valid." case .invalidPolicyIdentifiers: return "The policy identifiers are not valid." case .invalidTimeString: return "The time specified was not valid." case .invalidReason: return "The trust policy reason was not valid." case .invalidRequestInputs: return "The request inputs are not valid." case .invalidResponseVector: return "The response vector was not valid." case .invalidStopOnPolicy: return "The stop-on policy was not valid." case .invalidTuple: return "The tuple was not valid." case .multipleValuesUnsupported: return "Multiple values are not supported." case .notTrusted: return "The trust policy was not trusted." case .noDefaultAuthority: return "No default authority was detected." case .rejectedForm: return "The trust policy had a rejected form." case .requestLost: return "The request was lost." case .requestRejected: return "The request was rejected." case .unsupportedAddressType: return "The address type is not supported." case .unsupportedService: return "The service is not supported." case .invalidTupleGroup: return "The tuple group was not valid." case .invalidBaseACLs: return "The base ACLs are not valid." case .invalidTupleCredendtials: return "The tuple credentials are not valid." case .invalidEncoding: return "The encoding was not valid." case .invalidValidityPeriod: return "The validity period was not valid." case .invalidRequestor: return "The requestor was not valid." case .requestDescriptor: return "The request descriptor was not valid." case .invalidBundleInfo: return "The bundle information was not valid." case .invalidCRLIndex: return "The CRL index was not valid." case .noFieldValues: return "No field values were detected." case .unsupportedFieldFormat: return "The field format is not supported." case .unsupportedIndexInfo: return "The index information is not supported." case .unsupportedLocality: return "The locality is not supported." case .unsupportedNumAttributes: return "The number of attributes is not supported." case .unsupportedNumIndexes: return "The number of indexes is not supported." case .unsupportedNumRecordTypes: return "The number of record types is not supported." case .fieldSpecifiedMultiple: return "Too many fields were specified." case .incompatibleFieldFormat: return "The field format was incompatible." case .invalidParsingModule: return "The parsing module was not valid." case .databaseLocked: return "The database is locked." case .datastoreIsOpen: return "The data store is open." case .missingValue: return "A missing value was detected." case .unsupportedQueryLimits: return "The query limits are not supported." case .unsupportedNumSelectionPreds: return "The number of selection predicates is not supported." case .unsupportedOperator: return "The operator is not supported." case .invalidDBLocation: return "The database location is not valid." case .invalidAccessRequest: return "The access request is not valid." case .invalidIndexInfo: return "The index information is not valid." case .invalidNewOwner: return "The new owner is not valid." case .invalidModifyMode: return "The modify mode is not valid." case .missingRequiredExtension: return "A required certificate extension is missing." case .extendedKeyUsageNotCritical: return "The extended key usage extension was not marked critical." case .timestampMissing: return "A timestamp was expected but was not found." case .timestampInvalid: return "The timestamp was not valid." case .timestampNotTrusted: return "The timestamp was not trusted." case .timestampServiceNotAvailable: return "The timestamp service is not available." case .timestampBadAlg: return "An unrecognized or unsupported Algorithm Identifier in timestamp." case .timestampBadRequest: return "The timestamp transaction is not permitted or supported." case .timestampBadDataFormat: return "The timestamp data submitted has the wrong format." case .timestampTimeNotAvailable: return "The time source for the Timestamp Authority is not available." case .timestampUnacceptedPolicy: return "The requested policy is not supported by the Timestamp Authority." case .timestampUnacceptedExtension: return "The requested extension is not supported by the Timestamp Authority." case .timestampAddInfoNotAvailable: return "The additional information requested is not available." case .timestampSystemFailure: return "The timestamp request cannot be handled due to system failure." case .signingTimeMissing: return "A signing time was expected but was not found." case .timestampRejection: return "A timestamp transaction was rejected." case .timestampWaiting: return "A timestamp transaction is waiting." case .timestampRevocationWarning: return "A timestamp authority revocation warning was issued." case .timestampRevocationNotification: return "A timestamp authority revocation notification was issued." case .unexpectedError: return "Unexpected error has occurred." } } } extension Status: CustomNSError { public static let errorDomain = KeychainAccessErrorDomain public var errorCode: Int { return Int(rawValue) } public var errorUserInfo: [String : Any] { return [NSLocalizedDescriptionKey: description] } } ================================================ FILE: External/SymbolPicker/.gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ .swiftpm/ .DS_Store ================================================ FILE: External/SymbolPicker/LICENSE ================================================ MIT License Copyright (c) 2022 Yubo Qin & Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: External/SymbolPicker/Package.swift ================================================ // swift-tools-version:5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "SymbolPicker", defaultLocalization: "en", platforms: [ .iOS(.v14), .macOS(.v12), .tvOS(.v15), .watchOS(.v8), ], products: [ .library( name: "SymbolPicker", targets: ["SymbolPicker"] ), ], dependencies: [ ], targets: [ .target( name: "SymbolPicker", dependencies: [], path: "Sources/SymbolPicker", resources: [ .process("Resources"), ] ), ] ) ================================================ FILE: External/SymbolPicker/README.md ================================================ # SymbolPicker A simple and cross-platform SFSymbol picker for SwiftUI ![](https://img.shields.io/badge/License-MIT-green) ![](https://img.shields.io/badge/Platform-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS-blue) ## Features SymbolPicker provides a simple and cross-platform interface for picking a SFSymbol with search functionality that is backported to iOS 14. SymbolPicker is implemented with SwiftUI and supports iOS, macOS, tvOS and watchOS platforms. ![](/Screenshots/demo.png) ## Usage ### Requirements * iOS 14.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+ * Xcode 13.0+ * Swift 5.0+ ### Installation SymbolPicker is available as a Swift Package. Add this repo to your project through Xcode GUI or `Package.swift`. ```swift dependencies: [ .package(url: "https://github.com/xnth97/SymbolPicker.git", .upToNextMajor(from: "1.1.0")) ] ``` ### Example It is suggested to use SymbolPicker within a `sheet`. ```swift import SwiftUI import SymbolPicker struct ContentView: View { @State private var iconPickerPresented = false @State private var icon = "pencil" var body: some View { Button(action: { iconPickerPresented = true }) { HStack { Image(systemName: icon) Text(icon) } } .sheet(isPresented: $iconPickerPresented) { SymbolPicker(symbol: $icon) } } } ``` ## TODO - [ ] Categories support - [x] Multiplatform support - [ ] Inline UI - [ ] Codegen from latest SF Symbols ## License SymbolPicker is available under the MIT license. See the [LICENSE](LICENSE) file for more info. ================================================ FILE: External/SymbolPicker/Sources/SymbolPicker/Resources/en.lproj/Localizable.strings ================================================ "search_placeholder" = "Search"; "cancel" = "Cancel"; "sf_symbol_picker" = "Select a symbol"; "done" = "Done"; ================================================ FILE: External/SymbolPicker/Sources/SymbolPicker/Resources/sfsymbols.txt ================================================ square.and.arrow.up square.and.arrow.up.fill square.and.arrow.up.circle square.and.arrow.up.circle.fill square.and.arrow.up.trianglebadge.exclamationmark square.and.arrow.down square.and.arrow.down.fill square.and.arrow.up.on.square square.and.arrow.up.on.square.fill square.and.arrow.down.on.square square.and.arrow.down.on.square.fill rectangle.portrait.and.arrow.right rectangle.portrait.and.arrow.right.fill pencil pencil.circle pencil.circle.fill pencil.slash square.and.pencil rectangle.and.pencil.and.ellipsis scribble scribble.variable highlighter pencil.and.outline pencil.tip pencil.tip.crop.circle pencil.tip.crop.circle.badge.plus pencil.tip.crop.circle.badge.minus pencil.tip.crop.circle.badge.arrow.forward lasso lasso.and.sparkles trash trash.fill trash.circle trash.circle.fill trash.square trash.square.fill trash.slash trash.slash.fill trash.slash.circle trash.slash.circle.fill trash.slash.square trash.slash.square.fill folder folder.fill folder.circle folder.circle.fill folder.badge.plus folder.fill.badge.plus folder.badge.minus folder.fill.badge.minus folder.badge.questionmark folder.fill.badge.questionmark folder.badge.person.crop folder.fill.badge.person.crop square.grid.3x1.folder.badge.plus square.grid.3x1.folder.fill.badge.plus folder.badge.gearshape folder.fill.badge.gearshape plus.rectangle.on.folder plus.rectangle.on.folder.fill questionmark.folder questionmark.folder.fill paperplane paperplane.fill paperplane.circle paperplane.circle.fill tray tray.fill tray.circle tray.circle.fill tray.and.arrow.up tray.and.arrow.up.fill tray.and.arrow.down tray.and.arrow.down.fill tray.2 tray.2.fill tray.full tray.full.fill externaldrive externaldrive.fill externaldrive.badge.plus externaldrive.fill.badge.plus externaldrive.badge.minus externaldrive.fill.badge.minus externaldrive.badge.checkmark externaldrive.fill.badge.checkmark externaldrive.badge.xmark externaldrive.fill.badge.xmark externaldrive.badge.person.crop externaldrive.fill.badge.person.crop externaldrive.badge.icloud externaldrive.fill.badge.icloud externaldrive.badge.wifi externaldrive.fill.badge.wifi externaldrive.badge.timemachine externaldrive.fill.badge.timemachine internaldrive internaldrive.fill opticaldiscdrive opticaldiscdrive.fill externaldrive.connected.to.line.below externaldrive.connected.to.line.below.fill archivebox archivebox.fill archivebox.circle archivebox.circle.fill xmark.bin xmark.bin.fill xmark.bin.circle xmark.bin.circle.fill arrow.up.bin arrow.up.bin.fill doc doc.fill doc.circle doc.circle.fill doc.badge.plus doc.fill.badge.plus doc.badge.gearshape doc.badge.gearshape.fill doc.badge.ellipsis doc.fill.badge.ellipsis lock.doc lock.doc.fill arrow.up.doc arrow.up.doc.fill arrow.down.doc arrow.down.doc.fill doc.text doc.text.fill doc.zipper doc.on.doc doc.on.doc.fill doc.on.clipboard arrow.right.doc.on.clipboard arrow.up.doc.on.clipboard arrow.triangle.2.circlepath.doc.on.clipboard doc.on.clipboard.fill doc.richtext doc.richtext.fill doc.plaintext doc.plaintext.fill doc.append doc.append.fill doc.text.below.ecg doc.text.below.ecg.fill chart.bar.doc.horizontal chart.bar.doc.horizontal.fill list.bullet.rectangle.portrait list.bullet.rectangle.portrait.fill doc.text.magnifyingglass list.bullet.rectangle list.bullet.rectangle.fill list.dash.header.rectangle terminal terminal.fill note note.text note.text.badge.plus calendar calendar.circle calendar.circle.fill calendar.badge.plus calendar.badge.minus calendar.badge.clock calendar.badge.exclamationmark calendar.day.timeline.left calendar.day.timeline.right calendar.day.timeline.leading calendar.day.timeline.trailing arrowshape.turn.up.left arrowshape.turn.up.left.fill arrowshape.turn.up.left.circle arrowshape.turn.up.left.circle.fill arrowshape.turn.up.backward arrowshape.turn.up.backward.fill arrowshape.turn.up.backward.circle arrowshape.turn.up.backward.circle.fill arrowshape.turn.up.right arrowshape.turn.up.right.fill arrowshape.turn.up.right.circle arrowshape.turn.up.right.circle.fill arrowshape.turn.up.forward arrowshape.turn.up.forward.fill arrowshape.turn.up.forward.circle arrowshape.turn.up.forward.circle.fill arrowshape.turn.up.left.2 arrowshape.turn.up.left.2.fill arrowshape.turn.up.left.2.circle arrowshape.turn.up.left.2.circle.fill arrowshape.turn.up.backward.2 arrowshape.turn.up.backward.2.fill arrowshape.turn.up.backward.2.circle arrowshape.turn.up.backward.2.circle.fill arrowshape.zigzag.right arrowshape.zigzag.right.fill arrowshape.zigzag.forward arrowshape.zigzag.forward.fill arrowshape.bounce.right arrowshape.bounce.right.fill arrowshape.bounce.forward arrowshape.bounce.forward.fill book book.fill book.circle book.circle.fill books.vertical books.vertical.fill books.vertical.circle books.vertical.circle.fill book.closed book.closed.fill book.closed.circle book.closed.circle.fill character.book.closed character.book.closed.fill text.book.closed text.book.closed.fill menucard menucard.fill greetingcard greetingcard.fill magazine magazine.fill newspaper newspaper.fill newspaper.circle newspaper.circle.fill heart.text.square heart.text.square.fill square.text.square square.text.square.fill doc.text.image doc.text.image.fill bookmark bookmark.fill bookmark.circle bookmark.circle.fill bookmark.square bookmark.square.fill bookmark.slash bookmark.slash.fill rosette graduationcap graduationcap.fill graduationcap.circle graduationcap.circle.fill ticket ticket.fill paperclip paperclip.circle paperclip.circle.fill paperclip.badge.ellipsis rectangle.and.paperclip rectangle.dashed.and.paperclip link link.circle link.circle.fill link.badge.plus personalhotspot personalhotspot.circle personalhotspot.circle.fill lineweight person person.fill person.fill.turn.right person.fill.turn.down person.fill.turn.left person.fill.checkmark person.fill.xmark person.fill.questionmark person.circle person.circle.fill person.badge.plus person.fill.badge.plus person.badge.minus person.fill.badge.minus person.badge.clock person.badge.clock.fill shareplay shareplay.slash rectangle.inset.filled.and.person.filled person.and.arrow.left.and.arrow.right person.fill.and.arrow.left.and.arrow.right person.2 person.2.fill person.2.circle person.2.circle.fill person.wave.2 person.wave.2.fill person.2.wave.2 person.2.wave.2.fill person.3 person.3.fill person.3.sequence person.3.sequence.fill lanyardcard lanyardcard.fill person.crop.circle person.crop.circle.fill person.crop.circle.badge.plus person.crop.circle.fill.badge.plus person.crop.circle.badge.minus person.crop.circle.fill.badge.minus person.crop.circle.badge.checkmark person.crop.circle.fill.badge.checkmark person.crop.circle.badge.xmark person.crop.circle.fill.badge.xmark person.crop.circle.badge.questionmark person.crop.circle.badge.questionmark.fill person.crop.circle.badge.exclamationmark person.crop.circle.badge.exclamationmark.fill person.crop.circle.badge.moon person.crop.circle.badge.moon.fill person.crop.circle.badge.clock person.crop.circle.badge.clock.fill person.crop.circle.badge person.crop.circle.badge.fill person.crop.square person.crop.square.fill person.crop.artframe photo.artframe person.crop.rectangle.stack person.crop.rectangle.stack.fill person.2.crop.square.stack person.2.crop.square.stack.fill person.crop.rectangle person.crop.rectangle.fill arrow.up.and.person.rectangle.portrait arrow.up.and.person.rectangle.turn.right arrow.up.and.person.rectangle.turn.left person.crop.square.filled.and.at.rectangle person.crop.square.filled.and.at.rectangle.fill square.and.at.rectangle square.and.at.rectangle.fill person.text.rectangle person.text.rectangle.fill command command.circle command.circle.fill command.square command.square.fill option alt clear clear.fill delete.left delete.left.fill delete.backward delete.backward.fill delete.right delete.right.fill delete.forward delete.forward.fill shift shift.fill capslock capslock.fill escape restart restart.circle restart.circle.fill sleep sleep.circle sleep.circle.fill wake wake.circle wake.circle.fill power power.circle power.circle.fill power.dotted togglepower poweron poweroff powersleep directcurrent alternatingcurrent peacesign dot.arrowtriangles.up.right.down.left.circle globe globe.badge.chevron.backward network network.badge.shield.half.filled globe.americas globe.americas.fill globe.europe.africa globe.europe.africa.fill globe.asia.australia globe.asia.australia.fill sun.min sun.min.fill sun.max sun.max.fill sun.max.circle sun.max.circle.fill sunrise sunrise.fill sunset sunset.fill sun.and.horizon sun.and.horizon.fill sun.dust sun.dust.fill sun.haze sun.haze.fill moon moon.fill moon.circle moon.circle.fill zzz moon.zzz moon.zzz.fill sparkle sparkles moon.stars moon.stars.fill cloud cloud.fill cloud.drizzle cloud.drizzle.fill cloud.rain cloud.rain.fill cloud.heavyrain cloud.heavyrain.fill cloud.fog cloud.fog.fill cloud.hail cloud.hail.fill cloud.snow cloud.snow.fill cloud.sleet cloud.sleet.fill cloud.bolt cloud.bolt.fill cloud.bolt.rain cloud.bolt.rain.fill cloud.sun cloud.sun.fill cloud.sun.rain cloud.sun.rain.fill cloud.sun.bolt cloud.sun.bolt.fill cloud.moon cloud.moon.fill cloud.moon.rain cloud.moon.rain.fill cloud.moon.bolt cloud.moon.bolt.fill smoke smoke.fill wind wind.snow snowflake snowflake.circle snowflake.circle.fill tornado tropicalstorm hurricane thermometer.sun thermometer.sun.fill thermometer.snowflake thermometer aqi.low aqi.medium aqi.high humidity humidity.fill umbrella umbrella.fill flame flame.fill flame.circle flame.circle.fill light.min light.max rays slowmo timelapse cursorarrow.rays cursorarrow cursorarrow.square cursorarrow.and.square.on.square.dashed cursorarrow.click cursorarrow.click.2 contextualmenu.and.cursorarrow filemenu.and.cursorarrow filemenu.and.selection dot.circle.and.hand.point.up.left.fill dot.circle.and.cursorarrow cursorarrow.motionlines cursorarrow.motionlines.click cursorarrow.click.badge.clock keyboard keyboard.fill keyboard.badge.ellipsis keyboard.chevron.compact.down keyboard.chevron.compact.left keyboard.onehanded.left keyboard.onehanded.right rectangle.3.group rectangle.3.group.fill square.grid.3x2 square.grid.3x2.fill rectangle.grid.3x2 rectangle.grid.3x2.fill square.grid.2x2 square.grid.2x2.fill rectangle.grid.2x2 rectangle.grid.2x2.fill square.grid.3x1.below.line.grid.1x2 square.grid.3x1.below.line.grid.1x2.fill square.grid.4x3.fill rectangle.grid.1x2 rectangle.grid.1x2.fill circle.grid.2x2 circle.grid.2x2.fill circle.grid.3x3 circle.grid.3x3.fill circle.grid.3x3.circle circle.grid.3x3.circle.fill square.grid.3x3 square.grid.3x3.fill square.grid.3x3.topleft.filled square.grid.3x3.topmiddle.filled square.grid.3x3.topright.filled square.grid.3x3.middleleft.filled square.grid.3x3.middle.filled square.grid.3x3.middleright.filled square.grid.3x3.bottomleft.filled square.grid.3x3.bottommiddle.filled square.grid.3x3.bottomright.filled circle.hexagongrid circle.hexagongrid.fill circle.hexagongrid.circle circle.hexagongrid.circle.fill circle.hexagonpath circle.hexagonpath.fill circle.grid.cross circle.grid.cross.fill circle.grid.cross.left.filled circle.grid.cross.up.filled circle.grid.cross.right.filled circle.grid.cross.down.filled seal seal.fill checkmark.seal checkmark.seal.fill xmark.seal xmark.seal.fill exclamationmark.triangle exclamationmark.triangle.fill drop drop.fill drop.circle drop.circle.fill drop.triangle drop.triangle.fill play play.fill play.circle play.circle.fill play.square play.square.fill play.rectangle play.rectangle.fill play.slash play.slash.fill pause pause.fill pause.circle pause.circle.fill pause.rectangle pause.rectangle.fill stop stop.fill stop.circle stop.circle.fill record.circle record.circle.fill playpause playpause.fill backward backward.fill backward.circle backward.circle.fill forward forward.fill forward.circle forward.circle.fill backward.end backward.end.fill forward.end forward.end.fill backward.end.alt backward.end.alt.fill forward.end.alt forward.end.alt.fill backward.frame backward.frame.fill forward.frame forward.frame.fill eject eject.fill eject.circle eject.circle.fill mount mount.fill memories memories.badge.plus memories.badge.minus shuffle shuffle.circle shuffle.circle.fill repeat repeat.circle repeat.circle.fill repeat.1 repeat.1.circle repeat.1.circle.fill infinity infinity.circle infinity.circle.fill megaphone megaphone.fill speaker speaker.fill speaker.circle speaker.circle.fill speaker.slash speaker.slash.fill speaker.slash.circle speaker.slash.circle.fill speaker.zzz speaker.zzz.fill speaker.wave.1 speaker.wave.1.fill speaker.wave.2 speaker.wave.2.fill speaker.wave.2.circle speaker.wave.2.circle.fill speaker.wave.3 speaker.wave.3.fill speaker.badge.exclamationmark speaker.badge.exclamationmark.fill badge.plus.radiowaves.right badge.plus.radiowaves.forward music.note music.note.list music.quarternote.3 music.mic music.mic.circle music.mic.circle.fill arrow.rectanglepath goforward gobackward goforward.5 gobackward.5 goforward.10 gobackward.10 goforward.15 gobackward.15 goforward.30 gobackward.30 goforward.45 gobackward.45 goforward.60 gobackward.60 goforward.75 gobackward.75 goforward.90 gobackward.90 goforward.plus gobackward.minus swift magnifyingglass magnifyingglass.circle magnifyingglass.circle.fill plus.magnifyingglass minus.magnifyingglass 1.magnifyingglass arrow.up.left.and.down.right.magnifyingglass text.magnifyingglass sparkle.magnifyingglass location.magnifyingglass loupe mic mic.fill mic.circle mic.circle.fill mic.square mic.square.fill mic.slash mic.slash.fill mic.slash.circle mic.slash.circle.fill mic.badge.plus mic.fill.badge.plus line.diagonal line.diagonal.arrow circle circle.fill circle.slash circle.slash.fill circle.lefthalf.filled circle.righthalf.filled circle.tophalf.filled circle.bottomhalf.filled circle.inset.filled smallcircle.filled.circle smallcircle.filled.circle.fill circle.dashed circle.dashed.inset.filled circle.dotted circlebadge circlebadge.fill circlebadge.2 circlebadge.2.fill smallcircle.circle smallcircle.circle.fill target capsule capsule.fill capsule.lefthalf.filled capsule.righthalf.filled capsule.tophalf.filled capsule.bottomhalf.filled capsule.inset.filled capsule.portrait capsule.portrait.fill capsule.portrait.lefthalf.filled capsule.portrait.righthalf.filled capsule.portrait.tophalf.filled capsule.portrait.bottomhalf.filled capsule.portrait.inset.filled oval oval.fill oval.lefthalf.filled oval.righthalf.filled oval.tophalf.filled oval.bottomhalf.filled oval.inset.filled oval.portrait oval.portrait.fill oval.portrait.lefthalf.filled oval.portrait.righthalf.filled oval.portrait.tophalf.filled oval.portrait.bottomhalf.filled oval.portrait.inset.filled placeholdertext.fill square square.fill square.slash square.slash.fill square.lefthalf.filled square.righthalf.filled square.tophalf.filled square.bottomhalf.filled square.inset.filled square.split.2x1 square.split.2x1.fill square.split.1x2 square.split.1x2.fill square.split.2x2 square.split.2x2.fill square.split.diagonal.2x2 square.split.diagonal.2x2.fill square.split.diagonal square.split.diagonal.fill dot.square dot.square.fill circle.square circle.square.fill square.dashed square.dashed.inset.filled plus.square.dashed questionmark.square.dashed square.on.square square.fill.on.square.fill square.filled.on.square hand.raised.square.on.square hand.raised.square.on.square.fill sparkles.square.filled.on.square square.on.square.dashed plus.square.on.square plus.square.fill.on.square.fill square.on.circle square.fill.on.circle.fill r.square.on.square r.square.on.square.fill j.square.on.square j.square.on.square.fill h.square.on.square h.square.on.square.fill square.stack square.stack.fill squareshape squareshape.fill squareshape.dashed.squareshape squareshape.squareshape.dashed dot.squareshape dot.squareshape.fill app app.fill rectangle rectangle.fill rectangle.slash rectangle.slash.fill rectangle.lefthalf.filled rectangle.righthalf.filled rectangle.leadinghalf.filled rectangle.trailinghalf.filled rectangle.tophalf.filled rectangle.bottomhalf.filled rectangle.split.2x1 rectangle.split.2x1.fill rectangle.split.2x1.slash rectangle.split.2x1.slash.fill rectangle.split.1x2 rectangle.split.1x2.fill rectangle.split.3x1 rectangle.split.3x1.fill rectangle.split.2x2 rectangle.split.2x2.fill tablecells tablecells.fill tablecells.badge.ellipsis tablecells.fill.badge.ellipsis rectangle.split.3x3 rectangle.inset.filled rectangle.tophalf.inset.filled rectangle.bottomhalf.inset.filled rectangle.lefthalf.inset.filled rectangle.righthalf.inset.filled rectangle.leadinghalf.inset.filled rectangle.trailinghalf.inset.filled rectangle.lefthalf.inset.filled.arrow.left rectangle.righthalf.inset.filled.arrow.right rectangle.leadinghalf.inset.filled.arrow.leading rectangle.trailinghalf.inset.filled.arrow.trailing rectangle.topthird.inset.filled rectangle.bottomthird.inset.filled rectangle.leftthird.inset.filled rectangle.rightthird.inset.filled rectangle.leadingthird.inset.filled rectangle.trailingthird.inset.filled rectangle.center.inset.filled rectangle.center.inset.filled.badge.plus rectangle.inset.topleft.filled rectangle.inset.topright.filled rectangle.inset.topleading.filled rectangle.inset.toptrailing.filled rectangle.inset.bottomleft.filled rectangle.inset.bottomright.filled rectangle.inset.bottomleading.filled rectangle.inset.bottomtrailing.filled rectangle.on.rectangle rectangle.fill.on.rectangle.fill rectangle.on.rectangle.circle rectangle.on.rectangle.circle.fill rectangle.on.rectangle.square rectangle.on.rectangle.square.fill rectangle.inset.filled.on.rectangle rectangle.on.rectangle.slash rectangle.on.rectangle.slash.fill rectangle.on.rectangle.slash.circle rectangle.on.rectangle.slash.circle.fill play.rectangle.on.rectangle play.rectangle.on.rectangle.fill play.rectangle.on.rectangle.circle play.rectangle.on.rectangle.circle.fill plus.rectangle.on.rectangle plus.rectangle.fill.on.rectangle.fill rectangle.portrait rectangle.portrait.fill rectangle.portrait.slash rectangle.portrait.slash.fill rectangle.portrait.lefthalf.filled rectangle.portrait.righthalf.filled rectangle.portrait.tophalf.filled rectangle.portrait.bottomhalf.filled rectangle.portrait.inset.filled rectangle.portrait.tophalf.inset.filled rectangle.portrait.bottomhalf.inset.filled rectangle.portrait.lefthalf.inset.filled rectangle.portrait.righthalf.inset.filled rectangle.portrait.leadinghalf.inset.filled rectangle.portrait.trailinghalf.inset.filled rectangle.portrait.topthird.inset.filled rectangle.portrait.bottomthird.inset.filled rectangle.portrait.leftthird.inset.filled rectangle.portrait.rightthird.inset.filled rectangle.portrait.leadingthird.inset.filled rectangle.portrait.trailingthird.inset.filled rectangle.portrait.center.inset.filled rectangle.portrait.topleft.inset.filled rectangle.portrait.topright.inset.filled rectangle.portrait.topleading.inset.filled rectangle.portrait.toptrailing.inset.filled rectangle.portrait.bottomleft.inset.filled rectangle.portrait.bottomright.inset.filled rectangle.portrait.bottomleading.inset.filled rectangle.portrait.bottomtrailing.inset.filled rectangle.portrait.on.rectangle.portrait rectangle.portrait.on.rectangle.portrait.fill rectangle.portrait.on.rectangle.portrait.slash rectangle.portrait.on.rectangle.portrait.slash.fill rectangle.portrait.split.2x1 rectangle.portrait.split.2x1.fill rectangle.portrait.split.2x1.slash rectangle.portrait.split.2x1.slash.fill triangle triangle.fill triangle.lefthalf.filled triangle.righthalf.filled triangle.tophalf.filled triangle.bottomhalf.filled triangle.inset.filled diamond diamond.fill diamond.circle diamond.circle.fill diamond.lefthalf.filled diamond.righthalf.filled diamond.tophalf.filled diamond.bottomhalf.filled diamond.inset.filled octagon octagon.fill octagon.lefthalf.filled octagon.righthalf.filled octagon.tophalf.filled octagon.bottomhalf.filled hexagon hexagon.fill hexagon.lefthalf.filled hexagon.righthalf.filled hexagon.tophalf.filled hexagon.bottomhalf.filled pentagon pentagon.fill pentagon.lefthalf.filled pentagon.righthalf.filled pentagon.tophalf.filled pentagon.bottomhalf.filled suit.heart suit.heart.fill suit.club suit.club.fill suit.spade suit.spade.fill suit.diamond suit.diamond.fill heart heart.fill heart.circle heart.circle.fill heart.square heart.square.fill heart.rectangle heart.rectangle.fill heart.slash heart.slash.fill heart.slash.circle heart.slash.circle.fill bolt.heart bolt.heart.fill arrow.up.heart arrow.up.heart.fill arrow.down.heart arrow.down.heart.fill arrow.clockwise.heart arrow.clockwise.heart.fill rhombus rhombus.fill star star.fill star.leadinghalf.filled star.circle star.circle.fill star.square star.square.fill star.slash star.slash.fill line.horizontal.star.fill.line.horizontal flag flag.fill flag.circle flag.circle.fill flag.square flag.square.fill flag.slash flag.slash.fill flag.slash.circle flag.slash.circle.fill flag.badge.ellipsis flag.badge.ellipsis.fill flag.2.crossed flag.2.crossed.fill flag.filled.and.flag.crossed flag.and.flag.filled.crossed location location.fill location.circle location.circle.fill location.square location.square.fill location.slash location.slash.fill location.north location.north.fill location.north.circle location.north.circle.fill location.north.line location.north.line.fill sensor.tag.radiowaves.forward sensor.tag.radiowaves.forward.fill airtag.radiowaves.forward airtag.radiowaves.forward.fill airtag airtag.fill bell bell.fill bell.circle bell.circle.fill bell.square bell.square.fill bell.slash bell.slash.fill bell.slash.circle bell.slash.circle.fill bell.and.waveform bell.and.waveform.fill bell.badge bell.badge.fill bell.badge.circle bell.badge.circle.fill tag tag.fill tag.circle tag.circle.fill tag.square tag.square.fill tag.slash tag.slash.fill bolt bolt.fill bolt.circle bolt.circle.fill bolt.square bolt.square.fill bolt.ring.closed bolt.shield bolt.shield.fill bolt.slash bolt.slash.fill bolt.slash.circle bolt.slash.circle.fill bolt.badge.a bolt.badge.a.fill bolt.horizontal bolt.horizontal.fill bolt.horizontal.circle bolt.horizontal.circle.fill eye eye.fill eye.circle eye.circle.fill eye.square eye.square.fill eye.slash eye.slash.fill eye.slash.circle eye.slash.circle.fill eye.trianglebadge.exclamationmark eye.trianglebadge.exclamationmark.fill tshirt tshirt.fill eyes eyes.inverse eyebrow nose nose.fill mustache mustache.fill mouth mouth.fill eyeglasses facemask facemask.fill brain.head.profile brain icloud icloud.fill icloud.circle icloud.circle.fill icloud.square icloud.square.fill icloud.slash icloud.slash.fill exclamationmark.icloud exclamationmark.icloud.fill checkmark.icloud checkmark.icloud.fill xmark.icloud xmark.icloud.fill link.icloud link.icloud.fill bolt.horizontal.icloud bolt.horizontal.icloud.fill person.icloud person.icloud.fill lock.icloud lock.icloud.fill key.icloud key.icloud.fill arrow.clockwise.icloud arrow.clockwise.icloud.fill arrow.counterclockwise.icloud arrow.counterclockwise.icloud.fill icloud.and.arrow.down icloud.and.arrow.down.fill icloud.and.arrow.up icloud.and.arrow.up.fill flashlight.off.fill flashlight.on.fill camera camera.fill camera.circle camera.circle.fill camera.shutter.button camera.shutter.button.fill camera.badge.ellipsis camera.fill.badge.ellipsis arrow.triangle.2.circlepath.camera arrow.triangle.2.circlepath.camera.fill camera.on.rectangle camera.on.rectangle.fill message message.fill message.circle message.circle.fill message.and.waveform message.and.waveform.fill arrow.up.message arrow.up.message.fill plus.message plus.message.fill bubble.right bubble.right.fill bubble.right.circle bubble.right.circle.fill bubble.left bubble.left.fill bubble.left.circle bubble.left.circle.fill exclamationmark.bubble exclamationmark.bubble.fill exclamationmark.bubble.circle exclamationmark.bubble.circle.fill quote.opening quote.closing quote.bubble quote.bubble.fill star.bubble star.bubble.fill character.bubble character.bubble.fill text.bubble text.bubble.fill captions.bubble captions.bubble.fill plus.bubble plus.bubble.fill checkmark.bubble checkmark.bubble.fill rectangle.3.group.bubble.left rectangle.3.group.bubble.left.fill ellipsis.bubble ellipsis.bubble.fill ellipsis.vertical.bubble ellipsis.vertical.bubble.fill phone.bubble.left phone.bubble.left.fill video.bubble.left video.bubble.left.fill bubble.middle.bottom bubble.middle.bottom.fill bubble.middle.top bubble.middle.top.fill bubble.left.and.bubble.right bubble.left.and.bubble.right.fill bubble.left.and.exclamationmark.bubble.right bubble.left.and.exclamationmark.bubble.right.fill phone phone.fill phone.circle phone.circle.fill phone.badge.plus phone.fill.badge.plus phone.connection phone.fill.connection phone.and.waveform phone.and.waveform.fill phone.arrow.up.right phone.fill.arrow.up.right phone.arrow.down.left phone.fill.arrow.down.left phone.arrow.right phone.fill.arrow.right phone.down phone.down.fill phone.down.circle phone.down.circle.fill teletype teletype.circle teletype.circle.fill teletype.answer teletype.answer.circle teletype.answer.circle.fill video video.fill video.circle video.circle.fill video.square video.square.fill video.slash video.slash.fill video.badge.plus video.fill.badge.plus video.badge.checkmark video.fill.badge.checkmark video.badge.ellipsis video.fill.badge.ellipsis video.and.waveform video.and.waveform.fill arrow.up.right.video arrow.up.right.video.fill arrow.down.left.video arrow.down.left.video.fill questionmark.video questionmark.video.fill envelope envelope.fill envelope.circle envelope.circle.fill envelope.arrow.triangle.branch envelope.arrow.triangle.branch.fill envelope.open envelope.open.fill envelope.badge envelope.badge.fill envelope.badge.shield.half.filled envelope.badge.shield.half.filled.fill mail.stack mail.stack.fill mail mail.fill mail.and.text.magnifyingglass rectangle.and.text.magnifyingglass arrow.up.right.and.arrow.down.left.rectangle arrow.up.right.and.arrow.down.left.rectangle.fill gear gear.circle gear.circle.fill gear.badge.checkmark gear.badge.xmark gear.badge.questionmark gearshape gearshape.fill gearshape.circle gearshape.circle.fill gearshape.2 gearshape.2.fill signature line.3.crossed.swirl.circle line.3.crossed.swirl.circle.fill scissors scissors.circle scissors.circle.fill scissors.badge.ellipsis ellipsis ellipsis.circle ellipsis.circle.fill ellipsis.rectangle ellipsis.rectangle.fill bag bag.fill bag.circle bag.circle.fill bag.badge.plus bag.fill.badge.plus bag.badge.minus bag.fill.badge.minus cart cart.fill cart.circle cart.circle.fill cart.badge.plus cart.fill.badge.plus cart.badge.minus cart.fill.badge.minus creditcard creditcard.fill creditcard.circle creditcard.circle.fill creditcard.and.123 creditcard.trianglebadge.exclamationmark giftcard giftcard.fill wallet.pass wallet.pass.fill wand.and.rays wand.and.rays.inverse wand.and.stars wand.and.stars.inverse crop crop.rotate dial.min dial.min.fill dial.max dial.max.fill gyroscope nosign gauge gauge.badge.plus gauge.badge.minus speedometer barometer metronome metronome.fill amplifier dice dice.fill die.face.1 die.face.1.fill die.face.2 die.face.2.fill die.face.3 die.face.3.fill die.face.4 die.face.4.fill die.face.5 die.face.5.fill die.face.6 die.face.6.fill square.grid.3x3.square pianokeys pianokeys.inverse tuningfork paintbrush paintbrush.fill paintbrush.pointed paintbrush.pointed.fill bandage bandage.fill ruler ruler.fill level level.fill lines.measurement.horizontal wrench wrench.fill hammer hammer.fill hammer.circle hammer.circle.fill screwdriver screwdriver.fill eyedropper eyedropper.halffull eyedropper.full wrench.and.screwdriver wrench.and.screwdriver.fill applescript applescript.fill scroll scroll.fill stethoscope stethoscope.circle stethoscope.circle.fill printer printer.fill printer.filled.and.paper printer.dotmatrix printer.dotmatrix.fill printer.dotmatrix.filled.and.paper scanner scanner.fill faxmachine briefcase briefcase.fill briefcase.circle briefcase.circle.fill case case.fill latch.2.case latch.2.case.fill cross.case cross.case.fill suitcase suitcase.fill suitcase.cart suitcase.cart.fill theatermasks theatermasks.fill theatermasks.circle theatermasks.circle.fill puzzlepiece.extension puzzlepiece.extension.fill puzzlepiece puzzlepiece.fill homekit house house.fill house.circle house.circle.fill music.note.house music.note.house.fill building.columns building.columns.fill building.columns.circle building.columns.circle.fill signpost.left signpost.left.fill signpost.right signpost.right.fill square.split.bottomrightquarter square.split.bottomrightquarter.fill building building.fill building.2 building.2.fill building.2.crop.circle building.2.crop.circle.fill lock lock.fill lock.circle lock.circle.fill lock.square lock.square.fill lock.square.stack lock.square.stack.fill lock.rectangle lock.rectangle.fill lock.rectangle.stack lock.rectangle.stack.fill lock.rectangle.on.rectangle lock.rectangle.on.rectangle.fill lock.shield lock.shield.fill lock.slash lock.slash.fill lock.open lock.open.fill lock.rotation lock.rotation.open key key.fill wifi wifi.circle wifi.circle.fill wifi.square wifi.square.fill wifi.slash wifi.exclamationmark pin pin.fill pin.circle pin.circle.fill pin.square pin.square.fill pin.slash pin.slash.fill mappin mappin.circle mappin.circle.fill mappin.square mappin.square.fill mappin.slash mappin.slash.circle mappin.slash.circle.fill mappin.and.ellipse map map.fill map.circle map.circle.fill safari safari.fill move.3d scale.3d rotate.3d torus rotate.left rotate.left.fill rotate.right rotate.right.fill selection.pin.in.out powerplug powerplug.fill timeline.selection cpu cpu.fill memorychip memorychip.fill opticaldisc display lock.display lock.open.display display.and.arrow.down display.trianglebadge.exclamationmark display.2 desktopcomputer lock.desktopcomputer lock.open.desktopcomputer desktopcomputer.and.arrow.down desktopcomputer.trianglebadge.exclamationmark pc macpro.gen1 macpro.gen1.fill macpro.gen2 macpro.gen2.fill macpro.gen3 macpro.gen3.fill macpro.gen3.server server.rack xserve laptopcomputer lock.laptopcomputer lock.open.laptopcomputer laptopcomputer.and.arrow.down laptopcomputer.trianglebadge.exclamationmark laptopcomputer.and.iphone ipad.and.iphone macmini macmini.fill airport.express airport.extreme airport.extreme.tower ipod ipodshuffle.gen1 ipodshuffle.gen2 ipodshuffle.gen3 ipodshuffle.gen4 ipodtouch ipodtouch.slash ipodtouch.landscape flipphone candybarphone iphone.homebutton iphone.homebutton.circle iphone.homebutton.circle.fill iphone.homebutton.landscape iphone.homebutton.radiowaves.left.and.right iphone.homebutton.radiowaves.left.and.right.circle iphone.homebutton.radiowaves.left.and.right.circle.fill iphone.homebutton.slash iphone.homebutton.slash.circle iphone.homebutton.slash.circle.fill iphone.homebutton.badge.play iphone iphone.circle iphone.circle.fill iphone.landscape iphone.radiowaves.left.and.right iphone.radiowaves.left.and.right.circle iphone.radiowaves.left.and.right.circle.fill iphone.slash iphone.slash.circle iphone.slash.circle.fill iphone.badge.play lock.iphone lock.open.iphone iphone.and.arrow.forward arrow.turn.up.forward.iphone arrow.turn.up.forward.iphone.fill iphone.rear.camera apps.iphone apps.iphone.badge.plus apps.iphone.landscape platter.filled.top.iphone platter.filled.bottom.iphone platter.filled.top.and.arrow.up.iphone platter.filled.bottom.and.arrow.down.iphone platter.2.filled.iphone platter.2.filled.iphone.landscape iphone.smartbatterycase.gen2 iphone.smartbatterycase.gen1 ipad.homebutton ipad.homebutton.badge.play ipad.homebutton.landscape ipad.homebutton.landscape.badge.play ipad ipad.badge.play lock.ipad lock.open.ipad ipad.and.arrow.forward ipad.landscape ipad.landscape.badge.play ipad.rear.camera apps.ipad apps.ipad.landscape platter.2.filled.ipad platter.2.filled.ipad.landscape applepencil magicmouse magicmouse.fill computermouse computermouse.fill applewatch applewatch.watchface exclamationmark.applewatch lock.applewatch lock.open.applewatch applewatch.radiowaves.left.and.right applewatch.slash applewatch.side.right watchface.applewatch.case applewatch.case.inset.filled platter.filled.top.applewatch.case platter.filled.bottom.applewatch.case platter.top.applewatch.case platter.bottom.applewatch.case digitalcrown.arrow.clockwise digitalcrown.arrow.clockwise.fill digitalcrown.arrow.counterclockwise digitalcrown.arrow.counterclockwise.fill digitalcrown.press digitalcrown.press.fill digitalcrown.horizontal.arrow.clockwise digitalcrown.horizontal.arrow.clockwise.fill digitalcrown.horizontal.arrow.counterclockwise digitalcrown.horizontal.arrow.counterclockwise.fill digitalcrown.horizontal.press digitalcrown.horizontal.press.fill airpodsmax beats.headphones headphones headphones.circle headphones.circle.fill earbuds earbuds.case earbuds.case.fill earpods airpods airpod.right airpod.left airpods.chargingcase airpods.chargingcase.fill airpods.chargingcase.wireless airpods.chargingcase.wireless.fill airpodspro airpodpro.right airpodpro.left airpodspro.chargingcase.wireless airpodspro.chargingcase.wireless.fill airpods.gen3 airpod.gen3.right airpod.gen3.left airpods.gen3.chargingcase.wireless airpods.gen3.chargingcase.wireless.fill beats.earphones beats.powerbeatspro beats.powerbeatspro.right beats.powerbeatspro.left beats.powerbeats beats.powerbeats3 beats.studiobuds beats.studiobud.left beats.studiobud.right beats.studiobuds.chargingcase beats.studiobuds.chargingcase.fill beats.fit.pro beats.fit.pro.left beats.fit.pro.right beats.fit.pro.chargingcase beats.fit.pro.chargingcase.fill beats.powerbeatspro.chargingcase beats.powerbeatspro.chargingcase.fill homepodmini homepodmini.fill homepodmini.2 homepodmini.2.fill homepod.and.homepodmini homepod.and.homepodmini.fill hifispeaker.and.homepodmini hifispeaker.and.homepodmini.fill homepod homepod.fill homepod.2 homepod.2.fill hifispeaker.and.homepod hifispeaker.and.homepod.fill hifispeaker hifispeaker.fill hifispeaker.2 hifispeaker.2.fill appletv appletv.fill homepod.and.appletv homepod.and.appletv.fill homepodmini.and.appletv homepodmini.and.appletv.fill hifispeaker.and.appletv hifispeaker.and.appletv.fill appletvremote.gen1 appletvremote.gen1.fill appletvremote.gen2 appletvremote.gen2.fill appletvremote.gen3 appletvremote.gen3.fill appletvremote.gen4 appletvremote.gen4.fill magsafe.batterypack magsafe.batterypack.fill mediastick cable.connector cable.connector.horizontal radio radio.fill tv tv.fill tv.inset.filled tv.circle tv.circle.fill sparkles.tv sparkles.tv.fill 4k.tv 4k.tv.fill music.note.tv music.note.tv.fill play.tv play.tv.fill photo.tv tv.and.hifispeaker.fill tv.and.mediabox airplayvideo airplayvideo.circle airplayvideo.circle.fill airplayvideo.badge.exclamationmark airplayaudio airplayaudio.circle airplayaudio.circle.fill airplayaudio.badge.exclamationmark dot.radiowaves.left.and.right dot.radiowaves.right dot.radiowaves.forward wave.3.left wave.3.left.circle wave.3.left.circle.fill wave.3.backward wave.3.backward.circle wave.3.backward.circle.fill wave.3.right wave.3.right.circle wave.3.right.circle.fill wave.3.forward wave.3.forward.circle wave.3.forward.circle.fill dot.radiowaves.up.forward antenna.radiowaves.left.and.right antenna.radiowaves.left.and.right.slash antenna.radiowaves.left.and.right.circle antenna.radiowaves.left.and.right.circle.fill pip pip.fill pip.exit pip.enter pip.swap pip.remove rectangle.arrowtriangle.2.outward rectangle.arrowtriangle.2.inward rectangle.portrait.arrowtriangle.2.outward rectangle.portrait.arrowtriangle.2.inward rectangle.2.swap guitars guitars.fill airplane airplane.circle airplane.circle.fill airplane.arrival airplane.departure car car.fill car.circle car.circle.fill bolt.car bolt.car.fill bolt.car.circle bolt.car.circle.fill car.2 car.2.fill bus bus.fill bus.doubledecker bus.doubledecker.fill tram tram.fill tram.circle tram.circle.fill tram.fill.tunnel cablecar cablecar.fill ferry ferry.fill car.ferry car.ferry.fill train.side.front.car train.side.middle.car train.side.rear.car bicycle bicycle.circle bicycle.circle.fill scooter parkingsign parkingsign.circle parkingsign.circle.fill fuelpump fuelpump.fill fuelpump.circle fuelpump.circle.fill fanblades fanblades.fill bed.double bed.double.fill bed.double.circle bed.double.circle.fill lungs lungs.fill allergens pills pills.fill pills.circle pills.circle.fill testtube.2 ivfluid.bag ivfluid.bag.fill cross.vial cross.vial.fill cross cross.fill cross.circle cross.circle.fill hare hare.fill tortoise tortoise.fill pawprint pawprint.fill pawprint.circle pawprint.circle.fill ant ant.fill ant.circle ant.circle.fill ladybug ladybug.fill leaf leaf.fill leaf.circle leaf.circle.fill leaf.arrow.triangle.circlepath film film.fill film.circle film.circle.fill sportscourt sportscourt.fill face.smiling face.smiling.fill face.dashed face.dashed.fill crown crown.fill comb comb.fill qrcode barcode viewfinder viewfinder.circle viewfinder.circle.fill barcode.viewfinder qrcode.viewfinder plus.viewfinder camera.viewfinder faceid doc.viewfinder doc.viewfinder.fill location.viewfinder location.fill.viewfinder person.fill.viewfinder text.viewfinder dot.viewfinder dot.circle.viewfinder photo photo.fill photo.circle photo.circle.fill text.below.photo text.below.photo.fill checkerboard.rectangle camera.metering.center.weighted.average camera.metering.center.weighted camera.metering.matrix camera.metering.multispot camera.metering.none camera.metering.partial camera.metering.spot camera.metering.unknown camera.aperture rectangle.dashed rectangle.dashed.badge.record rectangle.badge.plus rectangle.fill.badge.plus rectangle.badge.minus rectangle.fill.badge.minus rectangle.badge.checkmark rectangle.fill.badge.checkmark rectangle.badge.xmark rectangle.fill.badge.xmark rectangle.badge.person.crop rectangle.fill.badge.person.crop photo.on.rectangle photo.fill.on.rectangle.fill rectangle.on.rectangle.angled rectangle.fill.on.rectangle.angled.fill photo.on.rectangle.angled rectangle.stack rectangle.stack.fill rectangle.stack.badge.plus rectangle.stack.fill.badge.plus rectangle.stack.badge.minus rectangle.stack.fill.badge.minus rectangle.stack.badge.person.crop rectangle.stack.badge.person.crop.fill rectangle.stack.badge.play rectangle.stack.badge.play.fill sparkles.rectangle.stack sparkles.rectangle.stack.fill sidebar.left sidebar.right sidebar.leading sidebar.trailing sidebar.squares.left sidebar.squares.right sidebar.squares.leading sidebar.squares.trailing macwindow macwindow.badge.plus slider.horizontal.2.rectangle.and.arrow.triangle.2.circlepath dock.rectangle dock.arrow.up.rectangle dock.arrow.down.rectangle menubar.rectangle menubar.dock.rectangle menubar.dock.rectangle.badge.record menubar.arrow.up.rectangle menubar.arrow.down.rectangle macwindow.on.rectangle text.and.command.macwindow keyboard.macwindow uiwindow.split.2x1 mosaic mosaic.fill squares.below.rectangle rectangle.split.3x3.fill square.on.square.squareshape.controlhandles squareshape.controlhandles.on.squareshape.controlhandles pano pano.fill circle.grid.2x1 circle.grid.2x1.fill circle.grid.2x1.left.filled circle.grid.2x1.right.filled square.and.line.vertical.and.square square.fill.and.line.vertical.and.square.fill square.filled.and.line.vertical.and.square square.and.line.vertical.and.square.filled flowchart flowchart.fill rectangle.connected.to.line.below align.horizontal.left align.horizontal.left.fill align.horizontal.center align.horizontal.center.fill align.horizontal.right align.horizontal.right.fill align.vertical.top align.vertical.top.fill align.vertical.center align.vertical.center.fill align.vertical.bottom align.vertical.bottom.fill shield shield.fill shield.lefthalf.filled shield.righthalf.filled shield.slash shield.slash.fill shield.lefthalf.filled.slash checkerboard.shield switch.2 point.topleft.down.curvedto.point.bottomright.up point.topleft.down.curvedto.point.bottomright.up.fill point.topleft.down.curvedto.point.filled.bottomright.up point.filled.topleft.down.curvedto.point.bottomright.up app.connected.to.app.below.fill slider.horizontal.3 slider.horizontal.below.rectangle slider.horizontal.below.square.filled.and.square slider.vertical.3 cube cube.fill cube.transparent cube.transparent.fill shippingbox shippingbox.fill shippingbox.circle shippingbox.circle.fill arkit arkit.badge.xmark cone cone.fill pyramid pyramid.fill square.stack.3d.down.right square.stack.3d.down.right.fill square.stack.3d.down.forward square.stack.3d.down.forward.fill square.stack.3d.up square.stack.3d.up.fill square.stack.3d.up.slash square.stack.3d.up.slash.fill square.stack.3d.up.badge.a square.stack.3d.up.badge.a.fill square.stack.3d.forward.dottedline square.stack.3d.forward.dottedline.fill livephoto livephoto.slash livephoto.badge.a livephoto.play scope helm clock clock.fill clock.circle clock.circle.fill clock.badge.checkmark clock.badge.checkmark.fill clock.badge.exclamationmark clock.badge.exclamationmark.fill deskclock deskclock.fill alarm alarm.fill stopwatch stopwatch.fill chart.xyaxis.line timer timer.square clock.arrow.circlepath exclamationmark.arrow.circlepath clock.arrow.2.circlepath gamecontroller gamecontroller.fill l.joystick l.joystick.fill r.joystick r.joystick.fill l.joystick.press.down l.joystick.press.down.fill r.joystick.press.down r.joystick.press.down.fill l.joystick.tilt.left l.joystick.tilt.left.fill l.joystick.tilt.right l.joystick.tilt.right.fill l.joystick.tilt.up l.joystick.tilt.up.fill l.joystick.tilt.down l.joystick.tilt.down.fill r.joystick.tilt.left r.joystick.tilt.left.fill r.joystick.tilt.right r.joystick.tilt.right.fill r.joystick.tilt.up r.joystick.tilt.up.fill r.joystick.tilt.down r.joystick.tilt.down.fill dpad dpad.fill dpad.left.filled dpad.up.filled dpad.right.filled dpad.down.filled circle.circle circle.circle.fill square.circle square.circle.fill triangle.circle triangle.circle.fill rectangle.roundedtop rectangle.roundedtop.fill rectangle.roundedbottom rectangle.roundedbottom.fill l.rectangle.roundedbottom l.rectangle.roundedbottom.fill l1.rectangle.roundedbottom l1.rectangle.roundedbottom.fill l2.rectangle.roundedtop l2.rectangle.roundedtop.fill r.rectangle.roundedbottom r.rectangle.roundedbottom.fill r1.rectangle.roundedbottom r1.rectangle.roundedbottom.fill r2.rectangle.roundedtop r2.rectangle.roundedtop.fill lb.rectangle.roundedbottom lb.rectangle.roundedbottom.fill rb.rectangle.roundedbottom rb.rectangle.roundedbottom.fill lt.rectangle.roundedtop lt.rectangle.roundedtop.fill rt.rectangle.roundedtop rt.rectangle.roundedtop.fill zl.rectangle.roundedtop zl.rectangle.roundedtop.fill zr.rectangle.roundedtop zr.rectangle.roundedtop.fill logo.playstation logo.xbox paintpalette paintpalette.fill cup.and.saucer cup.and.saucer.fill takeoutbag.and.cup.and.straw takeoutbag.and.cup.and.straw.fill fork.knife fork.knife.circle fork.knife.circle.fill figure.walk figure.walk.circle figure.walk.circle.fill figure.walk.diamond figure.walk.diamond.fill figure.stand figure.stand.line.dotted.figure.stand figure.wave figure.wave.circle figure.wave.circle.fill figure.roll ear ear.badge.checkmark ear.trianglebadge.exclamationmark ear.and.waveform ear.fill hearingdevice.ear hand.raised hand.raised.fill hand.raised.circle hand.raised.circle.fill hand.raised.square hand.raised.square.fill hand.raised.slash hand.raised.slash.fill hand.thumbsup hand.thumbsup.fill hand.thumbsup.circle hand.thumbsup.circle.fill hand.thumbsdown hand.thumbsdown.fill hand.thumbsdown.circle hand.thumbsdown.circle.fill hand.point.up.left hand.point.up.left.fill hand.draw hand.draw.fill hand.tap hand.tap.fill rectangle.and.hand.point.up.left rectangle.and.hand.point.up.left.fill rectangle.filled.and.hand.point.up.left rectangle.and.hand.point.up.left.filled hand.point.left hand.point.left.fill hand.point.right hand.point.right.fill hand.point.up hand.point.up.fill hand.point.up.braille hand.point.up.braille.fill hand.point.down hand.point.down.fill hand.wave hand.wave.fill hands.clap hands.clap.fill hands.sparkles hands.sparkles.fill rectangle.compress.vertical rectangle.expand.vertical rectangle.and.arrow.up.right.and.arrow.down.left rectangle.and.arrow.up.right.and.arrow.down.left.slash square.2.stack.3d square.2.stack.3d.top.filled square.2.stack.3d.bottom.filled square.3.layers.3d.down.right square.3.layers.3d.down.right.slash square.3.layers.3d.down.left square.3.layers.3d.down.left.slash square.3.layers.3d.down.forward square.3.layers.3d.down.backward square.3.stack.3d square.3.stack.3d.slash square.3.stack.3d.top.filled square.3.stack.3d.middle.filled square.3.stack.3d.bottom.filled cylinder cylinder.fill cylinder.split.1x2 cylinder.split.1x2.fill chart.bar chart.bar.fill chart.pie chart.pie.fill chart.bar.xaxis chart.line.uptrend.xyaxis chart.line.uptrend.xyaxis.circle chart.line.uptrend.xyaxis.circle.fill dot.squareshape.split.2x2 squareshape.split.2x2.dotted squareshape.split.2x2 squareshape.split.3x3 burst burst.fill waveform.path.ecg waveform.path.ecg.rectangle waveform.path.ecg.rectangle.fill waveform.path waveform.path.badge.plus waveform.path.badge.minus point.3.connected.trianglepath.dotted point.3.filled.connected.trianglepath.dotted waveform waveform.circle waveform.circle.fill waveform.badge.plus waveform.badge.minus waveform.badge.exclamationmark waveform.and.magnifyingglass waveform.and.mic staroflife staroflife.fill staroflife.circle staroflife.circle.fill simcard simcard.fill simcard.2 simcard.2.fill sdcard sdcard.fill esim esim.fill touchid bonjour atom scalemass scalemass.fill gift gift.fill gift.circle gift.circle.fill plus.app plus.app.fill arrow.down.app arrow.down.app.fill arrow.up.forward.app arrow.up.forward.app.fill xmark.app xmark.app.fill questionmark.app questionmark.app.fill app.badge app.badge.fill app.badge.checkmark app.badge.checkmark.fill app.dashed questionmark.app.dashed appclip app.gift app.gift.fill studentdesk hourglass hourglass.circle hourglass.circle.fill hourglass.badge.plus hourglass.bottomhalf.filled hourglass.tophalf.filled banknote banknote.fill paragraphsign purchased purchased.circle purchased.circle.fill perspective circle.and.line.horizontal circle.and.line.horizontal.fill trapezoid.and.line.vertical trapezoid.and.line.vertical.fill trapezoid.and.line.horizontal trapezoid.and.line.horizontal.fill aspectratio aspectratio.fill camera.filters skew arrow.left.and.right.righttriangle.left.righttriangle.right arrow.left.and.right.righttriangle.left.righttriangle.right.fill arrow.up.and.down.righttriangle.up.righttriangle.down arrow.up.and.down.righttriangle.up.righttriangle.down.fill arrowtriangle.left.and.line.vertical.and.arrowtriangle.right arrowtriangle.left.and.line.vertical.and.arrowtriangle.right.fill arrowtriangle.right.and.line.vertical.and.arrowtriangle.left arrowtriangle.right.and.line.vertical.and.arrowtriangle.left.fill grid grid.circle grid.circle.fill burn lifepreserver lifepreserver.fill recordingtape binoculars binoculars.fill battery.100 battery.75 battery.50 battery.25 battery.0 battery.100.bolt minus.plus.batteryblock minus.plus.batteryblock.fill bolt.batteryblock bolt.batteryblock.fill lightbulb lightbulb.fill lightbulb.circle lightbulb.circle.fill lightbulb.slash lightbulb.slash.fill fibrechannel checklist square.fill.text.grid.1x2 list.dash list.bullet list.bullet.circle list.bullet.circle.fill list.triangle list.bullet.indent list.number list.star increase.indent decrease.indent decrease.quotelevel increase.quotelevel list.bullet.below.rectangle text.badge.plus text.badge.minus text.badge.checkmark text.badge.xmark text.badge.star text.insert text.append text.quote text.alignleft text.aligncenter text.alignright text.justify text.justify.left text.justify.right text.justify.leading text.justify.trailing text.redaction list.and.film line.3.horizontal line.3.horizontal.decrease line.3.horizontal.decrease.circle line.3.horizontal.decrease.circle.fill line.3.horizontal.circle line.3.horizontal.circle.fill line.2.horizontal.decrease.circle line.2.horizontal.decrease.circle.fill character textformat.size.smaller textformat.size.larger textformat.size textformat textformat.alt textformat.superscript textformat.subscript abc textformat.abc textformat.abc.dottedunderline bold italic underline strikethrough shadow bold.italic.underline bold.underline view.2d view.3d character.cursor.ibeam fx f.cursive f.cursive.circle f.cursive.circle.fill k sum percent function fn textformat.123 123.rectangle 123.rectangle.fill character.textbox a.magnify info info.circle info.circle.fill at at.circle at.circle.fill at.badge.plus at.badge.minus questionmark questionmark.circle questionmark.circle.fill questionmark.square questionmark.square.fill questionmark.diamond questionmark.diamond.fill exclamationmark exclamationmark.2 exclamationmark.3 exclamationmark.circle exclamationmark.circle.fill exclamationmark.square exclamationmark.square.fill exclamationmark.octagon exclamationmark.octagon.fill exclamationmark.shield exclamationmark.shield.fill plus plus.circle plus.circle.fill plus.square plus.square.fill plus.rectangle plus.rectangle.fill plus.rectangle.portrait plus.rectangle.portrait.fill plus.diamond plus.diamond.fill minus minus.circle minus.circle.fill minus.square minus.square.fill minus.rectangle minus.rectangle.fill minus.rectangle.portrait minus.rectangle.portrait.fill minus.diamond minus.diamond.fill plusminus plusminus.circle plusminus.circle.fill plus.forwardslash.minus minus.forwardslash.plus multiply multiply.circle multiply.circle.fill multiply.square multiply.square.fill xmark.rectangle xmark.rectangle.fill xmark.rectangle.portrait xmark.rectangle.portrait.fill xmark.diamond xmark.diamond.fill xmark.shield xmark.shield.fill xmark.octagon xmark.octagon.fill divide divide.circle divide.circle.fill divide.square divide.square.fill equal equal.circle equal.circle.fill equal.square equal.square.fill lessthan lessthan.circle lessthan.circle.fill lessthan.square lessthan.square.fill greaterthan greaterthan.circle greaterthan.circle.fill greaterthan.square greaterthan.square.fill chevron.left.forwardslash.chevron.right parentheses curlybraces curlybraces.square curlybraces.square.fill ellipsis.curlybraces number number.circle number.circle.fill number.square number.square.fill x.squareroot xmark xmark.circle xmark.circle.fill xmark.square xmark.square.fill checkmark checkmark.circle checkmark.circle.fill checkmark.circle.trianglebadge.exclamationmark checkmark.square checkmark.square.fill checkmark.rectangle checkmark.rectangle.fill checkmark.rectangle.portrait checkmark.rectangle.portrait.fill checkmark.diamond checkmark.diamond.fill checkmark.shield checkmark.shield.fill chevron.left chevron.left.circle chevron.left.circle.fill chevron.left.square chevron.left.square.fill chevron.backward chevron.backward.circle chevron.backward.circle.fill chevron.backward.square chevron.backward.square.fill chevron.right chevron.right.circle chevron.right.circle.fill chevron.right.square chevron.right.square.fill chevron.forward chevron.forward.circle chevron.forward.circle.fill chevron.forward.square chevron.forward.square.fill chevron.left.2 chevron.backward.2 chevron.right.2 chevron.forward.2 chevron.up chevron.up.circle chevron.up.circle.fill chevron.up.square chevron.up.square.fill chevron.down chevron.down.circle chevron.down.circle.fill chevron.down.square chevron.down.square.fill control projective chevron.up.chevron.down chevron.compact.up chevron.compact.down chevron.compact.left chevron.compact.right arrow.left arrow.left.circle arrow.left.circle.fill arrow.left.square arrow.left.square.fill arrow.backward arrow.backward.circle arrow.backward.circle.fill arrow.backward.square arrow.backward.square.fill arrow.right arrow.right.circle arrow.right.circle.fill arrow.right.square arrow.right.square.fill arrow.forward arrow.forward.circle arrow.forward.circle.fill arrow.forward.square arrow.forward.square.fill arrow.up arrow.up.circle arrow.up.circle.fill arrow.up.square arrow.up.square.fill arrow.down arrow.down.circle arrow.down.circle.fill arrow.down.square arrow.down.square.fill arrow.up.left arrow.up.left.circle arrow.up.left.circle.fill arrow.up.left.square arrow.up.left.square.fill arrow.up.backward arrow.up.backward.circle arrow.up.backward.circle.fill arrow.up.backward.square arrow.up.backward.square.fill arrow.up.right arrow.up.right.circle arrow.up.right.circle.fill arrow.up.right.square arrow.up.right.square.fill arrow.up.forward arrow.up.forward.circle arrow.up.forward.circle.fill arrow.up.forward.square arrow.up.forward.square.fill arrow.down.left arrow.down.left.circle arrow.down.left.circle.fill arrow.down.left.square arrow.down.left.square.fill arrow.down.backward arrow.down.backward.circle arrow.down.backward.circle.fill arrow.down.backward.square arrow.down.backward.square.fill arrow.down.right arrow.down.right.circle arrow.down.right.circle.fill arrow.down.right.square arrow.down.right.square.fill arrow.down.forward arrow.down.forward.circle arrow.down.forward.circle.fill arrow.down.forward.square arrow.down.forward.square.fill arrow.left.arrow.right arrow.left.arrow.right.circle arrow.left.arrow.right.circle.fill arrow.left.arrow.right.square arrow.left.arrow.right.square.fill arrow.up.arrow.down arrow.up.arrow.down.circle arrow.up.arrow.down.circle.fill arrow.up.arrow.down.square arrow.up.arrow.down.square.fill arrow.turn.down.left arrow.turn.up.left arrow.turn.down.right arrow.turn.up.right arrow.turn.right.up arrow.turn.left.up arrow.turn.right.down arrow.turn.left.down arrow.uturn.left arrow.uturn.left.circle arrow.uturn.left.circle.fill arrow.uturn.left.circle.badge.ellipsis arrow.uturn.left.square arrow.uturn.left.square.fill arrow.uturn.backward arrow.uturn.backward.circle arrow.uturn.backward.circle.fill arrow.uturn.backward.circle.badge.ellipsis arrow.uturn.backward.square arrow.uturn.backward.square.fill arrow.uturn.right arrow.uturn.right.circle arrow.uturn.right.circle.fill arrow.uturn.right.square arrow.uturn.right.square.fill arrow.uturn.forward arrow.uturn.forward.circle arrow.uturn.forward.circle.fill arrow.uturn.forward.square arrow.uturn.forward.square.fill arrow.uturn.up arrow.uturn.up.circle arrow.uturn.up.circle.fill arrow.uturn.up.square arrow.uturn.up.square.fill arrow.uturn.down arrow.uturn.down.circle arrow.uturn.down.circle.fill arrow.uturn.down.square arrow.uturn.down.square.fill arrow.up.and.down.and.arrow.left.and.right arrow.up.left.and.down.right.and.arrow.up.right.and.down.left arrow.left.and.right arrow.left.and.right.circle arrow.left.and.right.circle.fill arrow.left.and.right.square arrow.left.and.right.square.fill arrow.up.and.down arrow.up.and.down.circle arrow.up.and.down.circle.fill arrow.up.and.down.square arrow.up.and.down.square.fill arrow.up.to.line arrow.up.to.line.compact arrow.up.to.line.circle arrow.up.to.line.circle.fill arrow.down.to.line arrow.down.to.line.compact arrow.down.to.line.circle arrow.down.to.line.circle.fill arrow.left.to.line arrow.left.to.line.compact arrow.left.to.line.circle arrow.left.to.line.circle.fill arrow.backward.to.line arrow.backward.to.line.circle arrow.backward.to.line.circle.fill arrow.right.to.line arrow.right.to.line.compact arrow.right.to.line.circle arrow.right.to.line.circle.fill arrow.forward.to.line arrow.forward.to.line.circle arrow.forward.to.line.circle.fill arrow.clockwise arrow.clockwise.circle arrow.clockwise.circle.fill arrow.counterclockwise arrow.counterclockwise.circle arrow.counterclockwise.circle.fill arrow.up.left.and.arrow.down.right arrow.up.left.and.arrow.down.right.circle arrow.up.left.and.arrow.down.right.circle.fill arrow.up.backward.and.arrow.down.forward arrow.up.backward.and.arrow.down.forward.circle arrow.up.backward.and.arrow.down.forward.circle.fill arrow.down.right.and.arrow.up.left arrow.down.right.and.arrow.up.left.circle arrow.down.right.and.arrow.up.left.circle.fill arrow.down.forward.and.arrow.up.backward arrow.down.forward.and.arrow.up.backward.circle arrow.down.forward.and.arrow.up.backward.circle.fill return return.left return.right arrow.2.squarepath arrow.triangle.2.circlepath arrow.triangle.2.circlepath.circle arrow.triangle.2.circlepath.circle.fill exclamationmark.arrow.triangle.2.circlepath arrow.triangle.capsulepath arrow.3.trianglepath arrow.triangle.turn.up.right.diamond arrow.triangle.turn.up.right.diamond.fill arrow.triangle.turn.up.right.circle arrow.triangle.turn.up.right.circle.fill arrow.triangle.merge arrow.triangle.swap arrow.triangle.branch arrow.triangle.pull arrowtriangle.left arrowtriangle.left.fill arrowtriangle.left.circle arrowtriangle.left.circle.fill arrowtriangle.left.square arrowtriangle.left.square.fill arrowtriangle.backward arrowtriangle.backward.fill arrowtriangle.backward.circle arrowtriangle.backward.circle.fill arrowtriangle.backward.square arrowtriangle.backward.square.fill arrowtriangle.right arrowtriangle.right.fill arrowtriangle.right.circle arrowtriangle.right.circle.fill arrowtriangle.right.square arrowtriangle.right.square.fill arrowtriangle.forward arrowtriangle.forward.fill arrowtriangle.forward.circle arrowtriangle.forward.circle.fill arrowtriangle.forward.square arrowtriangle.forward.square.fill arrowtriangle.up arrowtriangle.up.fill arrowtriangle.up.circle arrowtriangle.up.circle.fill arrowtriangle.up.square arrowtriangle.up.square.fill arrowtriangle.down arrowtriangle.down.fill arrowtriangle.down.circle arrowtriangle.down.circle.fill arrowtriangle.down.square arrowtriangle.down.square.fill slash.circle slash.circle.fill asterisk asterisk.circle asterisk.circle.fill a.circle a.circle.fill a.square a.square.fill b.circle b.circle.fill b.square b.square.fill c.circle c.circle.fill c.square c.square.fill d.circle d.circle.fill d.square d.square.fill e.circle e.circle.fill e.square e.square.fill f.circle f.circle.fill f.square f.square.fill g.circle g.circle.fill g.square g.square.fill h.circle h.circle.fill h.square h.square.fill i.circle i.circle.fill i.square i.square.fill j.circle j.circle.fill j.square j.square.fill k.circle k.circle.fill k.square k.square.fill l.circle l.circle.fill l.square l.square.fill m.circle m.circle.fill m.square m.square.fill n.circle n.circle.fill n.square n.square.fill o.circle o.circle.fill o.square o.square.fill p.circle p.circle.fill p.square p.square.fill q.circle q.circle.fill q.square q.square.fill r.circle r.circle.fill r.square r.square.fill s.circle s.circle.fill s.square s.square.fill t.circle t.circle.fill t.square t.square.fill u.circle u.circle.fill u.square u.square.fill v.circle v.circle.fill v.square v.square.fill w.circle w.circle.fill w.square w.square.fill x.circle x.circle.fill x.square x.square.fill y.circle y.circle.fill y.square y.square.fill z.circle z.circle.fill z.square z.square.fill dollarsign.circle dollarsign.circle.fill dollarsign.square dollarsign.square.fill centsign.circle centsign.circle.fill centsign.square centsign.square.fill yensign.circle yensign.circle.fill yensign.square yensign.square.fill sterlingsign.circle sterlingsign.circle.fill sterlingsign.square sterlingsign.square.fill francsign.circle francsign.circle.fill francsign.square francsign.square.fill florinsign.circle florinsign.circle.fill florinsign.square florinsign.square.fill turkishlirasign.circle turkishlirasign.circle.fill turkishlirasign.square turkishlirasign.square.fill rublesign.circle rublesign.circle.fill rublesign.square rublesign.square.fill eurosign.circle eurosign.circle.fill eurosign.square eurosign.square.fill dongsign.circle dongsign.circle.fill dongsign.square dongsign.square.fill indianrupeesign.circle indianrupeesign.circle.fill indianrupeesign.square indianrupeesign.square.fill tengesign.circle tengesign.circle.fill tengesign.square tengesign.square.fill pesetasign.circle pesetasign.circle.fill pesetasign.square pesetasign.square.fill pesosign.circle pesosign.circle.fill pesosign.square pesosign.square.fill kipsign.circle kipsign.circle.fill kipsign.square kipsign.square.fill wonsign.circle wonsign.circle.fill wonsign.square wonsign.square.fill lirasign.circle lirasign.circle.fill lirasign.square lirasign.square.fill australsign.circle australsign.circle.fill australsign.square australsign.square.fill hryvniasign.circle hryvniasign.circle.fill hryvniasign.square hryvniasign.square.fill nairasign.circle nairasign.circle.fill nairasign.square nairasign.square.fill guaranisign.circle guaranisign.circle.fill guaranisign.square guaranisign.square.fill coloncurrencysign.circle coloncurrencysign.circle.fill coloncurrencysign.square coloncurrencysign.square.fill cedisign.circle cedisign.circle.fill cedisign.square cedisign.square.fill cruzeirosign.circle cruzeirosign.circle.fill cruzeirosign.square cruzeirosign.square.fill tugriksign.circle tugriksign.circle.fill tugriksign.square tugriksign.square.fill millsign.circle millsign.circle.fill millsign.square millsign.square.fill shekelsign.circle shekelsign.circle.fill shekelsign.square shekelsign.square.fill manatsign.circle manatsign.circle.fill manatsign.square manatsign.square.fill rupeesign.circle rupeesign.circle.fill rupeesign.square rupeesign.square.fill bahtsign.circle bahtsign.circle.fill bahtsign.square bahtsign.square.fill larisign.circle larisign.circle.fill larisign.square larisign.square.fill bitcoinsign.circle bitcoinsign.circle.fill bitcoinsign.square bitcoinsign.square.fill brazilianrealsign.circle brazilianrealsign.circle.fill brazilianrealsign.square brazilianrealsign.square.fill 0.circle 0.circle.fill 0.square 0.square.fill 1.circle 1.circle.fill 1.square 1.square.fill 2.circle 2.circle.fill 2.square 2.square.fill 3.circle 3.circle.fill 3.square 3.square.fill 4.circle 4.circle.fill 4.square 4.square.fill 4.alt.circle 4.alt.circle.fill 4.alt.square 4.alt.square.fill 5.circle 5.circle.fill 5.square 5.square.fill 6.circle 6.circle.fill 6.square 6.square.fill 6.alt.circle 6.alt.circle.fill 6.alt.square 6.alt.square.fill 7.circle 7.circle.fill 7.square 7.square.fill 8.circle 8.circle.fill 8.square 8.square.fill 9.circle 9.circle.fill 9.square 9.square.fill 9.alt.circle 9.alt.circle.fill 9.alt.square 9.alt.square.fill 00.circle 00.circle.fill 00.square 00.square.fill 01.circle 01.circle.fill 01.square 01.square.fill 02.circle 02.circle.fill 02.square 02.square.fill 03.circle 03.circle.fill 03.square 03.square.fill 04.circle 04.circle.fill 04.square 04.square.fill 05.circle 05.circle.fill 05.square 05.square.fill 06.circle 06.circle.fill 06.square 06.square.fill 07.circle 07.circle.fill 07.square 07.square.fill 08.circle 08.circle.fill 08.square 08.square.fill 09.circle 09.circle.fill 09.square 09.square.fill 10.circle 10.circle.fill 10.square 10.square.fill 11.circle 11.circle.fill 11.square 11.square.fill 12.circle 12.circle.fill 12.square 12.square.fill 13.circle 13.circle.fill 13.square 13.square.fill 14.circle 14.circle.fill 14.square 14.square.fill 15.circle 15.circle.fill 15.square 15.square.fill 16.circle 16.circle.fill 16.square 16.square.fill 17.circle 17.circle.fill 17.square 17.square.fill 18.circle 18.circle.fill 18.square 18.square.fill 19.circle 19.circle.fill 19.square 19.square.fill 20.circle 20.circle.fill 20.square 20.square.fill 21.circle 21.circle.fill 21.square 21.square.fill 22.circle 22.circle.fill 22.square 22.square.fill 23.circle 23.circle.fill 23.square 23.square.fill 24.circle 24.circle.fill 24.square 24.square.fill 25.circle 25.circle.fill 25.square 25.square.fill 26.circle 26.circle.fill 26.square 26.square.fill 27.circle 27.circle.fill 27.square 27.square.fill 28.circle 28.circle.fill 28.square 28.square.fill 29.circle 29.circle.fill 29.square 29.square.fill 30.circle 30.circle.fill 30.square 30.square.fill 31.circle 31.circle.fill 31.square 31.square.fill 32.circle 32.circle.fill 32.square 32.square.fill 33.circle 33.circle.fill 33.square 33.square.fill 34.circle 34.circle.fill 34.square 34.square.fill 35.circle 35.circle.fill 35.square 35.square.fill 36.circle 36.circle.fill 36.square 36.square.fill 37.circle 37.circle.fill 37.square 37.square.fill 38.circle 38.circle.fill 38.square 38.square.fill 39.circle 39.circle.fill 39.square 39.square.fill 40.circle 40.circle.fill 40.square 40.square.fill 41.circle 41.circle.fill 41.square 41.square.fill 42.circle 42.circle.fill 42.square 42.square.fill 43.circle 43.circle.fill 43.square 43.square.fill 44.circle 44.circle.fill 44.square 44.square.fill 45.circle 45.circle.fill 45.square 45.square.fill 46.circle 46.circle.fill 46.square 46.square.fill 47.circle 47.circle.fill 47.square 47.square.fill 48.circle 48.circle.fill 48.square 48.square.fill 49.circle 49.circle.fill 49.square 49.square.fill 50.circle 50.circle.fill 50.square 50.square.fill applelogo ================================================ FILE: External/SymbolPicker/Sources/SymbolPicker/Resources/zh_CN.lproj/Localizable.strings ================================================ "search_placeholder" = "搜索"; "cancel" = "取消"; "sf_symbol_picker" = "选择符号"; "done" = "确定"; ================================================ FILE: External/SymbolPicker/Sources/SymbolPicker/SymbolPicker.swift ================================================ // // SymbolPicker.swift // SymbolPicker // // Created by Yubo Qin on 2/14/22. // import SwiftUI #if os(macOS) import AppKit typealias PlatformColor = NSColor #else import UIKit typealias PlatformColor = UIColor #endif public struct SymbolPicker: View { // MARK: - Static consts private static let symbols: [String] = { guard let path = Bundle.module.path(forResource: "sfsymbols", ofType: "txt"), let content = try? String(contentsOfFile: path) else { return [] } return content .split(separator: "\n") .map { String($0) } }() private static var gridDimension: CGFloat { #if os(iOS) return 64 #elseif os(tvOS) return 128 #elseif os(macOS) return 30 #else return 48 #endif } private static var symbolSize: CGFloat { #if os(iOS) return 24 #elseif os(tvOS) return 48 #elseif os(macOS) return 14 #else return 24 #endif } private static var symbolCornerRadius: CGFloat { #if os(iOS) return 8 #elseif os(tvOS) return 12 #elseif os(macOS) return 4 #else return 8 #endif } private static var systemGray5: Color { dynamicColor( light: .init(red: 0.9, green: 0.9, blue: 0.92, alpha: 1.0), dark: .init(red: 0.17, green: 0.17, blue: 0.18, alpha: 1.0) ) } private static var systemBackground: Color { dynamicColor( light: .init(red: 1, green: 1, blue: 1, alpha: 1.0), dark: .init(red: 0, green: 0, blue: 0, alpha: 1.0) ) } private static var secondarySystemBackground: Color { dynamicColor( light: .init(red: 0.95, green: 0.95, blue: 1, alpha: 1.0), dark: .init(red: 0, green: 0, blue: 0, alpha: 1.0) ) } // MARK: - Properties @Binding public var symbol: String @State private var searchText = "" @Environment(\.presentationMode) private var presentationMode // MARK: - Public Init public init(symbol: Binding) { _symbol = symbol } // MARK: - View Components @ViewBuilder private var searchableSymbolGrid: some View { #if os(iOS) if #available(iOS 15.0, *) { symbolGrid .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always)) } else { VStack { TextField(LocalizedString("search_placeholder"), text: $searchText) .padding(8) .padding(.horizontal, 8) .background(Self.systemGray5) .cornerRadius(8.0) .padding(.horizontal, 16.0) .autocapitalization(.none) .disableAutocorrection(true) symbolGrid .padding() } } #elseif os(tvOS) symbolGrid .searchable(text: $searchText, placement: .automatic) #elseif os(macOS) VStack(spacing: 10) { TextField(LocalizedString("search_placeholder"), text: $searchText) .disableAutocorrection(true) symbolGrid } #else symbolGrid .searchable(text: $searchText, placement: .automatic) #endif } private var symbolGrid: some View { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: Self.gridDimension, maximum: Self.gridDimension))]) { ForEach(Self.symbols.filter { searchText.isEmpty ? true : $0.localizedCaseInsensitiveContains(searchText) }, id: \.self) { thisSymbol in Button(action: { symbol = thisSymbol // Dismiss sheet. macOS will have done button #if !os(macOS) presentationMode.wrappedValue.dismiss() #endif }) { if thisSymbol == symbol { Image(systemName: thisSymbol) .font(.system(size: Self.symbolSize)) .frame(maxWidth: .infinity, minHeight: Self.gridDimension) #if !os(tvOS) .background(Color.accentColor) #else .background(Color.gray.opacity(0.3)) #endif .cornerRadius(Self.symbolCornerRadius) .foregroundColor(.white) } else { Image(systemName: thisSymbol) .font(.system(size: Self.symbolSize)) .frame(maxWidth: .infinity, minHeight: Self.gridDimension) .background(Self.systemBackground) .cornerRadius(Self.symbolCornerRadius) .foregroundColor(.primary) } } .buttonStyle(PlainButtonStyle()) } } } } public var body: some View { #if !os(macOS) NavigationView { ZStack { Self.secondarySystemBackground.edgesIgnoringSafeArea(.all) searchableSymbolGrid } #if os(iOS) .navigationBarTitleDisplayMode(.inline) #endif .toolbar { ToolbarItem(placement: .cancellationAction) { Button(LocalizedString("cancel")) { presentationMode.wrappedValue.dismiss() } } } } .navigationViewStyle(StackNavigationViewStyle()) #else VStack(alignment: .leading, spacing: 10) { Text(LocalizedString("sf_symbol_picker")) .font(.headline) Divider() searchableSymbolGrid .frame(maxWidth: .infinity, maxHeight: .infinity) Divider() HStack { Button { symbol = "" presentationMode.wrappedValue.dismiss() } label: { Text(LocalizedString("cancel")) } .keyboardShortcut(.cancelAction) Spacer() Button { presentationMode.wrappedValue.dismiss() } label: { Text(LocalizedString("done")) } } } .padding() .frame(width: 520, height: 300, alignment: .center) #endif } // MARK: - Private helpers private static func dynamicColor(light: PlatformColor, dark: PlatformColor) -> Color { #if os(iOS) let color = PlatformColor { $0.userInterfaceStyle == .dark ? dark : light } if #available(iOS 15.0, *) { return Color(uiColor: color) } else { return Color(color) } #elseif os(tvOS) let color = PlatformColor { $0.userInterfaceStyle == .dark ? dark : light } return Color(uiColor: color) #elseif os(macOS) let color = PlatformColor(name: nil) { $0.name == .darkAqua ? dark : light } if #available(macOS 12.0, *) { return Color(nsColor: color) } else { return Color(color) } #else return Color(uiColor: dark) #endif } } private func LocalizedString(_ key: String) -> String { NSLocalizedString(key, bundle: .module, comment: "") } struct SymbolPicker_Previews: PreviewProvider { @State static var symbol: String = "square.and.arrow.up" static var previews: some View { Group { SymbolPicker(symbol: Self.$symbol) SymbolPicker(symbol: Self.$symbol) .preferredColorScheme(.dark) } } } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Lakr Aream Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ActionBee ActionBee is a programmable pasteboard action trigger. ![Screenshot](./Resources/Demo/ScreenshotMain.png) ![Screenshot](./Resources/Demo/ScreenshotModule.png) ## Preview Video It can be used to clean your URL in text. To see code or import this module, check out [here](./Resources/ModuleSample/Module%20Export%20-%20Link%20Cleaner/). ![Clean URL](./Resources/Demo/Demo%20Link%20Cleaner.gif) With Universal Control and iCloud Pasteboard, link cleaner will work across devices. ![Clean URL Across Devices](./Resources/Demo/Demo%20Link%20Cleaner%20Across%20Devices.gif) It can also be used to run the formator as follows. To see code or import this module, check out [here](./Resources/ModuleSample/Module%20Export%20-%20Multiline%20Init%20Formatter/). ![Format Code](./Resources/Demo/Demo%20Multiline%20Formatter.gif) ## Action Module We encourage you to develop an action module yourself. To see how a module gets to work, create one and check out the source. It is well documented inside the code. Generically speaking, we pass your pasteboard data in the process's environment and spawn your module that is compiled as binary. At the end, we read back a recipe from stdout where you can define what to do. ## Warning This app itself does not require access to your data or an internet connection, but the module you install may do. We do **not** use any sandbox to protect your data or privacy. **Only import modules come with source code and exam each line yourself before compiling or executing them.** ## Contributor Made with love by [@Lakr233](https://twitter.com/@Lakr233), thanks to all my lovely friends for testing and being with me day and night. ## One More Thing If you are looking for an Android alternative (which was made for cleaning URL), checkout [Tarnhelm](https://github.com/lz233/Tarnhelm) by [@lz__233](https://twitter.com/lz__233). If you are looking for a great pasteboard manager, checkout [PasteNow](https://apps.apple.com/cn/app/pastenow-instant-clipboard/id1552536109) by [@tualatrix](https://twitter.com/tualatrix). I also recommend [Raycast](https://www.raycast.com/)'s built-in pasteboard manager if you are not a heavy user of it. ## License Generically speaking, we do not hold responsibility for anything that happened on your computer, nor your data or privacy. [License](./LICENSE) --- Copyright © 2022 Lakr Aream. All Rights Reserved. ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Binary/CommandLineBridge/CommandLineBridge/main.swift ================================================ // // main.swift // CommandLineBridge // // Created by Lakr Aream on 2022/8/13. // import Communicator import Definition import Foundation import Source guard let data = Communicator.retrieveParentData() else { fatalError("unable to receive argument data") } guard let argument = ArgumentData.retrieve(withData: data) else { fatalError("unable to receive argument object") } private let defaultRecipe: RecipeData = .init( postAction: .none, postContent: "", continueQueue: true ) let completion: ((RecipeData?) -> Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { [[NSBundle.mainBundle infoDictionary] setValue: @{ @"NSAllowsArbitraryLoads" : @YES } forKey:@"NSAppTransportSecurity"]; NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/compile.sh ================================================ #!/bin/bash # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then exit 1 fi echo "[*] cleaning build..." rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" \ 2>/dev/null | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" \ 1>/dev/null 2>/dev/null # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null echo "[+] completed compile" ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/.supplement/launch.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")"/../ .build/cli ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/Sources/Source/CleanerRule/CleanerRule.swift ================================================ // // DomainRule.swift // // // Created by Lakr Aream on 2022/8/17. // import Foundation let cleaners: [CleanerRule] = [ Twitter(), BiliBili(), B23TV(), ] protocol CleanerRule { func isPotentialCandidate(original url: URL) -> Bool func process(original url: URL) -> URL? } extension URL { func deletingAllQueryParameters() -> URL? { guard var components = URLComponents( url: self, resolvingAgainstBaseURL: false ) else { return nil } components.queryItems = [] guard let newUrl = components.url else { return nil } var str = newUrl.absoluteString if str.hasSuffix("?") { str.removeLast() } return URL(string: str) } } ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/Sources/Source/CleanerRule/Rule+BiliBili.swift ================================================ // // File.swift // // // Created by Lakr Aream on 2022/8/18. // import Foundation class BiliBili: CleanerRule { func isPotentialCandidate(original url: URL) -> Bool { url.host?.lowercased().contains("www.bilibili.com") ?? false } func process(original url: URL) -> URL? { guard url.deletingLastPathComponent().lastPathComponent == "video", let result = url.deletingAllQueryParameters() else { return nil } return result } } class B23TV: CleanerRule { func isPotentialCandidate(original url: URL) -> Bool { url.host?.lowercased().contains("b23.tv") ?? false } func process(original url: URL) -> URL? { var comps = URLComponents(url: url, resolvingAgainstBaseURL: false) // comps?.scheme = "https" guard let requestUrl = comps?.url else { return nil } let sem = DispatchSemaphore(value: 0) var cleanResult: URL? DispatchQueue.global().async { var request = URLRequest( url: requestUrl, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 6 ) request.addValue( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", forHTTPHeaderField: "user-agent" ) URLSession.shared.dataTask(with: request) { _, resp, _ in defer { sem.signal() } guard let resp = resp as? HTTPURLResponse else { return } cleanResult = resp.url?.deletingAllQueryParameters() } .resume() } sem.wait() return cleanResult } } ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/Sources/Source/CleanerRule/Rule+Twitter.swift ================================================ // // CleanerRule+Twitter.swift // // // Created by Lakr Aream on 2022/8/17. // import Foundation class Twitter: CleanerRule { func isPotentialCandidate(original url: URL) -> Bool { url.host?.lowercased().contains("twitter.com") ?? false } func process(original url: URL) -> URL? { guard url.deletingLastPathComponent().lastPathComponent == "status", let result = url.deletingAllQueryParameters() else { return nil } return result } } ================================================ FILE: Resources/ModuleSample/Module Export - Link Cleaner/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation import Cocoa public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { let text = event.pasteboardContent let types: NSTextCheckingResult.CheckingType = .link let detector = try? NSDataDetector(types: types.rawValue) guard let detect = detector else { completion(.none) } let matches = detect.matches( in: text, options: .reportCompletion, range: NSMakeRange(0, text.count) ) var stringComps: [StringComps] = [ .init(isLink: false, messgae: "") ] guard !matches.isEmpty else { completion(.none) } // lazy man slow process but it works~ for idx in 0 ..< text.count { let char = text[idx] var isLink = false for match in matches { if match.range.contains(idx) { isLink = true break } } if stringComps[stringComps.count - 1].isLink == isLink { stringComps[stringComps.count - 1].messgae += String(char) } else { stringComps.append(.init(isLink: isLink, messgae: String(char))) } } var processed = false stringComps = stringComps.map { comps -> StringComps in if !comps.isLink { return comps } guard let url = URL(string: comps.messgae) else { return comps } for cleaner in cleaners { if cleaner.isPotentialCandidate(original: url), let newUrl = cleaner.process(original: url) { processed = true return .init(isLink: comps.isLink, messgae: newUrl.absoluteString) } } return comps } let newText = stringComps .map(\.messgae) .joined() guard processed else { completion(.none) } let result = RecipeData( postAction: .overwrite, postContent: newText, continueQueue: true ) completion(result) } struct StringComps { var isLink: Bool var messgae: String } } extension StringProtocol { subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] } subscript(range: Range) -> SubSequence { let startIndex = index(self.startIndex, offsetBy: range.lowerBound) return self[startIndex..) -> SubSequence { let startIndex = index(self.startIndex, offsetBy: range.lowerBound) return self[startIndex..) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] } subscript(range: PartialRangeThrough) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] } subscript(range: PartialRangeUpTo) -> SubSequence { self[.. Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/compile.sh ================================================ #!/bin/bash # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then exit 1 fi echo "[*] cleaning build..." rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" \ 2>/dev/null | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" \ 1>/dev/null 2>/dev/null # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null echo "[+] completed compile" ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/.supplement/launch.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")"/../ .build/cli ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Multiline Init Formatter/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation /* ⚠️ Only changes within the current directory will be committed to the compiler, other modifications outside Source dir will be ignored when build. You can add any package dependencies in Package.swift, process your need, and build us a recipe. */ public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { guard event.pasteboardContent.hasPrefix("init("), event.pasteboardContent.hasSuffix(")") else { completion(.none) } var payload = event.pasteboardContent payload.removeFirst("init(".count) payload.removeLast(")".count) payload = payload .components(separatedBy: ",") .joined(separator: ",\n") payload = "init(\n\(payload)\n)" completion(RecipeData( postAction: .overwrite, postContent: payload, continueQueue: false )) } } ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Binary/CommandLineBridge/CommandLineBridge/main.swift ================================================ // // main.swift // CommandLineBridge // // Created by Lakr Aream on 2022/8/13. // import Communicator import Definition import Foundation import Source guard let data = Communicator.retrieveParentData() else { fatalError("unable to receive argument data") } guard let argument = ArgumentData.retrieve(withData: data) else { fatalError("unable to receive argument object") } private let defaultRecipe: RecipeData = .init( postAction: .none, postContent: "", continueQueue: true ) let completion: ((RecipeData?) -> Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/compile.sh ================================================ #!/bin/bash # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then exit 1 fi echo "[*] cleaning build..." rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" \ 2>/dev/null | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" \ 1>/dev/null 2>/dev/null # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null echo "[+] completed compile" ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/.supplement/launch.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")"/../ .build/cli ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Quick SFImage/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation import Cocoa /* ⚠️ Only changes within the current directory will be committed to the compiler, other modifications outside Source dir will be ignored when build. You can add any package dependencies in Package.swift, process your need, and build us a recipe. */ public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { let text = event.pasteboardContent if #available(macOS 11.0, *) { if NSImage(systemSymbolName: text, accessibilityDescription: nil) != nil { completion(.init( postAction: .overwrite, postContent: "Image(systemName: \"\(event.pasteboardContent)\")", continueQueue: false )) } } completion(.none) } } ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Binary/CommandLineBridge/CommandLineBridge/main.swift ================================================ // // main.swift // CommandLineBridge // // Created by Lakr Aream on 2022/8/13. // import Communicator import Definition import Foundation import Source guard let data = Communicator.retrieveParentData() else { fatalError("unable to receive argument data") } guard let argument = ArgumentData.retrieve(withData: data) else { fatalError("unable to receive argument object") } private let defaultRecipe: RecipeData = .init( postAction: .none, postContent: "", continueQueue: true ) let completion: ((RecipeData?) -> Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/compile.sh ================================================ #!/bin/bash # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then exit 1 fi echo "[*] cleaning build..." rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" \ 2>/dev/null | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" \ 1>/dev/null 2>/dev/null # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null echo "[+] completed compile" ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/.supplement/launch.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")"/../ .build/cli ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleSample/Module Export - Speak Dictionary/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation /* ⚠️ Only changes within the current directory will be committed to the compiler, other modifications outside Source dir will be ignored when build. You can add any package dependencies in Package.swift, process your need, and build us a recipe. */ public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { completion(RecipeData( postAction: .speak, postContent: event.pasteboardContent, continueQueue: false )) } } ================================================ FILE: Resources/ModuleTemplate/.templates ================================================ ================================================ FILE: Resources/ModuleTemplate/Executable/ActionBeeModule.exec ================================================ ================================================ FILE: Resources/ModuleTemplate/Executable/Put your binary here ================================================ ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge/main.swift ================================================ // // main.swift // CommandLineBridge // // Created by Lakr Aream on 2022/8/13. // import Communicator import Definition import Foundation import Source guard let data = Communicator.retrieveParentData() else { fatalError("unable to receive argument data") } guard let argument = ArgumentData.retrieve(withData: data) else { fatalError("unable to receive argument object") } private let defaultRecipe: RecipeData = .init( postAction: .none, postContent: "", continueQueue: true ) let completion: ((RecipeData?) -> Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation /* ⚠️ You are in charge to put result binary at designated location. */ public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { // do your workflow here, but avoid changing the pasteboard let appName = event.focusAppID let appID = event.focusAppID let content = event.pasteboardContent print( """ ==================== \(appName ?? "unknown app") - \(appID ?? "unknown app id") \(content) ==================== """ ) // after your work is done, return the recipe and tell parent to do the job // return nil if failed to process let result = RecipeData( postAction: .speak, postContent: "Hello World", continueQueue: false ) completion(result) } } ================================================ FILE: Resources/ModuleTemplate/ExecutableSwift/compile.command ================================================ #!/bin/bash # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/ echo "[*] starting build at $(pwd)..." echo "[*] cleaning build..." rm -rf ActionBeeModule.exec || true rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" \ 2>/dev/null | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" \ 1>/dev/null 2>/dev/null # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null mv ./.build/cli ./ActionBeeModule.exec echo "[+] completed compile" ================================================ FILE: Resources/ModuleTemplate/SourceNode/.eslintrc.js ================================================ module.exports = require('@innei/eslint-config-ts') ================================================ FILE: Resources/ModuleTemplate/SourceNode/.gitignore ================================================ node_modules pnpm-lock.yaml dist yarn.lock package-lock.json .DS_Store ================================================ FILE: Resources/ModuleTemplate/SourceNode/.supplement/compile.sh ================================================ #!/bin/zsh # this compiler script is designed to issue result to ./dist/index.js export PATH=$PATH:/opt/homebrew/bin:/usr/local/bin if ! [ -x "$(command -v npm)" ]; then echo '[E] npm is not installed.' >&2 exit 1 fi set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then echo "[E] malformed project architecture" exit 1 fi echo "[*] cleaning build..." rm -rf dist || true echo "[*] install dependencies..." npm i echo "[*] compile..." npm run build echo "[+] completed compile" ================================================ FILE: Resources/ModuleTemplate/SourceNode/package.json ================================================ { "name": "action.bee.source.module.src", "version": "0.0.0", "description": "ActionBee Module Source Using Node", "main": "./dist/index.js", "scripts": { "build": "ncc build src/index.ts -o dist" }, "keywords": [], "author": "Innei", "license": "MIT", "devDependencies": { "@types/node": "18.7.8", "@vercel/ncc": "0.34.0", "typescript": "4.7.4" } } ================================================ FILE: Resources/ModuleTemplate/SourceNode/src/global.d.ts ================================================ declare global { export interface ActionBeeMessageEvent { focusAppID?: string focusAppName?: string pasteboardContent: string } export type ActionBeeAction = 'none' | 'overwrite' | 'speak' } export {} ================================================ FILE: Resources/ModuleTemplate/SourceNode/src/index.ts ================================================ // ActionBee - Node Module Template // ⚠️ please put compiled your src into ./dist/index.js function finalizeResult( action: ActionBeeAction, content: string, continueQueue: boolean, ) { const result = { postAction: action, // none, overwrite, speak postContent: content, // your content to post continueQueue, } const base64 = Buffer.from(JSON.stringify(result)).toString('base64') process.stderr.write(`\nActionBee-Result-Recipe://${base64}`) process.exit(0) } function moduleMain() { const messageFromEnv = process.env['Communicator_Message'] if (!messageFromEnv) { process.stderr.write('ActionBee-Error: No message found') return } const toString = Buffer.from(messageFromEnv, 'base64').toString() const event: ActionBeeMessageEvent = JSON.parse(toString) console.log(event.focusAppID) // optional console.log(event.focusAppName) // optional console.log(event.pasteboardContent) // string finalizeResult('overwrite', 'Hello World', false) } moduleMain() ================================================ FILE: Resources/ModuleTemplate/SourceNode/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2017", "module": "CommonJS", "declaration": false, "allowJs": true, "allowSyntheticDefaultImports": true, "baseUrl": "./src", "resolveJsonModule": true, "strict": true, "esModuleInterop": true } } ================================================ FILE: Resources/ModuleTemplate/SourcePython/main.py ================================================ #!/usr/bin/env python3 import base64 import json import os from sys import stderr # Write your code inside SolutionMain() function. class PasteboardEvent: def __init__(self, focusAppID, focusAppName, pasteboardContent): self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent class ActionBeeRecipe: def __init__(self, postAction, postContent, continueQueue): self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue def finalizeAndSend(self): message = json.dumps(self.__dict__) b64msg = base64.b64encode(message.encode('utf-8')) finalmsg = "\nActionBee-Result-Recipe://" + b64msg.decode('utf-8') print(finalmsg, file=stderr) exit(0) def SolutionMain(event: PasteboardEvent) -> ActionBeeRecipe: print(event.focusAppID) print(event.focusAppName) print(event.pasteboardContent) return ActionBeeRecipe( postAction="none", # none, overwrite, speak postContent=event.pasteboardContent, continueQueue=True ) if __name__ == '__main__': raw_event = base64.b64decode( os.environ['Communicator_Message']).decode('utf-8') json_object = json.loads(raw_event) event = PasteboardEvent( json_object['focusAppID'], json_object['focusAppName'], json_object['pasteboardContent'] ) recipe = SolutionMain(event) recipe.finalizeAndSend() ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.gitignore ================================================ !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 !default.xcworkspace *.dSYM *.dSYM.zip *.hmap *.ipa *.lcov *.lock *.log *.mode1v3 *.mode2v3 *.moved-aside *.pbxuser *.perspectivev3 *.pid *.pid.lock *.seed *.swp *.tgz *.tsbuildinfo *.xccheckout *.xcscmblueprint *.xcuserstate *~.nib .AppleDB .AppleDesktop .AppleDouble .DS_Store .DocumentRevisions-V100 .LSOverride .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns ._* .apdisk .build .bundle .cache .cache/ .com.apple.timemachine.donotpresent .dynamodb/ .env .env.test .eslintcache .fseventsd .fusebox/ .grunt .idea .lock-wscript .next .node_repl_history .npm .nuxt .nyc_output .parcel-cache .pnp.* .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ .serverless/ .swiftpm .tern-port .vscode-test .vuepress/dist .yarn-integrity .yarn/build-state.yml .yarn/cache .yarn/unplugged /*.gcno Artifacts/ CI CI-Pods.tar Carthage/Build Carthage/Build/ DerivedData DerivedData/ Icon Network Trash Folder Pipeline/Dockers/Buildtime/ Podfile.lock Pods/ Temporary Items artifacts/ bower_components build/ build/Release coverage default.profraw dist dockerbuild dockermnt fastlane/Preview.html fastlane/report.xml fastlane/screenshots/**/*.png fastlane/test_output iOSInjectionProject/ jspm_packages/ lerna-debug.log* lib-cov logs node_modules/ npm-debug.log* pids profile project.xcworkspace report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json temp/ temps/ web_modules/ xcuserdata xcuserdata/ yarn-debug.log* yarn-error.log* ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge/main.swift ================================================ // // main.swift // CommandLineBridge // // Created by Lakr Aream on 2022/8/13. // import Communicator import Definition import Foundation import Source guard let data = Communicator.retrieveParentData() else { fatalError("unable to receive argument data") } guard let argument = ArgumentData.retrieve(withData: data) else { fatalError("unable to receive argument object") } private let defaultRecipe: RecipeData = .init( postAction: .none, postContent: "", continueQueue: true ) let completion: ((RecipeData?) -> Never) = { recipe in guard let recipe = recipe else { Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } guard let recipeBase64String = recipe.compileBase64() else { fatalError("failed to compile recipe data") } Communicator.sendRecipeDataAndExit(recipeBase64String) fatalError("malformed program flow") } do { try ActionBee.solutionMain(event: argument, completion: completion) } catch { print(error.localizedDescription) Communicator.sendRecipeDataAndExit(defaultRecipe.compileBase64()!) fatalError("malformed program flow") } CFRunLoopRun() ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 50307AB928A75EA500598724 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50307AB828A75EA500598724 /* main.swift */; }; 50307AC128A8AF0E00598724 /* Communicator in Frameworks */ = {isa = PBXBuildFile; productRef = 50307AC028A8AF0E00598724 /* Communicator */; }; 50628BD928A8AF7D00882579 /* Source in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BD828A8AF7D00882579 /* Source */; }; 50628BDB28A8CD2300882579 /* Definition in Frameworks */ = {isa = PBXBuildFile; productRef = 50628BDA28A8CD2300882579 /* Definition */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 50307AB328A75EA500598724 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 50307AB528A75EA500598724 /* CommandLineBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommandLineBridge; sourceTree = BUILT_PRODUCTS_DIR; }; 50307AB828A75EA500598724 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50307AB228A75EA500598724 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50307AC128A8AF0E00598724 /* Communicator in Frameworks */, 50628BD928A8AF7D00882579 /* Source in Frameworks */, 50628BDB28A8CD2300882579 /* Definition in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50307AAC28A75EA500598724 = { isa = PBXGroup; children = ( 50307AB728A75EA500598724 /* CommandLineBridge */, 50307AB628A75EA500598724 /* Products */, 50307ABF28A8AF0B00598724 /* Frameworks */, ); sourceTree = ""; }; 50307AB628A75EA500598724 /* Products */ = { isa = PBXGroup; children = ( 50307AB528A75EA500598724 /* CommandLineBridge */, ); name = Products; sourceTree = ""; }; 50307AB728A75EA500598724 /* CommandLineBridge */ = { isa = PBXGroup; children = ( 50307AB828A75EA500598724 /* main.swift */, ); path = CommandLineBridge; sourceTree = ""; }; 50307ABF28A8AF0B00598724 /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 50307AB428A75EA500598724 /* CommandLineBridge */ = { isa = PBXNativeTarget; buildConfigurationList = 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */; buildPhases = ( 50307AB128A75EA500598724 /* Sources */, 50307AB228A75EA500598724 /* Frameworks */, 50307AB328A75EA500598724 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = CommandLineBridge; packageProductDependencies = ( 50307AC028A8AF0E00598724 /* Communicator */, 50628BD828A8AF7D00882579 /* Source */, 50628BDA28A8CD2300882579 /* Definition */, ); productName = CommandLineBridge; productReference = 50307AB528A75EA500598724 /* CommandLineBridge */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50307AAD28A75EA500598724 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1340; LastUpgradeCheck = 1400; TargetAttributes = { 50307AB428A75EA500598724 = { CreatedOnToolsVersion = 13.4.1; }; }; }; buildConfigurationList = 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 50307AAC28A75EA500598724; productRefGroup = 50307AB628A75EA500598724 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50307AB428A75EA500598724 /* CommandLineBridge */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 50307AB128A75EA500598724 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50307AB928A75EA500598724 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 50307ABA28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 50307ABB28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 12.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 50307ABD28A75EA500598724 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Debug; }; 50307ABE28A75EA500598724 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50307AB028A75EA500598724 /* Build configuration list for PBXProject "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABA28A75EA500598724 /* Debug */, 50307ABB28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50307ABC28A75EA500598724 /* Build configuration list for PBXNativeTarget "CommandLineBridge" */ = { isa = XCConfigurationList; buildConfigurations = ( 50307ABD28A75EA500598724 /* Debug */, 50307ABE28A75EA500598724 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 50307AC028A8AF0E00598724 /* Communicator */ = { isa = XCSwiftPackageProductDependency; productName = Communicator; }; 50628BD828A8AF7D00882579 /* Source */ = { isa = XCSwiftPackageProductDependency; productName = Source; }; 50628BDA28A8CD2300882579 /* Definition */ = { isa = XCSwiftPackageProductDependency; productName = Definition; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50307AAD28A75EA500598724 /* Project object */; } ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Binary/CommandLineBridge/CommandLineBridge.xcodeproj/xcshareddata/xcschemes/CommandLineBridge.xcscheme ================================================ ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Communicator/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Communicator/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Communicator", products: [ .library(name: "Communicator", targets: ["Communicator"]), ], targets: [ .target(name: "Communicator", dependencies: []), ] ) ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Communicator/Sources/Communicator/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Communicator/Sources/Communicator/Communicator.m ================================================ // // Communicator.m // // // Created by Lakr Aream on 2022/8/14. // #import "Communicator.h" NSData *argumentData; __attribute__((constructor)) void communicator_constructor(void) { NSString *message = [NSProcessInfo.processInfo.environment valueForKey:@"Communicator_Message"]; if (message.length <= 0) { return; } NSData *data = [[NSData alloc] initWithBase64EncodedString:message options:NULL]; if (data == NULL || data.length <= 0) { return; } argumentData = data; } @implementation Communicator + (NSData*)retrieveParentData { return argumentData; } + (void)sendRecipeDataAndExit:(NSString*)base64String { NSLog(@"\nActionBee-Result-Recipe://%@", base64String); exit(0); } @end ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Communicator/Sources/Communicator/include/Communicator.h ================================================ // // Communicator.h // // // Created by Lakr Aream on 2022/8/14. // #import @interface Communicator : NSObject + (NSData* _Nullable )retrieveParentData; + (void)sendRecipeDataAndExit:(NSString* _Nonnull)base64String; @end ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Definition/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Definition/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Definition", products: [ .library(name: "Definition", targets: ["Definition"]), ], targets: [ .target(name: "Definition", dependencies: []), ] ) ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/Definition/Sources/Definition/Definition.swift ================================================ import Foundation private let decoder = JSONDecoder() private let encoder = JSONEncoder() public struct ArgumentData: Codable { public let focusAppID: String? public let focusAppName: String? public let pasteboardContent: String public init(focusAppID: String?, focusAppName: String?, pasteboardContent: String) { self.focusAppID = focusAppID self.focusAppName = focusAppName self.pasteboardContent = pasteboardContent } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> ArgumentData? { try? decoder.decode(ArgumentData.self, from: data) } } public struct RecipeData: Codable { public let postAction: PostAction public let postContent: String public let continueQueue: Bool public enum PostAction: String, Codable { case overwrite case speak case none } public init(postAction: PostAction, postContent: String, continueQueue: Bool) { self.postAction = postAction self.postContent = postContent self.continueQueue = continueQueue } public func compileBase64() -> String? { (try? encoder.encode(self))?.base64EncodedString() } public static func retrieve(withData data: Data) -> Self? { try? decoder.decode(Self.self, from: data) } } ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/compile.sh ================================================ #!/bin/zsh # this compiler script is designed to issue binary to ./.build/cli set -e cd "$(dirname "$0")"/../ echo "[*] starting build at $(pwd)..." if [ ! -f .action ]; then echo "[E] malformed project architecture" exit 1 fi echo "[*] cleaning build..." rm -rf .build || true mkdir .build echo "[*] looking for target binary..." BUILT_PRODUCTS_DIR=$( xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ -showBuildSettings \ CODE_SIGNING_ALLOWED="NO" | grep -m 1 "BUILT_PRODUCTS_DIR" | grep -oEi "\/.*" ) BINARY_LOCATION="$BUILT_PRODUCTS_DIR/CommandLineBridge" # remove the binary rm -f "$BINARY_LOCATION" || true echo "[*] building binary to $BUILT_PRODUCTS_DIR..." xcodebuild \ clean build \ -configuration Release \ -workspace ./App.xcworkspace \ -scheme CommandLineBridge \ CODE_SIGNING_ALLOWED="NO" # check if the binary exists if [ ! -f "$BINARY_LOCATION" ]; then echo "[E] failed to emit binary at $BINARY_LOCATION" exit 1 fi echo "[*] copying binary..." cp "$BINARY_LOCATION" .build/cli echo "[*] signing binary..." chmod +x .build/cli codesign -s - --deep --force .build/cli 1>/dev/null 2>/dev/null echo "[+] completed compile" ================================================ FILE: Resources/ModuleTemplate/SourceSwift/.supplement/launch.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")"/../ .build/cli ================================================ FILE: Resources/ModuleTemplate/SourceSwift/App.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Resources/ModuleTemplate/SourceSwift/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Resources/ModuleTemplate/SourceSwift/Source/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: Resources/ModuleTemplate/SourceSwift/Source/Package.swift ================================================ // swift-tools-version: 5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "Source", products: [ .library(name: "Source", targets: ["Source"]), ], dependencies: [ .package(name: "Definition", path: "./.supplement/Definition"), ], targets: [ .target(name: "Source", dependencies: ["Definition"]), ] ) ================================================ FILE: Resources/ModuleTemplate/SourceSwift/Source/Sources/Source/Source.swift ================================================ // ActionBee // // Executable Source Template - v1.0 // import Definition import Foundation /* ⚠️ Only changes within the current directory will be committed to the compiler, other modifications outside ./Source/Sources dir will be ignored when build. */ public enum ActionBee { public static func solutionMain(event: ArgumentData, completion: @escaping (RecipeData?) -> Never) throws { // do your workflow here, but avoid changing the pasteboard let appName = event.focusAppID let appID = event.focusAppID let content = event.pasteboardContent print( """ ==================== \(appName ?? "unknown app") - \(appID ?? "unknown app id") \(content) ==================== """ ) // after your work is done, return the recipe and tell parent to do the job // return nil if failed to process let result = RecipeData( postAction: .speak, postContent: "Hello World", continueQueue: false ) completion(result) } } ================================================ FILE: Resources/Scripts/NewlineDeduplicate.swift ================================================ import Foundation guard CommandLine.arguments.count == 2 else { exit(-1) } let url = URL(fileURLWithPath: CommandLine.arguments[1]) guard var str = try? String(contentsOf: url) else { exit(-1) } while str.contains("\n\n\n") { str = str.replacingOccurrences(of: "\n\n\n", with: "\n\n") } try str.write(to: url, atomically: true, encoding: .utf8) ================================================ FILE: Resources/Scripts/PackTemplates.sh ================================================ #!/bin/bash set -ex cd "$(dirname "$0")"/../ModuleTemplate SOURCE_DIR="$(pwd)" TARGET_DIR="../../App/Action/Action/Backend/Action/ActionTemplates" cd "$TARGET_DIR" TARGET_DIR="$(pwd)" FLAG_FILE=".templates" PATH_EXTENSION="ActionTemplatePackage" if [ ! -f "$SOURCE_DIR/$FLAG_FILE" ]; then echo "malformed project structure" exit 1 fi if [ ! -f "$TARGET_DIR/$FLAG_FILE" ]; then echo "malformed project structure" exit 1 fi echo "[*] Cleaning up old templates..." cd "$TARGET_DIR" rm -rf ./* echo "[*] Cleaning up dirty templates..." cd "$SOURCE_DIR" find "$SOURCE_DIR" -name ".DS_Store" -delete find "$SOURCE_DIR" -name "._*" -delete git clean -fdx echo "[*] Packaging templates..." cd "$SOURCE_DIR" for TEMPLATE_ITEM in * do echo "[*] Packaging $TEMPLATE_ITEM..." cd "$SOURCE_DIR/$TEMPLATE_ITEM" || continue tar -cvf "$TARGET_DIR/$TEMPLATE_ITEM.$PATH_EXTENSION" . done echo "[*] Done" ================================================ FILE: Resources/Scripts/UpdateGitHub.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")" cd ../../ # check if .root file exists if [ ! -f .root ]; then echo "malformed project structure, missing .root file" exit 1 fi ORIG_DIR=$(pwd) TARGET_DIR="/Users/qaq/Bootstrap/GitHub/ActionBee" # check if ORIG_DIR has prefix if [[ $ORIG_DIR != "/Users/qaq/Bootstrap/"* ]]; then echo "this script is used to sync commit on @Lakr233 device, do not run it!" exit 1 fi # check if target exists if [ ! -d $TARGET_DIR ]; then echo "target directory $TARGET_DIR does not exist" exit 1 fi # check if file target/.root exists if [ ! -f $TARGET_DIR/.root ]; then echo "target directory $TARGET_DIR is not target project" exit 1 fi echo "Syncing from $ORIG_DIR to $TARGET_DIR" # check if git repo at ORIG_DIR has uncommitted changes if [ -n "$(git status --porcelain)" ]; then echo "git repo at $ORIG_DIR has uncommitted changes" exit 1 fi # clean our project first git clean -fdx # remove everything at target rm -rf $TARGET_DIR/* # not .file at root # copy over cp -r $ORIG_DIR/* $TARGET_DIR PROHIBIT_FILE_LIST=( "Resources/Design" ) # remove prohibited files for file in "${PROHIBIT_FILE_LIST[@]}"; do echo "removing $TARGET_DIR/$file" rm -rf "${TARGET_DIR:?}/$file" done find $TARGET_DIR/External -name ".git" -delete # get current commit hash COMMIT_HASH=$(git rev-parse --short HEAD) cd $TARGET_DIR git add . git commit -m "Sync Update - $COMMIT_HASH" echo "" echo "======= Sync Update - $COMMIT_HASH =======" echo "To push to remote, run following command:" echo "" echo " cd $TARGET_DIR && git push origin master" echo "" echo "==========================================" echo "" # done ================================================ FILE: Resources/Scripts/UpdateLicenses.sh ================================================ #!/bin/bash set -ex cd "$(dirname "$0")" CURR_DIR="$(pwd)" cd ../../ SOURCE_DIR="$(pwd)" if [ ! -f "$SOURCE_DIR/App/Action/Action/Application/License.txt" ]; then echo "License.txt not found in App/Action/Action/Application" exit 1 fi cat "$SOURCE_DIR/LICENSE" > "$SOURCE_DIR/App/Action/Action/Application/License.txt" # for every LICENSE inside External for dir in "$SOURCE_DIR"/External/*; do if [ -d "$dir" ]; then if [ -f "$dir/LICENSE" ]; then echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "==========" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "$(basename "$dir")" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" cat "$dir/LICENSE" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" fi fi done echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "==========" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" TIME=$(date +%Y-%m-%d) echo "Updated: $TIME" >> "$SOURCE_DIR/App/Action/Action/Application/License.txt" swift "$CURR_DIR/NewlineDeduplicate.swift" "$SOURCE_DIR/App/Action/Action/Application/License.txt" echo "Done"