├── StatusbarCountdown ├── Images.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── ShakeWindow.swift ├── PreferencesWindow.swift ├── Base.lproj │ └── MainMenu.xib ├── AppDelegate.swift └── PreferencesWindow.xib ├── StatusbarCountdown.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── README.md ├── .gitignore ├── StatusbarCountdownTests ├── Info.plist └── StatusbarCountdownTests.swift └── LICENSE /StatusbarCountdown/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /StatusbarCountdown.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StatusbarCountdown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # macOS/OS X Statusbar Countdown 2 | 3 | A utility to count down to a date from your macOS menubar, written in Swift. 4 | 5 | macOS/OS X Statusbar Countdown 6 | 7 | ## Installation 8 | Download the binary package from [Releases](https://github.com/bbrks/osx-statusbar-countdown/releases) and run. 9 | 10 | If you encounter issues regarding unknown developer, Ctrl+Click on the .app and then click Open. 11 | 12 | Alternatively, clone the repo and compile in Xcode :) 13 | 14 | ## License 15 | This project is licensed under the [MIT License](LICENSE). 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | 28 | # Carthage 29 | # 30 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 31 | # Carthage/Checkouts 32 | 33 | Carthage/Build 34 | -------------------------------------------------------------------------------- /StatusbarCountdownTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /StatusbarCountdownTests/StatusbarCountdownTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusbarCountdownTests.swift 3 | // StatusbarCountdownTests 4 | // 5 | // Created by Ben Brooks on 31/03/2015. 6 | // Copyright (c) 2015 Ben Brooks. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | 12 | class StatusbarCountdownTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ben Brooks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /StatusbarCountdown/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /StatusbarCountdown/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2015 Ben Brooks. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /StatusbarCountdown/ShakeWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShakeWindow.swift 3 | // StatusbarCountdown 4 | // 5 | // Created by Jacqueline Alves on 02/10/19. 6 | // Copyright © 2019 Ben Brooks. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSWindow { 12 | func shakeWindow(){ 13 | let numberOfShakes = 3 14 | let durationOfShake = 0.3 15 | let vigourOfShake : CGFloat = 0.04 16 | let frame : CGRect = self.frame 17 | let shakeAnimation :CAKeyframeAnimation = CAKeyframeAnimation() 18 | 19 | let shakePath = CGMutablePath() 20 | shakePath.move( to: CGPoint(x:NSMinX(frame), y:NSMinY(frame))) 21 | 22 | for _ in 0...numberOfShakes-1 { 23 | shakePath.addLine(to: CGPoint(x:NSMinX(frame) - frame.size.width * vigourOfShake, y:NSMinY(frame))) 24 | shakePath.addLine(to: CGPoint(x:NSMinX(frame) + frame.size.width * vigourOfShake, y:NSMinY(frame))) 25 | } 26 | 27 | shakePath.closeSubpath() 28 | shakeAnimation.path = shakePath 29 | shakeAnimation.duration = durationOfShake 30 | 31 | self.animations = ["frameOrigin":shakeAnimation] 32 | self.animator().setFrameOrigin(self.frame.origin) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /StatusbarCountdown/PreferencesWindow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesWindow.swift 3 | // StatusbarCountdown 4 | // 5 | // Created by Jacqueline Alves on 02/10/19. 6 | // Copyright © 2019 Ben Brooks. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PreferencesWindow: NSWindowController, NSWindowDelegate { 12 | 13 | @IBOutlet weak var nameTextField: NSTextField! 14 | @IBOutlet weak var datePicker: NSDatePicker! 15 | @IBOutlet weak var nameBadInputFeedback: NSTextField! 16 | @IBOutlet weak var dateBadInputFeedback: NSTextField! 17 | 18 | var delegate: PreferencesWindowDelegate? 19 | 20 | override func windowDidLoad() { 21 | super.windowDidLoad() 22 | 23 | self.window?.center() // Center the popover 24 | self.window?.makeKeyAndOrderFront(nil) // Make popover appear on top of anything else 25 | 26 | datePicker.dateValue = Date() 27 | 28 | NSApp.activate(ignoringOtherApps: true) // Activate popover 29 | 30 | nameTextField.delegate = self 31 | } 32 | 33 | override var windowNibName : String! { 34 | return "PreferencesWindow" 35 | } 36 | 37 | func save() { 38 | let defaults = UserDefaults.standard 39 | 40 | defaults.set(nameTextField.stringValue, forKey: "name") 41 | defaults.set(datePicker.dateValue, forKey: "date") 42 | 43 | delegate?.preferencesDidUpdate() 44 | } 45 | 46 | @IBAction func validate(_ sender: Any) { 47 | var validation = true 48 | 49 | if nameTextField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) == "" { // Check if name is empty 50 | nameBadInputFeedback.isHidden = false 51 | window?.shakeWindow() 52 | 53 | validation = false 54 | 55 | } 56 | 57 | if datePicker.dateValue < Date() { // Check if date is after today 58 | dateBadInputFeedback.isHidden = false 59 | window?.shakeWindow() 60 | 61 | validation = false 62 | } 63 | 64 | if validation { // Everything is ok, save values to user defaults and close popover 65 | save() 66 | close() 67 | } 68 | } 69 | 70 | @IBAction func changeDatePicker(_ sender: Any) { 71 | dateBadInputFeedback.isHidden = true // Hide bad input feedback when change the date 72 | } 73 | 74 | @IBAction func closePopover(_ sender: Any) { 75 | close() 76 | } 77 | } 78 | 79 | extension PreferencesWindow: NSTextFieldDelegate { 80 | func controlTextDidChange(_ obj: Notification) { 81 | nameBadInputFeedback.isHidden = true // Hide bad input feedback when something is entered on textfield 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /StatusbarCountdown/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /StatusbarCountdown/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // StatusbarCountdown 4 | // 5 | // Created by Ben Brooks on 31/03/2015. 6 | // Copyright (c) 2015 Ben Brooks. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Foundation 11 | 12 | protocol PreferencesWindowDelegate { 13 | func preferencesDidUpdate() 14 | } 15 | 16 | @NSApplicationMain 17 | class AppDelegate: NSObject, NSApplicationDelegate, PreferencesWindowDelegate { 18 | 19 | let DEFAULT_NAME = "Countdown Name" 20 | let DEFAULT_DATE = NSDate(timeIntervalSince1970: 1597249800) 21 | 22 | var countToDate = NSDate(timeIntervalSince1970: 1597249800) 23 | var countdownName = "Countdown Name" 24 | 25 | var showName = true 26 | var showSeconds = true 27 | var zeroPad = false 28 | 29 | var formatter = NumberFormatter() 30 | 31 | let statusItem = NSStatusBar.system.statusItem(withLength: -1) 32 | @IBOutlet weak var statusMenu: NSMenu! 33 | var preferencesWindow: PreferencesWindow! 34 | 35 | func applicationDidFinishLaunching(_ notification: Notification) { 36 | preferencesWindow = PreferencesWindow() 37 | preferencesWindow.delegate = self 38 | 39 | statusItem.title = "" 40 | statusItem.menu = statusMenu 41 | formatter.minimumIntegerDigits = zeroPad ? 2 : 1 42 | 43 | Timer.scheduledTimer(timeInterval: 0.33, // 33ms ~ 30fps 44 | target: self, 45 | selector: #selector(tick), 46 | userInfo: nil, 47 | repeats: true) 48 | 49 | updatePreferences() 50 | } 51 | 52 | func preferencesDidUpdate() { // Delegate when setting values are updated 53 | updatePreferences() 54 | } 55 | 56 | func updatePreferences() { 57 | let defaults = UserDefaults.standard 58 | 59 | // Gets the saved values in user defaults 60 | countdownName = defaults.string(forKey: "name") ?? DEFAULT_NAME 61 | countToDate = defaults.value(forKey: "date") as? NSDate ?? DEFAULT_DATE 62 | } 63 | 64 | // Calculates the difference in time from now to the specified date and sets the statusItem title 65 | @objc func tick() { 66 | let diffSeconds = Int(countToDate.timeIntervalSinceNow) 67 | 68 | statusItem.title = (showName) ? countdownName + ": " : "" 69 | statusItem.title! += formatTime(diffSeconds) 70 | 71 | } 72 | 73 | // Convert seconds to 4 Time integers (days, hours minutes and seconds) 74 | func secondsToTime (_ seconds : Int) -> (Int, Int, Int, Int) { 75 | let days = seconds / (3600 * 24) 76 | var remainder = seconds % (3600 * 24) 77 | 78 | let hours = remainder / 3600 79 | remainder = remainder % 3600 80 | 81 | let minutes = remainder / 60 82 | 83 | let seconds = remainder % 60 84 | 85 | return (days, hours, minutes, seconds) 86 | } 87 | 88 | // Display seconds as Days, Hours, Minutes, Seconds. 89 | func formatTime(_ seconds: Int) -> (String) { 90 | let time = secondsToTime(abs(seconds)) 91 | let daysStr = (time.0 != 0) ? String(time.0) + "d " : "" 92 | let hoursStr = (time.1 != 0 || time.0 != 0) ? formatter.string(from: NSNumber(value: time.1))! + "h " : "" 93 | let minutesStr = (time.2 != 0 || time.1 != 0 || time.0 != 0) ? formatter.string(from: NSNumber(value: time.2))! + "m" : "" 94 | let secondsStr = (showSeconds) ? " " + formatter.string(from: NSNumber(value: time.3))! + "s" : "" 95 | let suffixStr = (seconds < 0) ? " ago" : "" // TODO: i18n? 96 | return daysStr + hoursStr + minutesStr + secondsStr + suffixStr 97 | } 98 | 99 | // MenuItem click event to toggle showSeconds 100 | @IBAction func toggleShowSeconds(sender: NSMenuItem) { 101 | if (showSeconds) { 102 | showSeconds = false 103 | sender.state = .off 104 | } else { 105 | showSeconds = true 106 | sender.state = .on 107 | } 108 | } 109 | 110 | // MenuItem click event to toggle showName 111 | @IBAction func toggleShowName(sender: NSMenuItem) { 112 | if (showName) { 113 | showName = false 114 | sender.state = .off 115 | } else { 116 | showName = true 117 | sender.state = .on 118 | } 119 | } 120 | 121 | // MenuItem click event to toggle zeroPad 122 | @IBAction func toggleZeroPad(sender: NSMenuItem) { 123 | if (zeroPad) { 124 | zeroPad = false 125 | sender.state = .off 126 | } else { 127 | zeroPad = true 128 | sender.state = .on 129 | } 130 | formatter.minimumIntegerDigits = zeroPad ? 2 : 1 131 | } 132 | 133 | // MenuItem click event to open preferences popover 134 | @IBAction func configurePreferences(_ sender: Any) { 135 | preferencesWindow.showWindow(nil) 136 | } 137 | 138 | // MenuItem click event to quit application 139 | @IBAction func quitApplication(sender: NSMenuItem) { 140 | NSApplication.shared.terminate(self); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /StatusbarCountdown/PreferencesWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 72 | 85 | 93 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /StatusbarCountdown.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 17E0B24A2344BC4400D83311 /* PreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E0B2482344BC4400D83311 /* PreferencesWindow.swift */; }; 11 | 17E0B24B2344BC4400D83311 /* PreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17E0B2492344BC4400D83311 /* PreferencesWindow.xib */; }; 12 | 17E0B24D23457B6500D83311 /* ShakeWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E0B24C23457B6500D83311 /* ShakeWindow.swift */; }; 13 | B57B8F4E1ACB590D00BD794F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57B8F4D1ACB590D00BD794F /* AppDelegate.swift */; }; 14 | B57B8F501ACB590D00BD794F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B57B8F4F1ACB590D00BD794F /* Images.xcassets */; }; 15 | B57B8F531ACB590D00BD794F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = B57B8F511ACB590D00BD794F /* MainMenu.xib */; }; 16 | B57B8F5F1ACB590D00BD794F /* StatusbarCountdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57B8F5E1ACB590D00BD794F /* StatusbarCountdownTests.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | B57B8F591ACB590D00BD794F /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = B57B8F401ACB590D00BD794F /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = B57B8F471ACB590D00BD794F; 25 | remoteInfo = StatusbarCountdown; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 17E0B2482344BC4400D83311 /* PreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindow.swift; sourceTree = ""; }; 31 | 17E0B2492344BC4400D83311 /* PreferencesWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferencesWindow.xib; sourceTree = ""; }; 32 | 17E0B24C23457B6500D83311 /* ShakeWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShakeWindow.swift; sourceTree = ""; }; 33 | B57B8F481ACB590D00BD794F /* StatusbarCountdown.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StatusbarCountdown.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | B57B8F4C1ACB590D00BD794F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | B57B8F4D1ACB590D00BD794F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36 | B57B8F4F1ACB590D00BD794F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 37 | B57B8F521ACB590D00BD794F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 38 | B57B8F581ACB590D00BD794F /* StatusbarCountdownTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StatusbarCountdownTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | B57B8F5D1ACB590D00BD794F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | B57B8F5E1ACB590D00BD794F /* StatusbarCountdownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusbarCountdownTests.swift; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | B57B8F451ACB590D00BD794F /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | B57B8F551ACB590D00BD794F /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | B57B8F3F1ACB590D00BD794F = { 62 | isa = PBXGroup; 63 | children = ( 64 | B57B8F4A1ACB590D00BD794F /* StatusbarCountdown */, 65 | B57B8F5B1ACB590D00BD794F /* StatusbarCountdownTests */, 66 | B57B8F491ACB590D00BD794F /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | B57B8F491ACB590D00BD794F /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | B57B8F481ACB590D00BD794F /* StatusbarCountdown.app */, 74 | B57B8F581ACB590D00BD794F /* StatusbarCountdownTests.xctest */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | B57B8F4A1ACB590D00BD794F /* StatusbarCountdown */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | B57B8F4D1ACB590D00BD794F /* AppDelegate.swift */, 83 | B57B8F4F1ACB590D00BD794F /* Images.xcassets */, 84 | B57B8F511ACB590D00BD794F /* MainMenu.xib */, 85 | 17E0B2482344BC4400D83311 /* PreferencesWindow.swift */, 86 | 17E0B2492344BC4400D83311 /* PreferencesWindow.xib */, 87 | 17E0B24C23457B6500D83311 /* ShakeWindow.swift */, 88 | B57B8F4B1ACB590D00BD794F /* Supporting Files */, 89 | ); 90 | path = StatusbarCountdown; 91 | sourceTree = ""; 92 | }; 93 | B57B8F4B1ACB590D00BD794F /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | B57B8F4C1ACB590D00BD794F /* Info.plist */, 97 | ); 98 | name = "Supporting Files"; 99 | sourceTree = ""; 100 | }; 101 | B57B8F5B1ACB590D00BD794F /* StatusbarCountdownTests */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | B57B8F5E1ACB590D00BD794F /* StatusbarCountdownTests.swift */, 105 | B57B8F5C1ACB590D00BD794F /* Supporting Files */, 106 | ); 107 | path = StatusbarCountdownTests; 108 | sourceTree = ""; 109 | }; 110 | B57B8F5C1ACB590D00BD794F /* Supporting Files */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | B57B8F5D1ACB590D00BD794F /* Info.plist */, 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | /* End PBXGroup section */ 119 | 120 | /* Begin PBXNativeTarget section */ 121 | B57B8F471ACB590D00BD794F /* StatusbarCountdown */ = { 122 | isa = PBXNativeTarget; 123 | buildConfigurationList = B57B8F621ACB590D00BD794F /* Build configuration list for PBXNativeTarget "StatusbarCountdown" */; 124 | buildPhases = ( 125 | B57B8F441ACB590D00BD794F /* Sources */, 126 | B57B8F451ACB590D00BD794F /* Frameworks */, 127 | B57B8F461ACB590D00BD794F /* Resources */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = StatusbarCountdown; 134 | productName = StatusbarCountdown; 135 | productReference = B57B8F481ACB590D00BD794F /* StatusbarCountdown.app */; 136 | productType = "com.apple.product-type.application"; 137 | }; 138 | B57B8F571ACB590D00BD794F /* StatusbarCountdownTests */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = B57B8F651ACB590D00BD794F /* Build configuration list for PBXNativeTarget "StatusbarCountdownTests" */; 141 | buildPhases = ( 142 | B57B8F541ACB590D00BD794F /* Sources */, 143 | B57B8F551ACB590D00BD794F /* Frameworks */, 144 | B57B8F561ACB590D00BD794F /* Resources */, 145 | ); 146 | buildRules = ( 147 | ); 148 | dependencies = ( 149 | B57B8F5A1ACB590D00BD794F /* PBXTargetDependency */, 150 | ); 151 | name = StatusbarCountdownTests; 152 | productName = StatusbarCountdownTests; 153 | productReference = B57B8F581ACB590D00BD794F /* StatusbarCountdownTests.xctest */; 154 | productType = "com.apple.product-type.bundle.unit-test"; 155 | }; 156 | /* End PBXNativeTarget section */ 157 | 158 | /* Begin PBXProject section */ 159 | B57B8F401ACB590D00BD794F /* Project object */ = { 160 | isa = PBXProject; 161 | attributes = { 162 | LastUpgradeCheck = 1000; 163 | ORGANIZATIONNAME = "Ben Brooks"; 164 | TargetAttributes = { 165 | B57B8F471ACB590D00BD794F = { 166 | CreatedOnToolsVersion = 6.2; 167 | }; 168 | B57B8F571ACB590D00BD794F = { 169 | CreatedOnToolsVersion = 6.2; 170 | TestTargetID = B57B8F471ACB590D00BD794F; 171 | }; 172 | }; 173 | }; 174 | buildConfigurationList = B57B8F431ACB590D00BD794F /* Build configuration list for PBXProject "StatusbarCountdown" */; 175 | compatibilityVersion = "Xcode 3.2"; 176 | developmentRegion = English; 177 | hasScannedForEncodings = 0; 178 | knownRegions = ( 179 | English, 180 | en, 181 | Base, 182 | ); 183 | mainGroup = B57B8F3F1ACB590D00BD794F; 184 | productRefGroup = B57B8F491ACB590D00BD794F /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | B57B8F471ACB590D00BD794F /* StatusbarCountdown */, 189 | B57B8F571ACB590D00BD794F /* StatusbarCountdownTests */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | B57B8F461ACB590D00BD794F /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | B57B8F501ACB590D00BD794F /* Images.xcassets in Resources */, 200 | B57B8F531ACB590D00BD794F /* MainMenu.xib in Resources */, 201 | 17E0B24B2344BC4400D83311 /* PreferencesWindow.xib in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | B57B8F561ACB590D00BD794F /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXResourcesBuildPhase section */ 213 | 214 | /* Begin PBXSourcesBuildPhase section */ 215 | B57B8F441ACB590D00BD794F /* Sources */ = { 216 | isa = PBXSourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | 17E0B24D23457B6500D83311 /* ShakeWindow.swift in Sources */, 220 | 17E0B24A2344BC4400D83311 /* PreferencesWindow.swift in Sources */, 221 | B57B8F4E1ACB590D00BD794F /* AppDelegate.swift in Sources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | B57B8F541ACB590D00BD794F /* Sources */ = { 226 | isa = PBXSourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | B57B8F5F1ACB590D00BD794F /* StatusbarCountdownTests.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXTargetDependency section */ 236 | B57B8F5A1ACB590D00BD794F /* PBXTargetDependency */ = { 237 | isa = PBXTargetDependency; 238 | target = B57B8F471ACB590D00BD794F /* StatusbarCountdown */; 239 | targetProxy = B57B8F591ACB590D00BD794F /* PBXContainerItemProxy */; 240 | }; 241 | /* End PBXTargetDependency section */ 242 | 243 | /* Begin PBXVariantGroup section */ 244 | B57B8F511ACB590D00BD794F /* MainMenu.xib */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | B57B8F521ACB590D00BD794F /* Base */, 248 | ); 249 | name = MainMenu.xib; 250 | sourceTree = ""; 251 | }; 252 | /* End PBXVariantGroup section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | B57B8F601ACB590D00BD794F /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 264 | CLANG_WARN_BOOL_CONVERSION = YES; 265 | CLANG_WARN_COMMA = YES; 266 | CLANG_WARN_CONSTANT_CONVERSION = YES; 267 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INFINITE_RECURSION = YES; 272 | CLANG_WARN_INT_CONVERSION = YES; 273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 278 | CLANG_WARN_STRICT_PROTOTYPES = YES; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | CODE_SIGN_IDENTITY = "-"; 283 | COPY_PHASE_STRIP = NO; 284 | ENABLE_STRICT_OBJC_MSGSEND = YES; 285 | ENABLE_TESTABILITY = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_OPTIMIZATION_LEVEL = 0; 290 | GCC_PREPROCESSOR_DEFINITIONS = ( 291 | "DEBUG=1", 292 | "$(inherited)", 293 | ); 294 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 295 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 296 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 297 | GCC_WARN_UNDECLARED_SELECTOR = YES; 298 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 299 | GCC_WARN_UNUSED_FUNCTION = YES; 300 | GCC_WARN_UNUSED_VARIABLE = YES; 301 | MACOSX_DEPLOYMENT_TARGET = 10.11; 302 | MTL_ENABLE_DEBUG_INFO = YES; 303 | ONLY_ACTIVE_ARCH = YES; 304 | SDKROOT = macosx; 305 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 306 | }; 307 | name = Debug; 308 | }; 309 | B57B8F611ACB590D00BD794F /* Release */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_EMPTY_BODY = YES; 324 | CLANG_WARN_ENUM_CONVERSION = YES; 325 | CLANG_WARN_INFINITE_RECURSION = YES; 326 | CLANG_WARN_INT_CONVERSION = YES; 327 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 329 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 332 | CLANG_WARN_STRICT_PROTOTYPES = YES; 333 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 334 | CLANG_WARN_UNREACHABLE_CODE = YES; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | CODE_SIGN_IDENTITY = "-"; 337 | COPY_PHASE_STRIP = NO; 338 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 339 | ENABLE_NS_ASSERTIONS = NO; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | GCC_C_LANGUAGE_STANDARD = gnu99; 342 | GCC_NO_COMMON_BLOCKS = YES; 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | MACOSX_DEPLOYMENT_TARGET = 10.11; 350 | MTL_ENABLE_DEBUG_INFO = NO; 351 | SDKROOT = macosx; 352 | SWIFT_COMPILATION_MODE = wholemodule; 353 | }; 354 | name = Release; 355 | }; 356 | B57B8F631ACB590D00BD794F /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | COMBINE_HIDPI_IMAGES = YES; 361 | INFOPLIST_FILE = StatusbarCountdown/Info.plist; 362 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 363 | PRODUCT_BUNDLE_IDENTIFIER = "me.bbrks.dev.$(PRODUCT_NAME:rfc1034identifier)"; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | SWIFT_VERSION = 4.2; 366 | }; 367 | name = Debug; 368 | }; 369 | B57B8F641ACB590D00BD794F /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 373 | COMBINE_HIDPI_IMAGES = YES; 374 | INFOPLIST_FILE = StatusbarCountdown/Info.plist; 375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 376 | PRODUCT_BUNDLE_IDENTIFIER = "me.bbrks.dev.$(PRODUCT_NAME:rfc1034identifier)"; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | SWIFT_VERSION = 4.2; 379 | }; 380 | name = Release; 381 | }; 382 | B57B8F661ACB590D00BD794F /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | BUNDLE_LOADER = "$(TEST_HOST)"; 386 | COMBINE_HIDPI_IMAGES = YES; 387 | FRAMEWORK_SEARCH_PATHS = ( 388 | "$(DEVELOPER_FRAMEWORKS_DIR)", 389 | "$(inherited)", 390 | ); 391 | GCC_PREPROCESSOR_DEFINITIONS = ( 392 | "DEBUG=1", 393 | "$(inherited)", 394 | ); 395 | INFOPLIST_FILE = StatusbarCountdownTests/Info.plist; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 397 | PRODUCT_BUNDLE_IDENTIFIER = "me.bbrks.dev.$(PRODUCT_NAME:rfc1034identifier)"; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusbarCountdown.app/Contents/MacOS/StatusbarCountdown"; 400 | }; 401 | name = Debug; 402 | }; 403 | B57B8F671ACB590D00BD794F /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | BUNDLE_LOADER = "$(TEST_HOST)"; 407 | COMBINE_HIDPI_IMAGES = YES; 408 | FRAMEWORK_SEARCH_PATHS = ( 409 | "$(DEVELOPER_FRAMEWORKS_DIR)", 410 | "$(inherited)", 411 | ); 412 | INFOPLIST_FILE = StatusbarCountdownTests/Info.plist; 413 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 414 | PRODUCT_BUNDLE_IDENTIFIER = "me.bbrks.dev.$(PRODUCT_NAME:rfc1034identifier)"; 415 | PRODUCT_NAME = "$(TARGET_NAME)"; 416 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StatusbarCountdown.app/Contents/MacOS/StatusbarCountdown"; 417 | }; 418 | name = Release; 419 | }; 420 | /* End XCBuildConfiguration section */ 421 | 422 | /* Begin XCConfigurationList section */ 423 | B57B8F431ACB590D00BD794F /* Build configuration list for PBXProject "StatusbarCountdown" */ = { 424 | isa = XCConfigurationList; 425 | buildConfigurations = ( 426 | B57B8F601ACB590D00BD794F /* Debug */, 427 | B57B8F611ACB590D00BD794F /* Release */, 428 | ); 429 | defaultConfigurationIsVisible = 0; 430 | defaultConfigurationName = Release; 431 | }; 432 | B57B8F621ACB590D00BD794F /* Build configuration list for PBXNativeTarget "StatusbarCountdown" */ = { 433 | isa = XCConfigurationList; 434 | buildConfigurations = ( 435 | B57B8F631ACB590D00BD794F /* Debug */, 436 | B57B8F641ACB590D00BD794F /* Release */, 437 | ); 438 | defaultConfigurationIsVisible = 0; 439 | defaultConfigurationName = Release; 440 | }; 441 | B57B8F651ACB590D00BD794F /* Build configuration list for PBXNativeTarget "StatusbarCountdownTests" */ = { 442 | isa = XCConfigurationList; 443 | buildConfigurations = ( 444 | B57B8F661ACB590D00BD794F /* Debug */, 445 | B57B8F671ACB590D00BD794F /* Release */, 446 | ); 447 | defaultConfigurationIsVisible = 0; 448 | defaultConfigurationName = Release; 449 | }; 450 | /* End XCConfigurationList section */ 451 | }; 452 | rootObject = B57B8F401ACB590D00BD794F /* Project object */; 453 | } 454 | --------------------------------------------------------------------------------