├── 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 |
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 |
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 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
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 |
--------------------------------------------------------------------------------