Repository: DKalachniuk/XcodeProjects Branch: master Commit: 9685a3a9237a Files: 75 Total size: 194.9 KB Directory structure: gitextract_v7__n1h4/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── XcodeProjects/ │ ├── AppDelegate.swift │ ├── Data/ │ │ ├── Alias.swift │ │ ├── CustomCommand.swift │ │ ├── Project.swift │ │ ├── TerminalCommand.swift │ │ └── TerminalScript.swift │ ├── Logic/ │ │ ├── CodableColorPicker.swift │ │ ├── Extensions/ │ │ │ ├── CharacterSet+All.swift │ │ │ ├── FileManger+Content.swift │ │ │ ├── NSColor+Codable.swift │ │ │ ├── NSWorkspace+App.swift │ │ │ ├── NSWorkspace+Execute.swift │ │ │ ├── NotificationNames.swift │ │ │ ├── String+Components.swift │ │ │ └── View+If.swift │ │ ├── ProfileFile.swift │ │ ├── PropertyWrappers/ │ │ │ └── UserDefaultsWrapper.swift │ │ └── UserDefaultsConfig.swift │ ├── Preview Content/ │ │ └── Preview Assets.xcassets/ │ │ └── Contents.json │ ├── Resourses/ │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── add.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── arrow.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── close.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── gear.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── more.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── packageSwift.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── plus.imageset/ │ │ │ │ └── Contents.json │ │ │ └── terminal.imageset/ │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── XcodeProjects.entitlements │ ├── Support/ │ │ ├── EventMonitor.swift │ │ ├── Preferences.swift │ │ ├── SharedFileList.h │ │ ├── SharedFileList.m │ │ └── XcodeProjects-Bridging-Header.h │ └── UI/ │ ├── Buttons/ │ │ ├── AddAliasButton.swift │ │ ├── AddCustomCommandButton.swift │ │ ├── AddProjectButton.swift │ │ ├── PreferencesView.swift │ │ └── TerminalCommandButton.swift │ ├── Cells/ │ │ ├── AliasCell.swift │ │ └── ProjectCell.swift │ ├── Controllers/ │ │ ├── ProjectPreferencesViewController.swift │ │ └── StatusViewController.swift │ ├── Images/ │ │ ├── ArrowImage.swift │ │ ├── ArrowImageWithHoverPopover.swift │ │ ├── CloseHintImage.swift │ │ └── PackageSwiftImage.swift │ ├── Modifiers/ │ │ ├── OnHover.swift │ │ ├── OnHoverText.swift │ │ └── RoundedCorners.swift │ └── Views/ │ ├── ActionsMenuText.swift │ ├── AliasNameView.swift │ ├── DividerSection.swift │ ├── HintView.swift │ ├── MainView.swift │ ├── ProjectIcon.swift │ ├── ProjectMenuView.swift │ ├── ProjectNameView.swift │ └── ProjectPreferencesView.swift ├── XcodeProjects.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ └── klm88400.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ ├── xcshareddata/ │ │ └── xcschemes/ │ │ ├── XcodeProjects.xcscheme │ │ └── XcodeProjectsTests.xcscheme │ └── xcuserdata/ │ └── klm88400.xcuserdatad/ │ └── xcdebugger/ │ └── Breakpoints_v2.xcbkptlist └── XcodeProjectsTests/ ├── Info.plist └── XcodeProjectsTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ github: [DKalachniuk] ================================================ FILE: .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 .xcuserstate # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 dkalachniuk 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 ================================================ # XcodeProjects Switch between projects in 2 clicks. Don't write "pod install", "pod update" and "cd " in the terminal anymore!
**NEW**: Remove derived data of particular project!
**NEW**: Add your custom commands for a project!
**NEW**: Open Package.swift file!
**NEW**: Keep you aliases and custom script's commands in one list. Switch On and Off in the Preferences
**NEW**: Now you can open your project in Fork app
# When do you need this tool - you want to **open** quickly specific **Xcode project** or **Package.swift** file - you want to **clear derived data for a specific project only** - you type "pod install", "pod update" and "pod deintegrate" in the terminal too often. Do it in 2 clicks - you need to **remove Podfile.lock file** quickly - you need to change directory to point to your project in the terminal - you want quickly **open your project in SourceTree** - you want to **execute custom command for a project** - you have a lot of projects or modules/frameworks and you need to switch between them quickly # How to use When you open the app for the first time you would see empty screen ![alt text](Images/example_empty_list.png?raw=true) click "+" button to add folder of you project within system dialog (Hint: choose multiple projects) ![alt text](Images/example_list_of_projects_dark.png?raw=true) ![alt text](Images/example_list_of_projects_light.png?raw=true) **Click "arrows" button to open your Xcode workspace/project** **Click "swift" button to open your Package.swift in Xcode** **Click "..." button to see the menu options** Switch between Projects/Aliases tabs in the bottom ![alt text](Images/example_list_of_aliases_light.png?raw=true) By default the app will parse your system .bash_profile and .zprofile files. But you can either add your alias manually or parse the existing script file for aliases. Also it is possible to drag projects around. ![alt text](Images/example_drag_and_drop_dark.png?raw=true) ## Options When "..." button of the project is clicked you will see the following ![alt text](Images/example_context_menu_light_openinfork.png?raw=true) ![alt text](Images/example_context_menu_light.png?raw=true) ![alt text](Images/example_context_menu_dark.png?raw=true) **Open in Terminal** - opens the path to the project in terminal. Handy to quickly switch to the folder of the project **Open in Finder** - opens the folder of the project in Finder **Open in Sourcetree** - opens the folder of the project in Sourcetree (if you have Sourcetree installed) **Open in Fork** - opens the folder of the project in Fork (if you have Fork installed) **Pod install** - runs "pod install" command in terminal for the project **Pod update** - runs "pod update" command in terminal for the project **Pod deintegrate** - runs "pod deintegrate" command in terminal for the project **Remove Podfile.lock** - removes file Podfile.lock from the project **Clear derived data** - removes project's derived data folder **Rename project** - opens new window to rename the project. Note: renames in the Xcode Projects app only - not physical folder/project etc. **Switch On/Off Aliases Tab** - switches on/off the picker to show projects and aliases **Remove from the list** - removes project from the list If you have added custom commands then they would appear at the bottom of the list with option to remove the command ![alt text](Images/custom_command.png?raw=true) # Preferences ![alt text](Images/preferences_dark.png?raw=true) **Open in GitHub** - opens the github webpage of XcodeProjects **Remove all projects** - removes all projects from XcodeProjects app **Open derived data in Finder** - opens Xcode derived data folder in Finder **Clear Xcode derived data** - removes Xcode derived data from default location ~/Library/Developer/Xcode/DerivedData **Launch at Login** - XcodeProjects app will be launched when MacOS was restarted/started **Show project's icon** - show/hide project's icon next to the project's name **Add custom command** - add custom command for all projects > Hint: Click on project icon to change its color # How to install Go to release section in this repo and download the binary. Drag & drop XcodeProjects.app to your Application folder. Enjoy! If you see this window ![alt text](Images/xcodeProjects_cantopen.jpeg?raw=true) it is ok. No worries. It is new security measure in MacOS. Please click with **a right button** the XcodeProjects app. Then you will see ![alt text](Images/xcodeProjects_rightclickMenu.jpeg?raw=true) Click Open ![alt text](Images/xcodeProjects_openMenu.jpeg?raw=true) Click Open again. That is it! # Support Just simple star ⭐️ will make my life happier :) If you want to support this project, feel free to send some coffee money to this [link](https://paypal.me/dkalachniuk) If you don't want to spend money, you can still support me if you will share this tool with your colleagues and/or give me a star. # Ideas - add command to support carthage update - add sorting to the list of projects - add favourites projects - configure the actions when you press on the project. so you will be able to see only those that you need # TODO - ~~update commands logic so some commands like: open workspace, open in finder will not use terminal to open the files~~ - ~~put action "open in Xcode" in the main view~~ - ~~remove project's derived data folder~~ - ~~ability to rename project name~~ - ~~add ability to add custom command~~ - add Sparkle framework to check for updates automatically # Special Thanks Special thanks to - [Solokub](https://github.com/Solokub) - for providing awesome design for the app. 🥳 - [Gui Rambo](https://gumroad.com/insidegui) - for his awesome project "StatusBuddy". [Gui Rambo's github](https://github.com/insidegui) # Badges [![HitCount](http://hits.dwyl.com/DKalachniuk/XcodeProjects.svg)](http://hits.dwyl.com/DKalachniuk/XcodeProjects) ================================================ FILE: XcodeProjects/AppDelegate.swift ================================================ // // AppDelegate.swift // XcodeProjects // // Created by Dima Kalachniuk on 06/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Cocoa var statusItem: NSStatusItem? @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) private lazy var popover = NSPopover() private var eventMonitor: EventMonitor? private let preferences = Preferences() private lazy var statusController: StatusViewController = { StatusViewController(preferences: preferences) }() var window: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { updateButton() popover.contentViewController = statusController eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in guard let self = self else { return } guard self.popover.isShown else { return } self.closePopover(sender: event) } } } // MARK: - UI private extension AppDelegate { func updateButton() { guard let button = statusItem.button else { return } button.image = NSImage(named: "terminal") button.image?.size = NSSize(width: 15, height: 15) button.action = #selector(togglePopover) } } // MARK: - Actions extension AppDelegate { static func closePopover() { if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.closePopover(sender: nil) } } @objc func togglePopover(_ sender: Any?) { if popover.isShown { closePopover(sender: sender) } else { showPopover(sender: sender) } } func showPopover(sender: Any?) { guard let button = statusItem.button else { return } eventMonitor?.start() popover.show( relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY ) NSApp.activate(ignoringOtherApps: true) } func closePopover(sender: Any?) { popover.performClose(sender) eventMonitor?.stop() } } ================================================ FILE: XcodeProjects/Data/Alias.swift ================================================ // // Alias.swift // XcodeProjects // // Created by Dima Kalachniuk on 29/06/2022. // Copyright © 2022 com.klm.mac.myProjects. All rights reserved. // import Foundation class Alias: Identifiable, Codable, Hashable { var id: UUID = UUID() var name: String init(name: String) { self.name = name } static func == (lhs: Alias, rhs: Alias) -> Bool { lhs.name == rhs.name && lhs.id == rhs.id } public func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(name) } } ================================================ FILE: XcodeProjects/Data/CustomCommand.swift ================================================ // // CustomCommand.swift // XcodeProjects // // Created by Dima Kalachniuk on 23/02/2022. // Copyright © 2022 com.klm.mac.myProjects. All rights reserved. // import Foundation class CustomCommand: Identifiable, Codable, Hashable { var id: UUID = UUID() let name: String let command: String init (name: String? = nil, command: String) { var newName = name ?? command if newName.isEmpty { newName = command } self.name = newName self.command = command } func fullCommand(for project: Project) -> String { "cd \(project.path);\(command)" } static func == (lhs: CustomCommand, rhs: CustomCommand) -> Bool { lhs.command == rhs.command } func hash(into hasher: inout Hasher) { hasher.combine(name); hasher.combine(command) } } ================================================ FILE: XcodeProjects/Data/Project.swift ================================================ // // Project.swift // XcodeProjects // // Created by Dima Kalachniuk on 06/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation import AppKit class Project: Identifiable, Codable { var id: UUID = UUID() var name: String let path: String var color: CodableColor var iconPath: String? init(name: String, path: String) { self.name = name self.path = path self.color = CodableColorPicker.shared.pickRandomColor() } convenience init?(url: URL) { guard let name = url.path.lastComponent else { return nil } let splitName = name.split(separator: "-").last ?? name self.init(name: String(splitName), path: url.path) } } extension Project { var urlPath: URL? { URL(fileURLWithPath: path) } var workspaceURL: URL? { FileManager.default.getWorkspaceFrom(for: self) } var swiftPackageURL: URL? { FileManager.default.getSwiftPackageFrom(for: self) } var projectURL: URL? { FileManager.default.getXcodeProjFrom(for: self) } var podfileLockPath: String? { FileManager.default.getPodfileLockFrom(for: self)?.absoluteString.removeFilePath } var hasPodfileLock: Bool { podfileLockPath != nil } var derivedDataPath: String? { FileManager.default.getXcodeDerivedData(for: self)?.absoluteString.removeFilePath } var hasDerivedData: Bool { derivedDataPath != nil } var hasXcodeProject: Bool { (workspaceURL ?? projectURL) != nil } var hasSwiftPackage: Bool { swiftPackageURL != nil } } extension Project { var hasCocoapods: Bool { FileManager.default.getPodfile(for: self) != nil } } extension Project { static let dummy = Project(name: "DummyProject", path: "/AnyPath") } ================================================ FILE: XcodeProjects/Data/TerminalCommand.swift ================================================ // // TerminalCommand.swift // XcodeProjects // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation enum ExecutionMethod { case inTerminal case justOpen } let derivedDataFolderPath = "Library/Developer/Xcode/DerivedData" let relativeDerivedDataFolderPath = "~/\(derivedDataFolderPath)" let directDerivedDataFolderPath = "\(FileManager.default.homeDirectoryForCurrentUser)\(derivedDataFolderPath)" enum TerminalCommand { case podInstall case podUpdate case podDeintegrate case removePodfileLock case finder case openInTerminal case sourceTree case fork case openWorkspace case openSwiftPackage case clearXcodeDerivedData case openXcodeDerivedData case clearProjectDerivedData case custom(command: String) case alias(command: String) var title: String { switch self { case .podInstall: return "Pod install" case .podUpdate: return "Pod update" case .podDeintegrate: return "Pod deintegrate" case .removePodfileLock: return "Remove Podfile.lock" case .finder: return "Open in Finder" case .openInTerminal: return "Open in Terminal" case .sourceTree: return "Open in Sourcetree" case .fork: return "Open in Fork" case .openWorkspace: return "Open Workspace" case .openSwiftPackage: return "Open Package.swift" case .clearXcodeDerivedData: return "Clear Xcode derived data" case .openXcodeDerivedData: return "Open derived data in Finder " case .clearProjectDerivedData: return "Clear derived data" case .custom(let command): return command case .alias(let command): return command } } var executionMethod: ExecutionMethod { switch self { case .podUpdate, .podInstall, .openInTerminal, .sourceTree, .fork, .clearXcodeDerivedData, .clearProjectDerivedData, .podDeintegrate, .removePodfileLock, .custom, .alias: return .inTerminal case .finder, .openWorkspace, .openXcodeDerivedData, .openSwiftPackage: return .justOpen } } var command: String? { switch self { case .podInstall: return "pod install" case .podUpdate: return "pod update" case .podDeintegrate: return "pod deintegrate" case .openInTerminal: return "cd " case .sourceTree: return "open -a SourceTree" case .fork: return "open -a Fork" case .custom(let command): return command case .alias(let command): return command default: return nil } } func script(for project: Project?) -> String? { // script for clearXcodeDerivedData command if self == .clearXcodeDerivedData { return getScriptToRemove(file: relativeDerivedDataFolderPath) } // script for clearProjectDerivedData command if self == .clearProjectDerivedData { return getScriptToRemove(file: project?.derivedDataPath) } // script for removePodfileLock command if self == .removePodfileLock { return getScriptToRemove(file: project?.podfileLockPath) } // script for aliases if let command = command, self == .alias(command: command) { return TerminalScript(command: (command.wrappedInScript)).script } // script for other in terminal commands guard let openInTerminalCommand = TerminalCommand.openInTerminal.command, var commandValue = command, let project = project else { return nil } let commandString = "\(openInTerminalCommand) \(project.path)" var twoLinesScript = commandString.wrappedInScript if self != .openInTerminal { twoLinesScript.append("\n") if self == .sourceTree || self == .fork { commandValue += " \(project.path)" } twoLinesScript.append(commandValue.wrappedInScript) } return TerminalScript(command: twoLinesScript).script } private func getScriptToRemove(file path: String?) -> String? { path.map({ TerminalScript(toRemovePath: $0).script }) } } extension TerminalCommand: Equatable { static func ==(lhs: TerminalCommand, rhs: TerminalCommand) -> Bool { switch (lhs, rhs) { case (.podInstall, .podInstall), (.podUpdate, .podUpdate), (.podDeintegrate, .podDeintegrate), (.removePodfileLock, .removePodfileLock), (.finder, .finder), (.openInTerminal, .openInTerminal), (.sourceTree, .sourceTree), (.fork, .fork), (.openWorkspace, .openWorkspace), (.openSwiftPackage, .openSwiftPackage), (.clearXcodeDerivedData, .clearXcodeDerivedData), (.openXcodeDerivedData, .openXcodeDerivedData), (.clearProjectDerivedData, .clearProjectDerivedData): return true case (.custom(let lhsCommand), .custom(let rhsCommand)): return lhsCommand == rhsCommand case (.alias(let lhsCommand), .alias(let rhsCommand)): return lhsCommand == rhsCommand default: return false } } } extension String { var wrappedInScript: String { "do script \"\(self)\" in front window" } } ================================================ FILE: XcodeProjects/Data/TerminalScript.swift ================================================ // // AppleScript.swift // XcodeProjects // // Created by Dima Kalachniuk on 20/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation struct TerminalScript { let command: String var script: String { let scriptText = """ tell application "Terminal" if not (exists window 1) then reopen activate \(command) end tell """ return scriptText } init(command: String) { self.command = command } init(toRemovePath: String) { self.command = "rm -rf \(toRemovePath)".wrappedInScript } } ================================================ FILE: XcodeProjects/Logic/CodableColorPicker.swift ================================================ // // CodableColorPicker.swift // XcodeProjects // // Created by Dima Kalachniuk on 25/07/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import AppKit class CodableColorPicker { static let shared = CodableColorPicker() private static let allColors = [NSColor.systemRed, NSColor.systemGreen, NSColor.systemBlue, NSColor.systemIndigo, NSColor.systemOrange, NSColor.systemPurple] private var usedColors: [NSColor] = [] { didSet { if usedColors.isEmpty { usedColors = CodableColorPicker.allColors } } } private init() { setupUsedColors() } } extension CodableColorPicker { func setupUsedColors() { usedColors = CodableColorPicker.allColors.filter({ !UserDefaultsConfig.projectObjects.map({$0.color.color}).contains($0) }) } func pickRandomColor() -> CodableColor { guard let randomColor = usedColors.randomElement(), let randomIndex = usedColors.firstIndex(of: randomColor) else { print("couldn't get random color from the array") return CodableColor(color: NSColor.systemIndigo) } usedColors.remove(at: randomIndex) return CodableColor(color: randomColor) } } ================================================ FILE: XcodeProjects/Logic/Extensions/CharacterSet+All.swift ================================================ // // CharacterSet+All.swift // XcodeProjects // // Created by Dima Kalachniuk on 17/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation extension CharacterSet { func allCharacters() -> [Character] { var result: [Character] = [] for plane: UInt8 in 0...16 where self.hasMember(inPlane: plane) { for unicode in UInt32(plane) << 16 ..< UInt32(plane + 1) << 16 { if let uniChar = UnicodeScalar(unicode), self.contains(uniChar) { result.append(Character(uniChar)) } } } return result } } ================================================ FILE: XcodeProjects/Logic/Extensions/FileManger+Content.swift ================================================ // // FileManger+Content.swift // XcodeProjects // // Created by Dima Kalachniuk on 20/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation extension FileManager { func getWorkspaceFrom(for project: Project) -> URL? { filter(urls: contentsOf(project: project), by: "xcworkspace")?.first } func getXcodeProjFrom(for project: Project) -> URL? { filter(urls: contentsOf(project: project), by: "xcodeproj")?.first } func getSwiftPackageFrom(for project: Project) -> URL? { filter(urls: contentsOf(project: project), byName: "Package.swift")?.first } func getPodfileLockFrom(for project: Project) -> URL? { filter(urls: contentsOf(project: project), byName: "Podfile.lock")?.first } func getPodfile(for project: Project) -> URL? { filter(urls: contentsOf(project: project), byName: "Podfile")?.first } func getXcodeDerivedData(for project: Project) -> URL? { guard let urls = contentsOf(url: URL(string: directDerivedDataFolderPath)) else { return nil } return urls.first(where: { $0.absoluteString.lowercased().contains(project.name.lowercased()) }) } } private extension FileManager { func contentsOf(url: URL?) -> [URL]? { guard let urlPath = url else { return nil } do { let fileURLs = try contentsOfDirectory(at: urlPath, includingPropertiesForKeys: nil) return fileURLs } catch { print("Error while enumerating files \(urlPath): \(error.localizedDescription)") return nil } } func contentsOf(project: Project) -> [URL]? { contentsOf(url: project.urlPath) } func filter(urls: [URL]?, by pathExtension: String) -> [URL]? { urls?.filter({ $0.pathExtension == pathExtension }) } func filter(urls: [URL]?, byName name: String) -> [URL]? { urls?.filter({ $0.lastPathComponent == name }) } } ================================================ FILE: XcodeProjects/Logic/Extensions/NSColor+Codable.swift ================================================ // // NSColor+Codable.swift // XcodeProjects // // Created by Dima Kalachniuk on 21/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import AppKit public struct CodableColor { /// The color to be (en/de)coded let color: NSColor } extension CodableColor: Encodable { public func encode(to encoder: Encoder) throws { let nsCoder = NSKeyedArchiver(requiringSecureCoding: true) color.encode(with: nsCoder) var container = encoder.unkeyedContainer() try container.encode(nsCoder.encodedData) } } extension CodableColor: Decodable { public init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() let decodedData = try container.decode(Data.self) let nsCoder = try NSKeyedUnarchiver(forReadingFrom: decodedData) self.color = NSColor(coder: nsCoder).unsafelyUnwrapped } } public extension NSColor { func codable() -> CodableColor { return CodableColor(color: self) } } ================================================ FILE: XcodeProjects/Logic/Extensions/NSWorkspace+App.swift ================================================ // // NSWorkspace+App.swift // XcodeProjects // // Created by Dima Kalachniuk on 01/07/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI extension NSWorkspace { var sourceTreeAppInstalled: Bool { (NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.torusknot.SourceTreeNotMAS") != nil) } var forkAppInstalled: Bool { (NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.DanPristupov.Fork") != nil) } } ================================================ FILE: XcodeProjects/Logic/Extensions/NSWorkspace+Execute.swift ================================================ // // NSWorkspace+Execute.swift // XcodeProjects // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI extension NSWorkspace { static func execute(command: TerminalCommand, forProject project: Project? = nil) { switch command.executionMethod { case .inTerminal: executeInTerminal(command: command, project: project) case .justOpen: open(project: project, withCommand: command) } } static func showErrorAlert(withMessage message: String) { let alert = NSAlert() alert.messageText = "Something went wrong" alert.informativeText = message alert.alertStyle = NSAlert.Style.warning alert.addButton(withTitle: "OK") alert.runModal() } } private extension NSWorkspace { static func open(project: Project?, withCommand command: TerminalCommand) { switch command { case .openWorkspace: guard let url = project?.workspaceURL ?? project?.projectURL else { return } NSWorkspace.shared.open(url) case .openSwiftPackage: guard let url = project?.swiftPackageURL else { return } NSWorkspace.shared.open(url) case .finder: guard let url = project?.urlPath else { return } NSWorkspace.shared.activateFileViewerSelecting([url]) case .openXcodeDerivedData: guard let url = URL(string: directDerivedDataFolderPath) else { return } NSWorkspace.shared.activateFileViewerSelecting([url]) default: break } } static func executeInTerminal(command: TerminalCommand, project: Project?) { guard let scriptText = command.script(for: project) else { return } let script = NSAppleScript(source: scriptText) var error: NSDictionary? script?.executeAndReturnError(&error) if var errorMessage = error?["NSAppleScriptErrorBriefMessage"] as? String { if let errorNumber = error?["NSAppleScriptErrorNumber"] as? NSNumber, errorNumber == NSNumber(integerLiteral: -1728) { errorMessage = "Please open Terminal app" } NSWorkspace.showErrorAlert(withMessage: errorMessage) } } } ================================================ FILE: XcodeProjects/Logic/Extensions/NotificationNames.swift ================================================ // // NotificationNames.swift // XcodeProjects // // Created by Dima Kalachniuk on 13/03/2021. // Copyright © 2021 com.klm.mac.myProjects. All rights reserved. // import Foundation extension NSNotification.Name { static let closePreferencesController = NSNotification.Name("closePreferencesController") } ================================================ FILE: XcodeProjects/Logic/Extensions/String+Components.swift ================================================ // // String+Components.swift // XcodeProjects // // Created by Dima Kalachniuk on 17/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation extension String { var lastComponent: Substring? { split(separator: "/").last } var removeFilePath: String? { replacingOccurrences(of: "file://", with: "") } } ================================================ FILE: XcodeProjects/Logic/Extensions/View+If.swift ================================================ // // View+If.swift // XcodeProjects // // Created by Dima Kalachniuk on 11/03/2021. // Copyright © 2021 com.klm.mac.myProjects. All rights reserved. // import SwiftUI extension View { @ViewBuilder func `if`( _ condition: Bool, transform: (Self) -> Transform ) -> some View { if condition { transform(self) } else { self } } } ================================================ FILE: XcodeProjects/Logic/ProfileFile.swift ================================================ // // ProfileFile.swift // XcodeProjects // // Created by Dima Kalachniuk on 29/06/2022. // Copyright © 2022 com.klm.mac.myProjects. All rights reserved. // import Foundation enum ProfileFile { case z case bash case custom(name: String, path: URL) } extension ProfileFile { var fileURL: URL { switch self { case .z, .bash: return parentURL.appendingPathComponent(fileName) // we need to add name of the file case .custom: return parentURL // name of the file is already in the parent path } } var aliases: [Alias] { let aliasString = "alias " var aliasesArray: [Alias] = [] let commands = content?.split(separator: "\n").map(String.init) for command in commands ?? [] where command.contains(aliasString) { let aliases = command.split(separator: "=").map(String.init) if let aliasName = aliases.first?.replacingOccurrences(of: aliasString, with: "") { aliasesArray.append(Alias(name: aliasName)) } } return aliasesArray } } fileprivate extension ProfileFile { var fileName: String { switch self { case .z: return ".zprofile" case .bash: return ".bash_profile" case .custom(let name, _): return name } } var parentURL: URL { switch self { case .z, .bash: return FileManager.default.homeDirectoryForCurrentUser case .custom(_, let path): return path } } var content: String? { do { return try String(contentsOf: fileURL, encoding: .utf8) } catch { return nil } } } extension ProfileFile: Equatable { static func ==(lhs: ProfileFile, rhs: ProfileFile) -> Bool { switch (lhs, rhs) { case (.z, .z), (.bash, .bash): return true case (.custom(let lhsName, let lhsURL), .custom(let rhsName, let rhsURL)): return lhsName == rhsName && lhsURL == rhsURL default: return false } } } ================================================ FILE: XcodeProjects/Logic/PropertyWrappers/UserDefaultsWrapper.swift ================================================ // // UserDefaultsWrapper.swift // KLCommons // // Created by Dima Kalachniuk on 17/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation private protocol AnyOptional { var isNil: Bool { get } } extension Optional: AnyOptional { var isNil: Bool { self == nil } } @propertyWrapper public struct UserDefault { let key: String let defaultValue: T var storage: UserDefaults = .standard public init(_ key: String, defaultValue: T, storage: UserDefaults = .standard) { self.key = key self.defaultValue = defaultValue self.storage = storage } public var wrappedValue: T { get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { if let optional = newValue as? AnyOptional, optional.isNil { storage.removeObject(forKey: key) } else { storage.setValue(newValue, forKey: key) } } } } public extension UserDefault where T: ExpressibleByNilLiteral { init(_ key: String, storage: UserDefaults = .standard) { self.init(key, defaultValue: nil, storage: storage) } } ================================================ FILE: XcodeProjects/Logic/UserDefaultsConfig.swift ================================================ // // UserDefaultsConfig.swift // Tim // // Created by Dima Kalachniuk on 17/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation struct UserDefaultsConfig { @UserDefault("projects", defaultValue: nil) static var projects: Data? @UserDefault("hintDisabled", defaultValue: false) static var hintDisabled: Bool @UserDefault("showProjectIcon", defaultValue: true) static var showProjectIcon: Bool @UserDefault("showAliases", defaultValue: true) static var showAliases: Bool @UserDefault("customTerminalCommands", defaultValue: nil) static var customTerminalCommands: Data? @UserDefault("aliases", defaultValue: nil) private static var aliases: Data? } extension UserDefaultsConfig { static var projectObjects: [Project] { if let projectsData = UserDefaultsConfig.projects { do { return try JSONDecoder().decode([Project].self, from: projectsData) } catch { } } return [] } static var customTerminalCommandObjects: [CustomCommand] { if let commandsData = UserDefaultsConfig.customTerminalCommands { do { return try JSONDecoder().decode([CustomCommand].self, from: commandsData) } catch { } } return [] } } extension UserDefaultsConfig { static func addNewAliasTerminalCommands(profileFiles: [ProfileFile]) { for file in profileFiles { UserDefaultsConfig.addNewAliasTerminalCommands(profileFile: file) } } private static func addNewAliasTerminalCommands(profileFile: ProfileFile) { UserDefaultsConfig.addNewAliasTerminalCommands(profileFile.aliases) } static func addNewAliasTerminalCommands(_ aliases: [Alias]) { let allAliases = Set(UserDefaultsConfig.aliasTerminalCommands + aliases) if let encodedAliases = try? JSONEncoder().encode(allAliases) { UserDefaultsConfig.aliases = encodedAliases } } static func removeAlias(_ alias: Alias) { var allAliases = UserDefaultsConfig.aliasTerminalCommands allAliases.removeAll(where: { $0.name == alias.name }) if let encodedAliases = try? JSONEncoder().encode(allAliases) { UserDefaultsConfig.aliases = encodedAliases } } static var aliasTerminalCommands: [Alias] { if let aliases = UserDefaultsConfig.aliases { do { return try JSONDecoder().decode([Alias].self, from: aliases) } catch {} } return [] } } ================================================ FILE: XcodeProjects/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "16x16", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 9.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 8-1.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 8.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 7.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 6.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 4-1.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 4.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 3-1.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy 3.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "photo_2020-06-17 10.58.36 copy.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/add.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "iconfinder_folder-plus_3325131.pdf" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/arrow.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "􀙚.pdf" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/close.imageset/Contents.json ================================================ { "images" : [ { "filename" : "close.pdf", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/gear.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "gear.pdf" } ], "info" : { "version" : 1, "author" : "xcode" }, "properties" : { "template-rendering-intent" : "template", "preserves-vector-representation" : true } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/more.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "more.pdf" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/packageSwift.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-swift.svg", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/plus.imageset/Contents.json ================================================ { "images" : [ { "filename" : "icons8-plus.svg", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } ================================================ FILE: XcodeProjects/Resourses/Assets.xcassets/terminal.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "code-terminal.pdf" } ], "info" : { "version" : 1, "author" : "xcode" }, "properties" : { "template-rendering-intent" : "template", "preserves-vector-representation" : true } } ================================================ FILE: XcodeProjects/Resourses/Base.lproj/Main.storyboard ================================================ Default Left to Right Right to Left Default Left to Right Right to Left ================================================ FILE: XcodeProjects/Resourses/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString 1.6.1 CFBundleVersion 1 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement NSAppleEventsUsageDescription Run Scripts NSFileProviderDomainUsageDescription NSHumanReadableCopyright Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass NSApplication NSSupportsAutomaticTermination NSSupportsSuddenTermination ================================================ FILE: XcodeProjects/Resourses/XcodeProjects.entitlements ================================================ com.apple.security.automation.apple-events com.apple.security.temporary-exception.apple-events com.apple.systemevents com.apple.terminal ================================================ FILE: XcodeProjects/Support/EventMonitor.swift ================================================ // // EventMonitor.swift // XcodeProjects // // Created by Dima Kalachniuk on 06/03/20. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Cocoa // Brought to you by: https://www.raywenderlich.com/450-menus-and-popovers-in-menu-bar-apps-for-macos public 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) } public func stop() { if monitor != nil { NSEvent.removeMonitor(monitor!) monitor = nil } } } ================================================ FILE: XcodeProjects/Support/Preferences.swift ================================================ // // Preferences.swift // StatusBuddy // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Foundation import Combine final class Preferences: ObservableObject { static let didChangeNotification = Notification.Name("com.dkcompany.xcodeprojects.PrefsChanged") private var appURL: URL { Bundle.main.bundleURL } @Published private var _launchAtLoginEnabled: Bool = false @Published private var _hintDisabled: Bool = UserDefaultsConfig.hintDisabled @Published private var _showProjectIcon: Bool = UserDefaultsConfig.showProjectIcon @Published private var _projects: [Project] = [] @Published private var _customTerminalCommands: [CustomCommand] = [] @Published private var _aliases: [Alias] = [] @Published private var _showAliases: Bool = UserDefaultsConfig.showAliases // is used after podfile.lock file or project's derived data was called // in order not to show that menu for the project again @Published var updateProjectMenu: Bool = false init() { _launchAtLoginEnabled = launchAtLoginEnabled _projects = UserDefaultsConfig.projectObjects _customTerminalCommands = UserDefaultsConfig.customTerminalCommandObjects _aliases = ProfileFile.z.aliases + ProfileFile.bash.aliases + UserDefaultsConfig.aliasTerminalCommands } private (set) var customTerminalCommands: [CustomCommand] { get { _customTerminalCommands } set (newCommands) { _customTerminalCommands = newCommands if let encodedCommands = try? JSONEncoder().encode(newCommands) { UserDefaultsConfig.customTerminalCommands = encodedCommands } } } private (set) var projects: [Project] { get { _projects } set (newProjects) { _projects = newProjects if let encodedProjects = try? JSONEncoder().encode(newProjects) { UserDefaultsConfig.projects = encodedProjects } } } private (set) var aliases: [Alias] { get { _aliases } set (newAliases) { _aliases = newAliases UserDefaultsConfig.addNewAliasTerminalCommands(newAliases) } } private (set) var hintDisabled: Bool { get { _hintDisabled } set (newHintDisabled) { _hintDisabled = newHintDisabled UserDefaultsConfig.hintDisabled = _hintDisabled } } private (set) var showProjectIcon: Bool { get { _showProjectIcon } set (newShowProjectIcon) { _showProjectIcon = newShowProjectIcon UserDefaultsConfig.showProjectIcon = _showProjectIcon } } private (set) var showAliases: Bool { get { _showAliases } set (newValue) { _showAliases = newValue UserDefaultsConfig.showAliases = newValue } } private (set) var launchAtLoginEnabled: Bool { get { _launchAtLoginEnabled || SharedFileList.sessionLoginItems().containsItem(appURL) } set { _launchAtLoginEnabled = newValue if newValue { SharedFileList.sessionLoginItems().addItem(appURL) } else { SharedFileList.sessionLoginItems().removeItem(appURL) } didChange() } } private func didChange() { NotificationCenter.default.post(name: Self.didChangeNotification, object: self) } } extension Preferences { func hideHint() { hintDisabled = true } func toggleShowProjectIcon() { showProjectIcon.toggle() } func toggleShowAliases() { showAliases.toggle() } func toggleLaunchAtLogin() { launchAtLoginEnabled.toggle() } } extension Preferences { func addAliases(_ newAliases: [Alias]) { aliases.append(contentsOf: newAliases) } func removeAlias(_ alias: Alias) { UserDefaultsConfig.removeAlias(alias) aliases.removeAll(where: { $0.name == alias.name }) } func addProjects(_ newProjects: [Project]) { projects.append(contentsOf: newProjects) } func moveProjects(from source: IndexSet, to destination: Int) { projects.move(fromOffsets: source, toOffset: destination) } func removeProject(_ project: Project) { projects.removeAll(where: { $0.name == project.name }) } func removeAllProjects() { projects.removeAll() } func changeProjectsColor(_ project: Project, newColor: CodableColor) { guard let index = projects.firstIndex(where: { $0.id == project.id }) else { return } project.color = newColor projects[index] = project CodableColorPicker.shared.setupUsedColors() } func changeProjectsIcon(_ project: Project, iconPath: String?) { guard let index = projects.firstIndex(where: { $0.id == project.id }) else { return } project.iconPath = iconPath projects[index] = project } func changeProjectsName(_ project: Project, newName: String) { guard let index = projects.firstIndex(where: { $0.id == project.id }) else { return } project.name = newName projects[index] = project } func addNewTerminalCommand(_ command: CustomCommand) -> Result { if !customTerminalCommands.contains(where: { $0.command == command.command }) { customTerminalCommands.append(command) return .success(true) } else { return .failure(.terminalCommandAlreadyExists) } } func removeCustomTerminalCommand(_ command: CustomCommand){ customTerminalCommands.removeAll(where: { $0.command == command.command }) } } enum PreferencesError: Error { case terminalCommandAlreadyExists } ================================================ FILE: XcodeProjects/Support/SharedFileList.h ================================================ // // SharedFileList.h // NoiseBuddy // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // #import NS_ASSUME_NONNULL_BEGIN @interface SharedFileList : NSObject + (instancetype)sessionLoginItems; @property (nonatomic, readonly) NSSet *items; @property (nonatomic, copy, nullable) void(^changeHandler)(SharedFileList *); - (BOOL)containsItem:(NSURL *)url; - (void)addItem:(NSURL *)url; - (void)removeItem:(NSURL *)url; @end NS_ASSUME_NONNULL_END ================================================ FILE: XcodeProjects/Support/SharedFileList.m ================================================ // // SharedFileList.m // SyzygyKit // // Created by Dave DeLong on 9/22/18. // Copyright © 2018 Syzygy. All rights reserved. // #import "SharedFileList.h" #import #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" void sharedFileListDidChange(LSSharedFileListRef inList, void *context); @implementation SharedFileList { LSSharedFileListRef _listRef; NSSet *_listSnapshot; } + (BOOL)automaticallyNotifiesObserversOfItems { return NO; } + (instancetype)sessionLoginItems { return [[self alloc] initWithType:kLSSharedFileListSessionLoginItems]; } - (instancetype)initWithType:(CFStringRef)type { self = [super init]; if (self) { _listRef = LSSharedFileListCreate(NULL, type, NULL); _listSnapshot = [self _snapshot]; LSSharedFileListAddObserver(_listRef, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (voidPtr)CFBridgingRetain(self)); } return self; } - (void)dealloc { LSSharedFileListRemoveObserver(_listRef, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, (__bridge void *)(self)); CFRelease(_listRef); } - (NSSet *)items { return [_listSnapshot copy]; } - (NSSet *)_snapshot { NSMutableSet *snapshot = [NSMutableSet set]; NSArray *listSnapshot = (NSArray *)CFBridgingRelease(LSSharedFileListCopySnapshot(_listRef, NULL)); for (id itemObject in listSnapshot) { LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = NULL; LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); NSURL *itemURL = CFBridgingRelease(currentItemURL); if (itemURL != nil) { [snapshot addObject:itemURL]; } } return snapshot; } - (void)_listDidChange { NSSet *newSnapshot = [self _snapshot]; [self willChangeValueForKey:@"items"]; _listSnapshot = newSnapshot; [self didChangeValueForKey:@"items"]; if (self.changeHandler != nil) { self.changeHandler(self); } } - (BOOL)containsItem:(NSURL *)url { return [_listSnapshot containsObject:url]; } - (void)addItem:(NSURL *)url { if ([self containsItem:url] == YES) { return; } LSSharedFileListInsertItemURL(_listRef, kLSSharedFileListItemLast, NULL, NULL, (__bridge CFURLRef)url, NULL, NULL); } - (void)removeItem:(NSURL *)url { if ([self containsItem:url] == NO) { return; } NSArray *listSnapshot = (NSArray *)CFBridgingRelease(LSSharedFileListCopySnapshot(_listRef, NULL)); for (id itemObject in listSnapshot) { LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject; UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = NULL; LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); NSURL *itemURL = CFBridgingRelease(currentItemURL); if ([itemURL isEqual:url]) { LSSharedFileListItemRemove(_listRef, item); } } } @end void sharedFileListDidChange(LSSharedFileListRef inList, void *context) { SharedFileList *list = (__bridge id)context; [list _listDidChange]; } #pragma clang diagnostic pop ================================================ FILE: XcodeProjects/Support/XcodeProjects-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // #import "SharedFileList.h" ================================================ FILE: XcodeProjects/UI/Buttons/AddAliasButton.swift ================================================ // // AddAliasButton.swift // XcodeProjects // // Created by Dima Kalachniuk on 14/07/2023. // Copyright © 2023 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct AddAliasButton: View { let action: (() -> Void) var body: some View { Button(action: action) { Image("plus") .resizable() .renderingMode(.template) .frame(width: 16, height: 16) .foregroundColor(Color(NSColor.labelColor)) }.buttonStyle(BorderlessButtonStyle()) } } struct AddAliasButton_Previews: PreviewProvider { static var previews: some View { AddAliasButton(action: {}) } } ================================================ FILE: XcodeProjects/UI/Buttons/AddCustomCommandButton.swift ================================================ // // AddCustomCommandButton.swift // XcodeProjects // // Created by Dima Kalachniuk on 23/02/2022. // Copyright © 2022 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct AddCustomCommandButton: View { let action: (() -> Void) var body: some View { Button(action: action) { Image("terminal") .resizable() .renderingMode(.template) .frame(width: 16, height: 16) .foregroundColor(Color(NSColor.labelColor)) }.buttonStyle(BorderlessButtonStyle()) } } ================================================ FILE: XcodeProjects/UI/Buttons/AddProjectButton.swift ================================================ // // PlusButton.swift // XcodeProjects // // Created by Dima Kalachniuk on 17/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct AddProjectButton: View { let action: (() -> Void) var body: some View { Button(action: action) { Image("add") .resizable() .renderingMode(.template) .frame(width: 16, height: 16) .foregroundColor(Color(NSColor.labelColor)) }.buttonStyle(BorderlessButtonStyle()) } } ================================================ FILE: XcodeProjects/UI/Buttons/PreferencesView.swift ================================================ // // PreferencesView.swift // StatusBuddy // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct PreferencesView: View { @EnvironmentObject var preferences: Preferences @State private var showingRemoveAllProjectsAlert = false @State private var showAliases = UserDefaultsConfig.showAliases var body: some View { MenuButton(label: Image("gear")) { DividerSection(title: "About") Button(action: openGithub, label: { Text("Open in GitHub") }) DividerSection(title: "Derived Data") Button(action: openDerivedData, label: { Text(TerminalCommand.openXcodeDerivedData.title) }) Button(action: clearDerivedData, label: { Text("Clear derived data") }) Group { DividerSection(title:"Preferences") Button(action: toggleLaunchAtLogin, label: { HStack(spacing: 4) { Image(systemName: "checkmark") .frame(width: 10, height: 10) //.opacity(self.preferences.launchAtLoginEnabled ? 1 : 0) .if(self.preferences.launchAtLoginEnabled == false) { $0.hidden() } Text("Launch at Login") }.offset(x: -14, y: 0) }) Button(action: toggleShowProjectIcon, label: { HStack(spacing: 4) { Image(systemName: "checkmark") .frame(width: 10, height: 10) // .opacity(self.preferences.showProjectIcon ? 1 : 0) .if(self.preferences.showProjectIcon == false) { $0.hidden() } Text("Show project's icon") }.offset(x: -14, y: 0) }) Button(action: { let controller = ProjectPreferencesViewController(preferences: preferences, type: .addTerminalCommand) controller.window?.title = "Add custom command" controller.showWindow(nil) AppDelegate.closePopover() }, label: { Text("Add custom command") }) } if !self.preferences.projects.isEmpty { DividerSection(title: nil) Button("Remove All projects") { showingRemoveAllProjectsAlert = true } } Group { DividerSection(title: nil) let text = showAliases ? "Turn Off Aliases Tab" : "Turn On Aliases Tab" Button(text) { preferences.toggleShowAliases() showAliases.toggle() } DividerSection(title: nil) Button(action: quit, label: { Text("Quit") }) } } .menuButtonStyle(BorderlessButtonMenuButtonStyle()) .alert(isPresented: $showingRemoveAllProjectsAlert, content: { Alert(title: Text("Remove all projects"), message: Text("You can't undo it later"), primaryButton: .default(Text("Remove"), action: { removeProjects() }), secondaryButton: .cancel(Text("No"))) }) } } private extension PreferencesView { func toggleLaunchAtLogin() { preferences.toggleLaunchAtLogin() } func toggleShowProjectIcon() { preferences.toggleShowProjectIcon() } func openGithub() { if let githubURL = URL(string: "https://github.com/DKalachniuk/XcodeProjects") { AppDelegate.closePopover() _ = NSWorkspace.shared.open(githubURL) } } func removeProjects() { preferences.removeAllProjects() } func quit() { NSApp.terminate(nil) } func clearDerivedData() { NSWorkspace.execute(command: .clearXcodeDerivedData) } func openDerivedData() { NSWorkspace.execute(command: .openXcodeDerivedData) } } struct PreferencesView_Previews: PreviewProvider { static var previews: some View { PreferencesView() } } ================================================ FILE: XcodeProjects/UI/Buttons/TerminalCommandButton.swift ================================================ // // TerminalCommandButton.swift // XcodeProjects // // Created by Dima Kalachniuk on 11/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct TerminalCommandButton: View { let project: Project let command: TerminalCommand var customName: String? var completion: (() -> Void)? var body: some View { Button(action: { AppDelegate.closePopover() NSWorkspace.execute(command: self.command, forProject: self.project) completion?() }) { Text(customName ?? command.title) } } } struct TerminalCommandButton_Previews: PreviewProvider { static var previews: some View { TerminalCommandButton(project: Project.dummy, command: .podInstall) } } ================================================ FILE: XcodeProjects/UI/Cells/AliasCell.swift ================================================ // // AliasCell.swift // XcodeProjects // // Created by Dima Kalachniuk on 29/06/2022. // Copyright © 2022 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct AliasCell: View { let alias: Alias @EnvironmentObject var preferences: Preferences var completion: (() -> Void)? var body: some View { VStack { HStack(spacing: 0) { AliasNameView(alias: alias) Divider() .frame(width: 2) .padding(0) MenuButton(label: ActionsMenuText()) { Button(action: { preferences.removeAlias(alias) }) { Text("Remove \(alias.name) from the list") } } .menuButtonStyle(BorderlessButtonMenuButtonStyle()) .frame(width: 40, height: 40, alignment: .center) .background(RoundedCorners.right) .modifier(OnHover(tl: 0, tr: 12, bl: 0, br: 12)) .modifier(OnHoverText()) } } .background(RoundedCorners.all) .onTapGesture { AppDelegate.closePopover() NSWorkspace.execute(command: .alias(command: alias.name)) completion?() } } } ================================================ FILE: XcodeProjects/UI/Cells/ProjectCell.swift ================================================ // // ProjectCell.swift // XcodeProjects // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ProjectCell: View { let project: Project @EnvironmentObject var preferences: Preferences var body: some View { VStack { HStack(spacing: 0) { ProjectNameView(project: self.project) .environmentObject(preferences) Divider() .frame(width: 2) .padding(0) ProjectMenuView(project: self.project) .environmentObject(preferences) .frame(width: 40, height: 40, alignment: .center) .background(RoundedCorners.right ) .modifier(OnHover(tl: 0, tr: 12, bl: 0, br: 12)) .modifier(OnHoverText()) } } } } struct ProjectCell_Previews: PreviewProvider { static var previews: some View { ProjectCell(project: Project.dummy) } } ================================================ FILE: XcodeProjects/UI/Controllers/ProjectPreferencesViewController.swift ================================================ // // ProjectPreferencesViewController.swift // XcodeProjects // // Created by Dima Kalachniuk on 13/08/2020. // Copyright © 2020 com.klm.mac.myProjects. All rights reserved. // import Cocoa import Combine import SwiftUI enum ProjectPreferencesType { case project case addTerminalCommand case addAlias var size: NSSize { let width = self == .project ? 300 : 400 let height = self == .project ? 140 : 190 return NSSize(width: width, height: height) } } class ProjectPreferencesViewController: NSWindowController { convenience init(project: Project? = nil, preferences: Preferences, type: ProjectPreferencesType) { let contentView = ProjectPreferencesView(type: type, project: project).environmentObject(preferences) let hostingController = NSHostingController(rootView: contentView.frame(width: type.size.width, height: type.size.height)) let window = NSWindow(contentViewController: hostingController) window.setContentSize(type.size) self.init(window: window) NotificationCenter.default.addObserver(forName: .closePreferencesController, object: nil, queue: nil) { [self] _ in dismiss() } } func dismiss() { close() AppDelegate.closePopover() } } ================================================ FILE: XcodeProjects/UI/Controllers/StatusViewController.swift ================================================ // // EventMonitor.swift // XcodeProjects // // Created by Dima Kalachniuk on 06/03/20. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import Cocoa import Combine import SwiftUI class StatusViewController: NSViewController { let preferences: Preferences init(preferences: Preferences) { self.preferences = preferences super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError() } override func loadView() { view = NSView() preferredContentSize = NSSize(width: 360, height: 360) let contentView = MainView().environmentObject(preferences) let hoster = NSHostingController(rootView: contentView) addChild(hoster) hoster.view.autoresizingMask = [.width, .height] hoster.view.frame = view.bounds view.addSubview(hoster.view) } } ================================================ FILE: XcodeProjects/UI/Images/ArrowImage.swift ================================================ // // ArrowImage.swift // XcodeProjects // // Created by Dima Kalachniuk on 24/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI enum ArrowSize { case normal case small var width: CGFloat { self == .normal ? 20 : 14 } var height: CGFloat { self == .normal ? 16.5 : 10 } } struct ArrowImage: View { var size: ArrowSize = .normal var body: some View { Image("arrow") .resizable() .renderingMode(.template) .frame(width: size.width, height: size.height, alignment: .center) } } struct ArrowImage_Previews: PreviewProvider { static var previews: some View { ArrowImage() } } ================================================ FILE: XcodeProjects/UI/Images/ArrowImageWithHoverPopover.swift ================================================ // // ArrowImageWIthHoverPopover.swift // XcodeProjects // // Created by Dima Kalachniuk on 01/07/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ArrowImageWithHoverPopover: View { let project: Project @State private var noXcodeProjectHover: Bool = false var body: some View { ArrowImage() .opacity(project.hasXcodeProject ? 1.0 : 0.1) .onHover(perform: { hovered in if !self.project.hasXcodeProject { self.noXcodeProjectHover = hovered } }) .popover(isPresented: $noXcodeProjectHover, content: { VStack(alignment: .trailing, spacing: 6) { Text("No Xcode project/workspace was found 😔") } .foregroundColor(Color(.secondaryLabelColor)) .font(.system(size: 11)) .padding() }) } } struct ArrowImageWIthHoverPopover_Previews: PreviewProvider { static var previews: some View { ArrowImageWithHoverPopover(project: Project.dummy) } } ================================================ FILE: XcodeProjects/UI/Images/CloseHintImage.swift ================================================ // // CloseHintImage.swift // XcodeProjects // // Created by Dima Kalachniuk on 24/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct CloseHintImage: View { var body: some View { Image("close") .resizable() .renderingMode(.template) .frame(width: 15, height: 15, alignment: .center) } } struct CloseHintImage_Previews: PreviewProvider { static var previews: some View { CloseHintImage() } } ================================================ FILE: XcodeProjects/UI/Images/PackageSwiftImage.swift ================================================ // // PackageSwiftImage.swift // XcodeProjects // // Created by Dima Kalachniuk on 12/07/2023. // Copyright © 2023 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct PackageSwiftImage: View { var size: ArrowSize = .normal var body: some View { Image("packageSwift") .resizable() .renderingMode(.template) .frame(width: size.width, height: size.height, alignment: .center) } } struct PackageSwiftImagee_Previews: PreviewProvider { static var previews: some View { ArrowImage() } } ================================================ FILE: XcodeProjects/UI/Modifiers/OnHover.swift ================================================ // // OnHover.swift // XcodeProjects // // Created by Dima Kalachniuk on 22/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct OnHover: ViewModifier { @State private var isHovered = false var tl: CGFloat = 0.0 var tr: CGFloat = 0.0 var bl: CGFloat = 0.0 var br: CGFloat = 0.0 func body(content: Content) -> some View { content .onHover(perform: { isHovered in self.isHovered = isHovered }) .background(isHovered ? RoundedCorners(color: Color.primary.opacity(0.2), tl: self.tl, tr: self.tr, bl: self.bl, br: self.br) : RoundedCorners(color: Color.clear)) } } ================================================ FILE: XcodeProjects/UI/Modifiers/OnHoverText.swift ================================================ // // OnHoverText.swift // XcodeProjects // // Created by Dima Kalachniuk on 22/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct OnHoverText: ViewModifier { @State private var isHovered = false func body(content: Content) -> some View { content .onHover(perform: { isHovered in self.isHovered = isHovered }) .foregroundColor(isHovered ? Color.primary : Color.primary.opacity(0.5)) } } ================================================ FILE: XcodeProjects/UI/Modifiers/RoundedCorners.swift ================================================ // // RoundedCorners.swift // XcodeProjects // // Created by Dima Kalachniuk on 22/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct RoundedCorners: View { var color: Color var tl: CGFloat = 0.0 var tr: CGFloat = 0.0 var bl: CGFloat = 0.0 var br: CGFloat = 0.0 var body: some View { GeometryReader { geometry in Path { path in let w = geometry.size.width let h = geometry.size.height // Make sure we do not exceed the size of the rectangle let tr = min(min(self.tr, h/2), w/2) let tl = min(min(self.tl, h/2), w/2) let bl = min(min(self.bl, h/2), w/2) let br = min(min(self.br, h/2), w/2) path.move(to: CGPoint(x: w / 2.0, y: 0)) path.addLine(to: CGPoint(x: w - tr, y: 0)) path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false) path.addLine(to: CGPoint(x: w, y: h - br)) path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false) path.addLine(to: CGPoint(x: bl, y: h)) path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false) path.addLine(to: CGPoint(x: 0, y: tl)) path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false) } .fill(self.color) } } } extension RoundedCorners { static let right = RoundedCorners(color: Color.gray.opacity(0.25), tl: 0, tr: 12, bl: 0, br: 12) static let left = RoundedCorners(color: Color.gray.opacity(0.25), tl: 12, tr: 0, bl: 12, br: 0) static let all = RoundedCorners(color: Color.gray.opacity(0.25), tl: 12, tr: 12, bl: 12, br: 12) } ================================================ FILE: XcodeProjects/UI/Views/ActionsMenuText.swift ================================================ // // ActionsMenuText.swift // XcodeProjects // // Created by Dima Kalachniuk on 22/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ActionsMenuText: View { var body: some View { Text(" ...") } } ================================================ FILE: XcodeProjects/UI/Views/AliasNameView.swift ================================================ // // AliasNameView.swift // XcodeProjects // // Created by Dima Kalachniuk on 14/07/2023. // Copyright © 2023 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct AliasNameView: View { let alias: Alias var body: some View { HStack { Spacer() Text(alias.name) Spacer() } .frame(height: 40) .background(RoundedCorners.left) .modifier(OnHover(tl: 12, tr: 0, bl: 12, br: 0)) } } struct AliasCellView_Previews: PreviewProvider { static var previews: some View { AliasNameView(alias: Alias(name: "pod install")) } } ================================================ FILE: XcodeProjects/UI/Views/DividerSection.swift ================================================ // // DividerSection.swift // XcodeProjects // // Created by Dima Kalachniuk on 20/03/2021. // Copyright © 2021 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct DividerSection: View { let title: String? var body: some View { VStack { Divider() if let title = title { Text(title) } } } } ================================================ FILE: XcodeProjects/UI/Views/HintView.swift ================================================ // // HintView.swift // XcodeProjects // // Created by Dima Kalachniuk on 24/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct HintView: View { @EnvironmentObject var preferences: Preferences @State var showCloseButton = false var body: some View { VStack(spacing: 8) { Spacer().frame(height: 1) HStack { Spacer().frame(width: 8) ArrowImage(size: .small) .foregroundColor(Color.secondary) Text("- open with Xcode. Click 􀍠 for more options") .font(Font.system(size: 10)) .foregroundColor(Color.secondary) Spacer() if showCloseButton { HStack { Button(action: { self.preferences.hideHint() }, label: { CloseHintImage() }) .buttonStyle(BorderlessButtonStyle()) .frame(width: 10, height: 10, alignment: .center) Spacer().frame(width: 15) } } } HStack { Spacer().frame(width: 8) Text("long tap on project's icon to set custom image icon") .font(Font.system(size: 10)) .foregroundColor(Color.secondary) Spacer() } } .padding(.leading, 10) .onHover { result in self.showCloseButton = result } .opacity(0.8) } } struct HintView_Previews: PreviewProvider { static var previews: some View { HintView() } } ================================================ FILE: XcodeProjects/UI/Views/MainView.swift ================================================ // // MainView.swift // StatusBuddy // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct MainView: View { @State private var searchTerm = "" //@State private var listToShow: ListToDisplay = .projects @State private var listToShow: Int = 0 @EnvironmentObject var preferences: Preferences // enum ListToDisplay: Int { // case projects = 0 // case aliases // } private var projects: [Project] { preferences.projects.filter({ searchTerm.isEmpty ? true : $0.name.lowercased().contains(searchTerm.lowercased()) }) } var body: some View { VStack(spacing: 0) { HStack(spacing: 15) { Spacer().frame(width: 0) TextField("Search in projects", text: $searchTerm) .frame(width: 215) .textFieldStyle(RoundedBorderTextFieldStyle()) AddProjectButton(action: addProject) AddCustomCommandButton(action: addCustomCommand) PreferencesView() }.padding(EdgeInsets(top: 10, leading: 10, bottom: 5, trailing: 10)) Divider().padding([.top], 3) if listToShow == 0 || preferences.showAliases == false { if projects.isEmpty { Spacer() if searchTerm.isEmpty { VStack { HStack { Text("Please add a project") AddProjectButton(action: addProject) } } } else { Text("No projects") } Spacer() } else { VStack { if self.preferences.hintDisabled == false { HintView().environmentObject(self.preferences) } List { ForEach(projects) { project in ProjectCell(project: project).environmentObject(self.preferences) } .onMove(perform: move) } .padding(0) } } } else { VStack { Spacer().frame(height: 10) HStack(spacing: 10) { Text("Add aliases from custom script file") AddProjectButton(action: parseCustomScriptFile) Text("Add alias") AddAliasButton(action: addCustomAlias) } let allAliases = preferences.aliases if allAliases.isEmpty { Group { Spacer() Text("No aliases were detected. Please add them") Spacer() } } else { List { ForEach(allAliases) { alias in AliasCell(alias: alias, completion: nil).environmentObject(self.preferences) } } } } } if preferences.showAliases { Picker("", selection: $listToShow) { Text("Projects").tag(0) Text("Aliases").tag(1) } .pickerStyle(SegmentedPickerStyle()) .padding([.leading], -8) .padding() } } } func move(from source: IndexSet, to destination: Int) { if searchTerm.isEmpty { preferences.moveProjects(from: source, to: destination) } } } extension MainView { private func parseCustomScriptFile() { let appDelegate: AppDelegate? = NSApplication.shared.delegate as? AppDelegate let dialog = NSOpenPanel() dialog.title = "Choose script file to parse" dialog.showsResizeIndicator = true dialog.showsHiddenFiles = true dialog.canChooseDirectories = false dialog.canCreateDirectories = false dialog.allowsMultipleSelection = true dialog.canChooseFiles = true dialog.becomesKeyOnlyIfNeeded = true appDelegate?.closePopover(sender: nil) if dialog.runModal() == NSApplication.ModalResponse.OK { let newProfileFiles = dialog.urls.map{ProfileFile.custom(name: $0.lastPathComponent, path: $0.standardizedFileURL)} preferences.addAliases(newProfileFiles.flatMap{$0.aliases}) } else { print("something went wrong") } appDelegate?.showPopover(sender: nil) } private func addProject() { let appDelegate: AppDelegate? = NSApplication.shared.delegate as? AppDelegate let dialog = NSOpenPanel() dialog.title = "Choose a folder with your project/workspace" dialog.showsResizeIndicator = true dialog.showsHiddenFiles = false dialog.canChooseDirectories = true dialog.canCreateDirectories = true dialog.allowsMultipleSelection = true dialog.canChooseFiles = false dialog.becomesKeyOnlyIfNeeded = true appDelegate?.closePopover(sender: nil) if dialog.runModal() == NSApplication.ModalResponse.OK { let projectUrls = dialog.urls let projects = projectUrls.compactMap { Project(url: $0) } preferences.addProjects(projects) appDelegate?.showPopover(sender: nil) } else { print("something went wrong") appDelegate?.showPopover(sender: nil) } } private func addCustomCommand() { let controller = ProjectPreferencesViewController(preferences: preferences, type: .addTerminalCommand) controller.window?.title = "Add custom command" controller.showWindow(nil) AppDelegate.closePopover() } private func addCustomAlias() { let controller = ProjectPreferencesViewController(preferences: preferences, type: .addAlias) controller.window?.title = "Add custom alias" controller.showWindow(nil) AppDelegate.closePopover() } } ================================================ FILE: XcodeProjects/UI/Views/ProjectIcon.swift ================================================ // // ProjectIcon.swift // XcodeProjects // // Created by Dima Kalachniuk on 21/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ProjectIcon: View { let project: Project @EnvironmentObject var preferences: Preferences var body: some View { ZStack { if let iconPath = project.iconPath, let data = try? Data(contentsOf: URL(fileURLWithPath: iconPath)), let nsImage = NSImage(data: data) { Image(nsImage: nsImage) .resizable() .aspectRatio(contentMode: .fill) } else { Rectangle() .foregroundColor(Color.clear) .background(Color(self.project.color.color)) .cornerRadius(8) Text(String(self.project.name.first ?? "-").capitalized) .font(.system(size: 14, weight: Font.Weight.semibold)) .padding(EdgeInsets(top: 0, leading: 4, bottom: 0, trailing: 4)) .foregroundColor(Color.white) } } .frame(width: 24, height: 24) .onTapGesture { let newColor = CodableColorPicker.shared.pickRandomColor() self.preferences.changeProjectsColor(self.project, newColor: newColor) } .onLongPressGesture { showImagePicker() } } } private extension ProjectIcon { func showImagePicker() { let dialog = NSOpenPanel() dialog.title = "Choose an icon for your project" dialog.showsResizeIndicator = true dialog.showsHiddenFiles = false dialog.canChooseDirectories = false dialog.canCreateDirectories = false dialog.allowsMultipleSelection = false dialog.canChooseFiles = true dialog.becomesKeyOnlyIfNeeded = true dialog.allowedFileTypes = ["png","jpg","jpeg"] dialog.begin { (result) -> Void in if result.rawValue == NSApplication.ModalResponse.OK.rawValue { guard let selectedPath = dialog.url?.path else { return } self.preferences.changeProjectsIcon(self.project, iconPath: selectedPath) } } let appDelegate: AppDelegate? = NSApplication.shared.delegate as? AppDelegate appDelegate?.closePopover(sender: nil) } } struct ProjectIcon_Previews: PreviewProvider { static var previews: some View { ProjectIcon(project: Project.dummy) } } ================================================ FILE: XcodeProjects/UI/Views/ProjectMenuView.swift ================================================ // // ProjectMenuView.swift // XcodeProjects // // Created by Dima Kalachniuk on 09/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ProjectMenuView: View { let project: Project @EnvironmentObject var preferences: Preferences @State private var hover = false var body: some View { MenuButton(label: ActionsMenuText()) { Group { TerminalCommandButton(project: project, command: .openInTerminal) TerminalCommandButton(project: project, command: .finder) } Group { if NSWorkspace.shared.sourceTreeAppInstalled { TerminalCommandButton(project: project, command: .sourceTree) } if NSWorkspace.shared.forkAppInstalled { TerminalCommandButton(project: project, command: .fork) } } if project.hasCocoapods { DividerSection(title: "Cocoapods") TerminalCommandButton(project: project, command: .podInstall) TerminalCommandButton(project: project, command: .podUpdate) TerminalCommandButton(project: project, command: .podDeintegrate) if project.hasPodfileLock { TerminalCommandButton(project: project, command: .removePodfileLock) { preferences.updateProjectMenu = true } } } customCommandButtons() DividerSection(title: "Preferences") if project.hasDerivedData { TerminalCommandButton(project: project, command: .clearProjectDerivedData, customName: "Clear \(project.name) derived data folder") { preferences.updateProjectMenu = true } } Button(action: { let controller = ProjectPreferencesViewController(project: project, preferences: preferences, type: .project) controller.window?.title = "\(self.project.name)'s preferences" controller.showWindow(nil) AppDelegate.closePopover() }) { Text("Rename project") } if project.iconPath != nil { Button(action: { self.preferences.changeProjectsIcon(self.project, iconPath: nil) }) { Text("Remove project's icon") } } Button(action: { preferences.removeProject(project) }) { Text("Remove \(project.name) from the list") } } .menuButtonStyle(BorderlessButtonMenuButtonStyle()) } } extension ProjectMenuView { func customCommandButtons() -> some View { VStack { if !preferences.customTerminalCommands.isEmpty { DividerSection(title: "Custom commands") ForEach(preferences.customTerminalCommands, id: \.self) { customCommand in CustomTerminalCommandButton(command: customCommand, project: project).environmentObject(preferences) } } } } } struct CustomTerminalCommandButton: View { let command: CustomCommand let project: Project @EnvironmentObject var preferences: Preferences var body: some View { MenuButton(label: Text(command.name)) { TerminalCommandButton(project: project, command: .custom(command: command.fullCommand(for: project))) Button(action: { preferences.removeCustomTerminalCommand(command) }) { HStack(spacing: 4) { Text("Remove \(command.name)") Image(systemName: "minus.circle.fill") .frame(width: 10, height: 10) } } } } } struct ProjectMenuView_Previews: PreviewProvider { static var previews: some View { ProjectMenuView(project: Project.dummy).environmentObject(Preferences()) } } ================================================ FILE: XcodeProjects/UI/Views/ProjectNameView.swift ================================================ // // ProjectNameView.swift // XcodeProjects // // Created by Dima Kalachniuk on 22/06/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import SwiftUI struct ProjectNameView: View { let project: Project @EnvironmentObject var preferences: Preferences var body: some View { HStack { Spacer() .frame(width: 8) if self.preferences.showProjectIcon { ProjectIcon(project: project).environmentObject(preferences) } Text(self.project.name) .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 5)) // open project Spacer() Button(action: { AppDelegate.closePopover() NSWorkspace.execute(command: .openWorkspace, forProject: self.project) }) { ArrowImageWithHoverPopover(project: self.project) } .buttonStyle(BorderlessButtonStyle()) .padding([.trailing], 5) .modifier(OnHoverText()) // open package swift if exists if project.hasSwiftPackage { Button(action: { AppDelegate.closePopover() NSWorkspace.execute(command: .openSwiftPackage, forProject: self.project) }) { PackageSwiftImage() } .buttonStyle(BorderlessButtonStyle()) .padding([.trailing], 5) .modifier(OnHoverText()) } Spacer().frame(width: 3) } .frame(height: 40) .background(RoundedCorners.left) .modifier(OnHover(tl: 12, tr: 0, bl: 12, br: 0)) } } struct ProjectNameView_Previews: PreviewProvider { static var previews: some View { ProjectNameView(project: Project.dummy) } } ================================================ FILE: XcodeProjects/UI/Views/ProjectPreferencesView.swift ================================================ // // ProjectPreferencesView.swift // XcodeProjects // // Created by Dima Kalachniuk on 13/08/2020. // Copyright © 2020 com.klm.mac.myProjects. All rights reserved. // import SwiftUI struct ProjectPreferencesView: View { let type: ProjectPreferencesType let project: Project? @State var newCommandTitle: String = "" @State var newValue: String = "" @EnvironmentObject var preferences: Preferences var body: some View { VStack { VStack(alignment: .leading, spacing: 5) { if type == .addTerminalCommand { Text(titleAddTheCommand) TextField(titleAddTheCommand, text: $newCommandTitle) } Text(title) TextField(value, text: $newValue) }.padding() Button(action: { if value != newValue { switch type { case .project: if let project = project { preferences.changeProjectsName(project, newName: newValue) } case .addTerminalCommand: let result = preferences.addNewTerminalCommand(CustomCommand(name: newCommandTitle, command: newValue)) switch result { case .failure(let error): NSWorkspace.showErrorAlert(withMessage: error.localizedDescription) break case .success: break } case .addAlias: preferences.addAliases([Alias(name: newValue)]) break } NotificationCenter.default.post(name: .closePreferencesController, object: nil) } }, label: { Text(saveButton) }) .disabled(newValue.isEmpty) } } } private extension ProjectPreferencesView { var titleAddTheCommand: String { "Name of the command" } var title: String { switch type { case .project: return "New name" case .addTerminalCommand: return "Terminal command" case .addAlias: return "Alias to call" } } var value: String { switch type { case .project: return project?.name ?? "" case .addTerminalCommand: return "cd will be added automatically" case .addAlias: return "" } } var saveButton: String { switch type { case .project: return "Rename" case .addTerminalCommand: return "Add new command" case .addAlias: return "Add new alias" } } } struct ProjectPreferencesView_Previews: PreviewProvider { static var previews: some View { ProjectPreferencesView(type: .project, project: Project.dummy, newValue: "something") } } ================================================ FILE: XcodeProjects.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 6EC78A69286C7167007F453C /* Alias.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC78A68286C7167007F453C /* Alias.swift */; }; 6EC78A6B286C7808007F453C /* AliasCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC78A6A286C7808007F453C /* AliasCell.swift */; }; 6EC78A6D286C9E72007F453C /* ProfileFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC78A6C286C9E72007F453C /* ProfileFile.swift */; }; 6EE247962A5F453A00DC2B4A /* PackageSwiftImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EE247952A5F453A00DC2B4A /* PackageSwiftImage.swift */; }; 6EE2479A2A61488E00DC2B4A /* AddAliasButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EE247992A61488E00DC2B4A /* AddAliasButton.swift */; }; 6EE2479C2A61669700DC2B4A /* AliasNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EE2479B2A61669700DC2B4A /* AliasNameView.swift */; }; C8088D562418E5F5001239CB /* TerminalCommandButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8088D552418E5F5001239CB /* TerminalCommandButton.swift */; }; C80D1228241583E800CBCB0F /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D1227241583E800CBCB0F /* PreferencesView.swift */; }; C80D122A2415840300CBCB0F /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80D12292415840300CBCB0F /* Preferences.swift */; }; C80D122F2415842B00CBCB0F /* SharedFileList.m in Sources */ = {isa = PBXBuildFile; fileRef = C80D122E2415842B00CBCB0F /* SharedFileList.m */; }; C80E916624A39ADF0073CF3F /* ArrowImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80E916524A39ADF0073CF3F /* ArrowImage.swift */; }; C80E916824A39B570073CF3F /* HintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80E916724A39B570073CF3F /* HintView.swift */; }; C80E916C24A39E2C0073CF3F /* CloseHintImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80E916B24A39E2C0073CF3F /* CloseHintImage.swift */; }; C8412E2A24129B4F0066A8A5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E2924129B4F0066A8A5 /* AppDelegate.swift */; }; C8412E2E24129B530066A8A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8412E2D24129B530066A8A5 /* Assets.xcassets */; }; C8412E3124129B530066A8A5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8412E3024129B530066A8A5 /* Preview Assets.xcassets */; }; C8412E3424129B530066A8A5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C8412E3224129B530066A8A5 /* Main.storyboard */; }; C8412E4024129B530066A8A5 /* XcodeProjectsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E3F24129B530066A8A5 /* XcodeProjectsTests.swift */; }; C8412E4B2412A23B0066A8A5 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E4A2412A23B0066A8A5 /* EventMonitor.swift */; }; C8412E4F2412A2A00066A8A5 /* StatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E4E2412A2A00066A8A5 /* StatusViewController.swift */; }; C8412E512412A2F00066A8A5 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E502412A2F00066A8A5 /* MainView.swift */; }; C8412E822412A70F0066A8A5 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8412E812412A70F0066A8A5 /* Project.swift */; }; C8456CF1249A540F00BAEF17 /* AddProjectButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8456CF0249A540F00BAEF17 /* AddProjectButton.swift */; }; C855572724A14300006AF499 /* OnHover.swift in Sources */ = {isa = PBXBuildFile; fileRef = C855572624A14300006AF499 /* OnHover.swift */; }; C855572924A144F8006AF499 /* RoundedCorners.swift in Sources */ = {isa = PBXBuildFile; fileRef = C855572824A144F8006AF499 /* RoundedCorners.swift */; }; C855572D24A14A01006AF499 /* ActionsMenuText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C855572C24A14A00006AF499 /* ActionsMenuText.swift */; }; C855572F24A14B30006AF499 /* ProjectNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C855572E24A14B30006AF499 /* ProjectNameView.swift */; }; C855573124A15104006AF499 /* OnHoverText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C855573024A15104006AF499 /* OnHoverText.swift */; }; C8573C44249D6EA600E1F969 /* FileManger+Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8573C43249D6EA600E1F969 /* FileManger+Content.swift */; }; C8573C49249E77E400E1F969 /* TerminalScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8573C48249E77E400E1F969 /* TerminalScript.swift */; }; C874747226069D600091E791 /* DividerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C874747126069D600091E791 /* DividerSection.swift */; }; C8A4C09B25FACA280008054A /* View+If.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A4C09A25FACA280008054A /* View+If.swift */; }; C8A4C09F25FADFBA0008054A /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = C8A4C09E25FADFB90008054A /* README.md */; }; C8AE963524997AC000E6B080 /* String+Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8AE963424997AC000E6B080 /* String+Components.swift */; }; C8AE96372499818200E6B080 /* CharacterSet+All.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8AE96362499818200E6B080 /* CharacterSet+All.swift */; }; C8BE992324CA1635000D4E5E /* ProjectIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BE992224CA1635000D4E5E /* ProjectIcon.swift */; }; C8BE992424CA1732000D4E5E /* NSColor+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8573C4E24A0045200E1F969 /* NSColor+Codable.swift */; }; C8D35B3624CC2CB00075308D /* CodableColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D35B3524CC2CB00075308D /* CodableColorPicker.swift */; }; C8D9623824AD31F100B70B23 /* ArrowImageWithHoverPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D9623724AD31F000B70B23 /* ArrowImageWithHoverPopover.swift */; }; C8D9623A24AD348600B70B23 /* NSWorkspace+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D9623924AD348600B70B23 /* NSWorkspace+App.swift */; }; C8D9F49425FCCE61004C952C /* ProjectPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D9F49325FCCE61004C952C /* ProjectPreferencesView.swift */; }; C8D9F49725FCCE80004C952C /* ProjectPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D9F49625FCCE80004C952C /* ProjectPreferencesViewController.swift */; }; C8D9F49A25FCD5F6004C952C /* NotificationNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D9F49925FCD5F6004C952C /* NotificationNames.swift */; }; C8DB027227C6DD860048157A /* CustomCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB027127C6DD860048157A /* CustomCommand.swift */; }; C8DB027427C6E4410048157A /* AddCustomCommandButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB027327C6E4410048157A /* AddCustomCommandButton.swift */; }; C8F450F1241632D1002B9D69 /* ProjectCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F450F0241632D1002B9D69 /* ProjectCell.swift */; }; C8F450F6241638C2002B9D69 /* TerminalCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F450F5241638C2002B9D69 /* TerminalCommand.swift */; }; C8F450FA24165C21002B9D69 /* UserDefaultsWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F450F924165C21002B9D69 /* UserDefaultsWrapper.swift */; }; C8F450FC24165C29002B9D69 /* UserDefaultsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F450FB24165C29002B9D69 /* UserDefaultsConfig.swift */; }; C8F450FF24167592002B9D69 /* NSWorkspace+Execute.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F450FE24167592002B9D69 /* NSWorkspace+Execute.swift */; }; C8F451012416950D002B9D69 /* ProjectMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F451002416950D002B9D69 /* ProjectMenuView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ C8412E3C24129B530066A8A5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C8412E1E24129B4F0066A8A5 /* Project object */; proxyType = 1; remoteGlobalIDString = C8412E2524129B4F0066A8A5; remoteInfo = MyProjects; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ C80D12382415868600CBCB0F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 6EC78A68286C7167007F453C /* Alias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alias.swift; sourceTree = ""; }; 6EC78A6A286C7808007F453C /* AliasCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AliasCell.swift; sourceTree = ""; }; 6EC78A6C286C9E72007F453C /* ProfileFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileFile.swift; sourceTree = ""; }; 6EE247952A5F453A00DC2B4A /* PackageSwiftImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageSwiftImage.swift; sourceTree = ""; }; 6EE247992A61488E00DC2B4A /* AddAliasButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAliasButton.swift; sourceTree = ""; }; 6EE2479B2A61669700DC2B4A /* AliasNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AliasNameView.swift; sourceTree = ""; }; C8088D552418E5F5001239CB /* TerminalCommandButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCommandButton.swift; sourceTree = ""; }; C80D1227241583E800CBCB0F /* PreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; C80D12292415840300CBCB0F /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; C80D122C2415842A00CBCB0F /* XcodeProjects-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XcodeProjects-Bridging-Header.h"; sourceTree = ""; }; C80D122D2415842B00CBCB0F /* SharedFileList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharedFileList.h; sourceTree = ""; }; C80D122E2415842B00CBCB0F /* SharedFileList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharedFileList.m; sourceTree = ""; }; C80E916524A39ADF0073CF3F /* ArrowImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowImage.swift; sourceTree = ""; }; C80E916724A39B570073CF3F /* HintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HintView.swift; sourceTree = ""; }; C80E916B24A39E2C0073CF3F /* CloseHintImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseHintImage.swift; sourceTree = ""; }; C8412E2624129B4F0066A8A5 /* XcodeProjects.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XcodeProjects.app; sourceTree = BUILT_PRODUCTS_DIR; }; C8412E2924129B4F0066A8A5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C8412E2D24129B530066A8A5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C8412E3024129B530066A8A5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; C8412E3324129B530066A8A5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C8412E3524129B530066A8A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C8412E3624129B530066A8A5 /* XcodeProjects.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = XcodeProjects.entitlements; sourceTree = ""; }; C8412E3B24129B530066A8A5 /* XcodeProjectsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XcodeProjectsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C8412E3F24129B530066A8A5 /* XcodeProjectsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeProjectsTests.swift; sourceTree = ""; }; C8412E4124129B530066A8A5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C8412E4A2412A23B0066A8A5 /* EventMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = ""; }; C8412E4E2412A2A00066A8A5 /* StatusViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusViewController.swift; sourceTree = ""; }; C8412E502412A2F00066A8A5 /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; C8412E812412A70F0066A8A5 /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = ""; }; C8456CF0249A540F00BAEF17 /* AddProjectButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddProjectButton.swift; sourceTree = ""; }; C855572624A14300006AF499 /* OnHover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnHover.swift; sourceTree = ""; }; C855572824A144F8006AF499 /* RoundedCorners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCorners.swift; sourceTree = ""; }; C855572C24A14A00006AF499 /* ActionsMenuText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionsMenuText.swift; sourceTree = ""; }; C855572E24A14B30006AF499 /* ProjectNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectNameView.swift; sourceTree = ""; }; C855573024A15104006AF499 /* OnHoverText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnHoverText.swift; sourceTree = ""; }; C8573C43249D6EA600E1F969 /* FileManger+Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManger+Content.swift"; sourceTree = ""; }; C8573C48249E77E400E1F969 /* TerminalScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalScript.swift; sourceTree = ""; }; C8573C4E24A0045200E1F969 /* NSColor+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Codable.swift"; sourceTree = ""; }; C874747126069D600091E791 /* DividerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DividerSection.swift; sourceTree = ""; }; C8A4C09A25FACA280008054A /* View+If.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+If.swift"; sourceTree = ""; }; C8A4C09E25FADFB90008054A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; C8AE963424997AC000E6B080 /* String+Components.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Components.swift"; sourceTree = ""; }; C8AE96362499818200E6B080 /* CharacterSet+All.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+All.swift"; sourceTree = ""; }; C8BE992224CA1635000D4E5E /* ProjectIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectIcon.swift; sourceTree = ""; }; C8D35B3524CC2CB00075308D /* CodableColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableColorPicker.swift; sourceTree = ""; }; C8D9623724AD31F000B70B23 /* ArrowImageWithHoverPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowImageWithHoverPopover.swift; sourceTree = ""; }; C8D9623924AD348600B70B23 /* NSWorkspace+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+App.swift"; sourceTree = ""; }; C8D9F49325FCCE61004C952C /* ProjectPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectPreferencesView.swift; sourceTree = ""; }; C8D9F49625FCCE80004C952C /* ProjectPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectPreferencesViewController.swift; sourceTree = ""; }; C8D9F49925FCD5F6004C952C /* NotificationNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationNames.swift; sourceTree = ""; }; C8DB027127C6DD860048157A /* CustomCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCommand.swift; sourceTree = ""; }; C8DB027327C6E4410048157A /* AddCustomCommandButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCustomCommandButton.swift; sourceTree = ""; }; C8F450F0241632D1002B9D69 /* ProjectCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectCell.swift; sourceTree = ""; }; C8F450F5241638C2002B9D69 /* TerminalCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalCommand.swift; sourceTree = ""; }; C8F450F924165C21002B9D69 /* UserDefaultsWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsWrapper.swift; sourceTree = ""; }; C8F450FB24165C29002B9D69 /* UserDefaultsConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsConfig.swift; sourceTree = ""; }; C8F450FE24167592002B9D69 /* NSWorkspace+Execute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+Execute.swift"; sourceTree = ""; }; C8F451002416950D002B9D69 /* ProjectMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectMenuView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ C8412E2324129B4F0066A8A5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; C8412E3824129B530066A8A5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ C80D122B2415841F00CBCB0F /* Support */ = { isa = PBXGroup; children = ( C8412E4A2412A23B0066A8A5 /* EventMonitor.swift */, C80D122D2415842B00CBCB0F /* SharedFileList.h */, C80D122E2415842B00CBCB0F /* SharedFileList.m */, C80D12292415840300CBCB0F /* Preferences.swift */, C80D122C2415842A00CBCB0F /* XcodeProjects-Bridging-Header.h */, ); path = Support; sourceTree = ""; }; C80D12352415868600CBCB0F /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; C80E916424A39ACD0073CF3F /* Images */ = { isa = PBXGroup; children = ( C80E916524A39ADF0073CF3F /* ArrowImage.swift */, C8D9623724AD31F000B70B23 /* ArrowImageWithHoverPopover.swift */, C80E916B24A39E2C0073CF3F /* CloseHintImage.swift */, 6EE247952A5F453A00DC2B4A /* PackageSwiftImage.swift */, ); path = Images; sourceTree = ""; }; C8412E1D24129B4F0066A8A5 = { isa = PBXGroup; children = ( C8412E2824129B4F0066A8A5 /* XcodeProjects */, C8412E3E24129B530066A8A5 /* XcodeProjectsTests */, C8412E2724129B4F0066A8A5 /* Products */, C80D12352415868600CBCB0F /* Frameworks */, C8BE992124CA161A000D4E5E /* Recovered References */, ); sourceTree = ""; }; C8412E2724129B4F0066A8A5 /* Products */ = { isa = PBXGroup; children = ( C8412E2624129B4F0066A8A5 /* XcodeProjects.app */, C8412E3B24129B530066A8A5 /* XcodeProjectsTests.xctest */, ); name = Products; sourceTree = ""; }; C8412E2824129B4F0066A8A5 /* XcodeProjects */ = { isa = PBXGroup; children = ( C8A4C09E25FADFB90008054A /* README.md */, C8456CE8249A1C0400BAEF17 /* Resourses */, C80D122B2415841F00CBCB0F /* Support */, C8F450F4241638B9002B9D69 /* Logic */, C8412E802412A7010066A8A5 /* Data */, C8412E4C2412A2950066A8A5 /* UI */, C8412E2924129B4F0066A8A5 /* AppDelegate.swift */, C8412E2F24129B530066A8A5 /* Preview Content */, ); path = XcodeProjects; sourceTree = ""; }; C8412E2F24129B530066A8A5 /* Preview Content */ = { isa = PBXGroup; children = ( C8412E3024129B530066A8A5 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; C8412E3E24129B530066A8A5 /* XcodeProjectsTests */ = { isa = PBXGroup; children = ( C8412E3F24129B530066A8A5 /* XcodeProjectsTests.swift */, C8412E4124129B530066A8A5 /* Info.plist */, ); path = XcodeProjectsTests; sourceTree = ""; }; C8412E4C2412A2950066A8A5 /* UI */ = { isa = PBXGroup; children = ( C80E916424A39ACD0073CF3F /* Images */, C855572524A142F0006AF499 /* Modifiers */, C8456CEF249A53F000BAEF17 /* Buttons */, C8F450EF241632C3002B9D69 /* Cells */, C8412E522412A2F20066A8A5 /* Views */, C8412E4D2412A29A0066A8A5 /* Controllers */, ); path = UI; sourceTree = ""; }; C8412E4D2412A29A0066A8A5 /* Controllers */ = { isa = PBXGroup; children = ( C8D9F49625FCCE80004C952C /* ProjectPreferencesViewController.swift */, C8412E4E2412A2A00066A8A5 /* StatusViewController.swift */, ); path = Controllers; sourceTree = ""; }; C8412E522412A2F20066A8A5 /* Views */ = { isa = PBXGroup; children = ( C8D9F49325FCCE61004C952C /* ProjectPreferencesView.swift */, C8BE992224CA1635000D4E5E /* ProjectIcon.swift */, C8412E502412A2F00066A8A5 /* MainView.swift */, C8F451002416950D002B9D69 /* ProjectMenuView.swift */, C855572E24A14B30006AF499 /* ProjectNameView.swift */, C855572C24A14A00006AF499 /* ActionsMenuText.swift */, C80E916724A39B570073CF3F /* HintView.swift */, C874747126069D600091E791 /* DividerSection.swift */, 6EE2479B2A61669700DC2B4A /* AliasNameView.swift */, ); path = Views; sourceTree = ""; }; C8412E802412A7010066A8A5 /* Data */ = { isa = PBXGroup; children = ( C8F450F5241638C2002B9D69 /* TerminalCommand.swift */, C8412E812412A70F0066A8A5 /* Project.swift */, C8DB027127C6DD860048157A /* CustomCommand.swift */, C8573C48249E77E400E1F969 /* TerminalScript.swift */, 6EC78A68286C7167007F453C /* Alias.swift */, ); path = Data; sourceTree = ""; }; C8456CE8249A1C0400BAEF17 /* Resourses */ = { isa = PBXGroup; children = ( C8412E2D24129B530066A8A5 /* Assets.xcassets */, C8412E3224129B530066A8A5 /* Main.storyboard */, C8412E3524129B530066A8A5 /* Info.plist */, C8412E3624129B530066A8A5 /* XcodeProjects.entitlements */, ); path = Resourses; sourceTree = ""; }; C8456CE9249A1D6700BAEF17 /* PropertyWrappers */ = { isa = PBXGroup; children = ( C8F450F924165C21002B9D69 /* UserDefaultsWrapper.swift */, ); path = PropertyWrappers; sourceTree = ""; }; C8456CEF249A53F000BAEF17 /* Buttons */ = { isa = PBXGroup; children = ( C80D1227241583E800CBCB0F /* PreferencesView.swift */, C8456CF0249A540F00BAEF17 /* AddProjectButton.swift */, C8088D552418E5F5001239CB /* TerminalCommandButton.swift */, C8DB027327C6E4410048157A /* AddCustomCommandButton.swift */, 6EE247992A61488E00DC2B4A /* AddAliasButton.swift */, ); path = Buttons; sourceTree = ""; }; C855572524A142F0006AF499 /* Modifiers */ = { isa = PBXGroup; children = ( C855572624A14300006AF499 /* OnHover.swift */, C855573024A15104006AF499 /* OnHoverText.swift */, C855572824A144F8006AF499 /* RoundedCorners.swift */, ); path = Modifiers; sourceTree = ""; }; C8BE992124CA161A000D4E5E /* Recovered References */ = { isa = PBXGroup; children = ( ); name = "Recovered References"; sourceTree = ""; }; C8F450EF241632C3002B9D69 /* Cells */ = { isa = PBXGroup; children = ( C8F450F0241632D1002B9D69 /* ProjectCell.swift */, 6EC78A6A286C7808007F453C /* AliasCell.swift */, ); path = Cells; sourceTree = ""; }; C8F450F4241638B9002B9D69 /* Logic */ = { isa = PBXGroup; children = ( C8456CE9249A1D6700BAEF17 /* PropertyWrappers */, C8F450FD24167576002B9D69 /* Extensions */, C8F450FB24165C29002B9D69 /* UserDefaultsConfig.swift */, C8D35B3524CC2CB00075308D /* CodableColorPicker.swift */, 6EC78A6C286C9E72007F453C /* ProfileFile.swift */, ); path = Logic; sourceTree = ""; }; C8F450FD24167576002B9D69 /* Extensions */ = { isa = PBXGroup; children = ( C8F450FE24167592002B9D69 /* NSWorkspace+Execute.swift */, C8D9623924AD348600B70B23 /* NSWorkspace+App.swift */, C8AE963424997AC000E6B080 /* String+Components.swift */, C8AE96362499818200E6B080 /* CharacterSet+All.swift */, C8573C43249D6EA600E1F969 /* FileManger+Content.swift */, C8573C4E24A0045200E1F969 /* NSColor+Codable.swift */, C8A4C09A25FACA280008054A /* View+If.swift */, C8D9F49925FCD5F6004C952C /* NotificationNames.swift */, ); path = Extensions; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ C8412E2524129B4F0066A8A5 /* XcodeProjects */ = { isa = PBXNativeTarget; buildConfigurationList = C8412E4424129B530066A8A5 /* Build configuration list for PBXNativeTarget "XcodeProjects" */; buildPhases = ( C8412E2224129B4F0066A8A5 /* Sources */, C8412E2324129B4F0066A8A5 /* Frameworks */, C8412E2424129B4F0066A8A5 /* Resources */, C80D12382415868600CBCB0F /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( ); name = XcodeProjects; productName = MyProjects; productReference = C8412E2624129B4F0066A8A5 /* XcodeProjects.app */; productType = "com.apple.product-type.application"; }; C8412E3A24129B530066A8A5 /* XcodeProjectsTests */ = { isa = PBXNativeTarget; buildConfigurationList = C8412E4724129B530066A8A5 /* Build configuration list for PBXNativeTarget "XcodeProjectsTests" */; buildPhases = ( C8412E3724129B530066A8A5 /* Sources */, C8412E3824129B530066A8A5 /* Frameworks */, C8412E3924129B530066A8A5 /* Resources */, ); buildRules = ( ); dependencies = ( C8412E3D24129B530066A8A5 /* PBXTargetDependency */, ); name = XcodeProjectsTests; productName = MyProjectsTests; productReference = C8412E3B24129B530066A8A5 /* XcodeProjectsTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ C8412E1E24129B4F0066A8A5 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 1420; ORGANIZATIONNAME = com.klm.mac.myProjects; TargetAttributes = { C8412E2524129B4F0066A8A5 = { CreatedOnToolsVersion = 11.3.1; LastSwiftMigration = 1130; }; C8412E3A24129B530066A8A5 = { CreatedOnToolsVersion = 11.3.1; TestTargetID = C8412E2524129B4F0066A8A5; }; }; }; buildConfigurationList = C8412E2124129B4F0066A8A5 /* Build configuration list for PBXProject "XcodeProjects" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = C8412E1D24129B4F0066A8A5; productRefGroup = C8412E2724129B4F0066A8A5 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( C8412E2524129B4F0066A8A5 /* XcodeProjects */, C8412E3A24129B530066A8A5 /* XcodeProjectsTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ C8412E2424129B4F0066A8A5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( C8412E3424129B530066A8A5 /* Main.storyboard in Resources */, C8412E3124129B530066A8A5 /* Preview Assets.xcassets in Resources */, C8A4C09F25FADFBA0008054A /* README.md in Resources */, C8412E2E24129B530066A8A5 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; C8412E3924129B530066A8A5 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ C8412E2224129B4F0066A8A5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C8D9F49425FCCE61004C952C /* ProjectPreferencesView.swift in Sources */, 6EE2479C2A61669700DC2B4A /* AliasNameView.swift in Sources */, C8D9F49725FCCE80004C952C /* ProjectPreferencesViewController.swift in Sources */, C8BE992424CA1732000D4E5E /* NSColor+Codable.swift in Sources */, C8412E2A24129B4F0066A8A5 /* AppDelegate.swift in Sources */, C8D9623824AD31F100B70B23 /* ArrowImageWithHoverPopover.swift in Sources */, C80E916824A39B570073CF3F /* HintView.swift in Sources */, C8AE96372499818200E6B080 /* CharacterSet+All.swift in Sources */, C8D35B3624CC2CB00075308D /* CodableColorPicker.swift in Sources */, C80D122A2415840300CBCB0F /* Preferences.swift in Sources */, C80E916624A39ADF0073CF3F /* ArrowImage.swift in Sources */, C80D1228241583E800CBCB0F /* PreferencesView.swift in Sources */, C8412E512412A2F00066A8A5 /* MainView.swift in Sources */, C8DB027427C6E4410048157A /* AddCustomCommandButton.swift in Sources */, C855572724A14300006AF499 /* OnHover.swift in Sources */, C8F450FC24165C29002B9D69 /* UserDefaultsConfig.swift in Sources */, 6EC78A69286C7167007F453C /* Alias.swift in Sources */, 6EC78A6B286C7808007F453C /* AliasCell.swift in Sources */, C8F450FA24165C21002B9D69 /* UserDefaultsWrapper.swift in Sources */, C8BE992324CA1635000D4E5E /* ProjectIcon.swift in Sources */, C8F450F1241632D1002B9D69 /* ProjectCell.swift in Sources */, C855572D24A14A01006AF499 /* ActionsMenuText.swift in Sources */, C855573124A15104006AF499 /* OnHoverText.swift in Sources */, 6EC78A6D286C9E72007F453C /* ProfileFile.swift in Sources */, C8412E4F2412A2A00066A8A5 /* StatusViewController.swift in Sources */, C8412E822412A70F0066A8A5 /* Project.swift in Sources */, C8F450F6241638C2002B9D69 /* TerminalCommand.swift in Sources */, C855572924A144F8006AF499 /* RoundedCorners.swift in Sources */, C8AE963524997AC000E6B080 /* String+Components.swift in Sources */, C80D122F2415842B00CBCB0F /* SharedFileList.m in Sources */, C80E916C24A39E2C0073CF3F /* CloseHintImage.swift in Sources */, C8088D562418E5F5001239CB /* TerminalCommandButton.swift in Sources */, C8DB027227C6DD860048157A /* CustomCommand.swift in Sources */, C8D9623A24AD348600B70B23 /* NSWorkspace+App.swift in Sources */, C8F451012416950D002B9D69 /* ProjectMenuView.swift in Sources */, C874747226069D600091E791 /* DividerSection.swift in Sources */, C8573C44249D6EA600E1F969 /* FileManger+Content.swift in Sources */, C855572F24A14B30006AF499 /* ProjectNameView.swift in Sources */, 6EE247962A5F453A00DC2B4A /* PackageSwiftImage.swift in Sources */, C8F450FF24167592002B9D69 /* NSWorkspace+Execute.swift in Sources */, C8412E4B2412A23B0066A8A5 /* EventMonitor.swift in Sources */, 6EE2479A2A61488E00DC2B4A /* AddAliasButton.swift in Sources */, C8D9F49A25FCD5F6004C952C /* NotificationNames.swift in Sources */, C8A4C09B25FACA280008054A /* View+If.swift in Sources */, C8456CF1249A540F00BAEF17 /* AddProjectButton.swift in Sources */, C8573C49249E77E400E1F969 /* TerminalScript.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; C8412E3724129B530066A8A5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( C8412E4024129B530066A8A5 /* XcodeProjectsTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ C8412E3D24129B530066A8A5 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C8412E2524129B4F0066A8A5 /* XcodeProjects */; targetProxy = C8412E3C24129B530066A8A5 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ C8412E3224129B530066A8A5 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( C8412E3324129B530066A8A5 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C8412E4224129B530066A8A5 /* 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; 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 = 11.0; 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; }; C8412E4324129B530066A8A5 /* 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; 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 = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; C8412E4524129B530066A8A5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = XcodeProjects/Resourses/XcodeProjects.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"XcodeProjects/Preview Content\""; DEVELOPMENT_TEAM = N5V3ES565J; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = XcodeProjects/Resourses/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.9; PRODUCT_BUNDLE_IDENTIFIER = com.dkcompany.xcodeprojects; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "XcodeProjects/Support/XcodeProjects-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; name = Debug; }; C8412E4624129B530066A8A5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = XcodeProjects/Resourses/XcodeProjects.entitlements; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"XcodeProjects/Preview Content\""; DEVELOPMENT_TEAM = N5V3ES565J; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = XcodeProjects/Resourses/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.9; PRODUCT_BUNDLE_IDENTIFIER = com.dkcompany.xcodeprojects; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "XcodeProjects/Support/XcodeProjects-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Release; }; C8412E4824129B530066A8A5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = N5V3ES565J; INFOPLIST_FILE = XcodeProjectsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.dkcompany.xcodeprojects; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XcodeProjects.app/Contents/MacOS/XcodeProjects"; }; name = Debug; }; C8412E4924129B530066A8A5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = N5V3ES565J; INFOPLIST_FILE = XcodeProjectsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", "@loader_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; PRODUCT_BUNDLE_IDENTIFIER = com.dkcompany.xcodeprojects; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XcodeProjects.app/Contents/MacOS/XcodeProjects"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C8412E2124129B4F0066A8A5 /* Build configuration list for PBXProject "XcodeProjects" */ = { isa = XCConfigurationList; buildConfigurations = ( C8412E4224129B530066A8A5 /* Debug */, C8412E4324129B530066A8A5 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C8412E4424129B530066A8A5 /* Build configuration list for PBXNativeTarget "XcodeProjects" */ = { isa = XCConfigurationList; buildConfigurations = ( C8412E4524129B530066A8A5 /* Debug */, C8412E4624129B530066A8A5 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C8412E4724129B530066A8A5 /* Build configuration list for PBXNativeTarget "XcodeProjectsTests" */ = { isa = XCConfigurationList; buildConfigurations = ( C8412E4824129B530066A8A5 /* Debug */, C8412E4924129B530066A8A5 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = C8412E1E24129B4F0066A8A5 /* Project object */; } ================================================ FILE: XcodeProjects.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: XcodeProjects.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: XcodeProjects.xcodeproj/xcshareddata/xcschemes/XcodeProjects.xcscheme ================================================ ================================================ FILE: XcodeProjects.xcodeproj/xcshareddata/xcschemes/XcodeProjectsTests.xcscheme ================================================ ================================================ FILE: XcodeProjects.xcodeproj/xcuserdata/klm88400.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: XcodeProjectsTests/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: XcodeProjectsTests/XcodeProjectsTests.swift ================================================ // // XcodeProjectsTests.swift // XcodeProjectsTests // // Created by Dima Kalachniuk on 06/03/2020. // Copyright © 2020 com.dkcompany.xcodeprojects. All rights reserved. // import XCTest @testable import XcodeProjects class XcodeProjectsTests: XCTestCase { override func setUp() { // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } }