diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..6b85076 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,51 @@ +disabled_rules: # rule identifiers to exclude from running + - colon + - comma + - control_statement +opt_in_rules: # some rules are only opt-in + - empty_count + # Find all the available rules by running: + # swiftlint rules +# included: # paths to include during linting. `--path` is ignored if present. +# - Clocker +# excluded: # paths to ignore during linting. Takes precedence over `included`. + # - Carthage + # - Pods + # - Source/ExcludedFolder + # - Source/ExcludedFile.swift + # - Source/*/ExcludedFile.swift # Exclude files with a wildcard +analyzer_rules: # Rules run by `swiftlint analyze` (experimental) + - explicit_self + +# configurable rules can be customized from this configuration file +# binary rules can set their severity level +force_cast: warning # implicitly +force_try: + severity: warning # explicitly +# rules that have both warning and error levels, can set just the warning level +# implicitly +line_length: 110 +# they can set both implicitly with an array +type_body_length: + - 300 # warning + - 400 # error +# or they can set both explicitly +file_length: + warning: 500 + error: 1200 +# naming rules can set warnings/errors for min_length and max_length +# additionally they can set excluded names +type_name: + min_length: 3 # only warning + max_length: # warning and error + warning: 40 + error: 50 + excluded: iPhone # excluded via string +identifier_name: + min_length: # only min_length + error: 3 # only error + # excluded: # excluded via string array + # - id + # - URL + # - GlobalAPIKey +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown) \ No newline at end of file diff --git a/Clocker/AppDelegate.swift b/Clocker/AppDelegate.swift index a2873cd..2203052 100644 --- a/Clocker/AppDelegate.swift +++ b/Clocker/AppDelegate.swift @@ -3,60 +3,60 @@ import Cocoa open class AppDelegate : NSObject, NSApplicationDelegate { - + lazy private var floatingWindow: FloatingWindowController = FloatingWindowController.shared() lazy private var panelController: PanelController = PanelController.shared() private var statusBarHandler: StatusItemHandler! - + deinit { panelController.removeObserver(self, forKeyPath: "hasActivePanel") } - + private var kContextActivePanel = 0 - + open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - + if context == &kContextActivePanel { statusBarHandler.setHasActiveIcon(panelController.hasActivePanelGetter()) } else if let path = keyPath, path == "values.globalPing" { - + let hotKeyCenter = PTHotKeyCenter.shared() - + // Unregister old hot key let oldHotKey = hotKeyCenter?.hotKey(withIdentifier: path) hotKeyCenter?.unregisterHotKey(oldHotKey) - + // We don't register unless there's a valid key combination guard let newObject = object as? NSObject, let newShortcut = newObject.value(forKeyPath: path) as? [AnyHashable: Any] else { return } - + // Register new key let newHotKey: PTHotKey = PTHotKey(identifier: keyPath, keyCombo: newShortcut, target: self, action: #selector(ping(_:))) - + hotKeyCenter?.register(newHotKey) } - + } - + public func applicationWillFinishLaunching(_ notification: Notification) { iVersion.sharedInstance().useAllAvailableLanguages = true iVersion.sharedInstance().verboseLogging = false } - + public func applicationDidFinishLaunching(_ notification: Notification) { - + // Initializing the event store takes really long EventCenter.sharedCenter() - + AppDefaults.initialize() - + // Check if we can show the onboarding flow! showOnboardingFlow() - + // Ratings Controller initialization RateController.applicationDidLaunch(UserDefaults.standard) @@ -66,26 +66,26 @@ open class AppDelegate : NSObject, NSApplicationDelegate { checkIfRunFromApplicationsFolder() #endif } - + public func applicationDockMenu(_ sender: NSApplication) -> NSMenu? { let menu = NSMenu(title: "Quick Access") - + Logger.log(object: ["Dock Menu Triggered": "YES"], for: "Dock Menu Triggered") - + let toggleMenuItem = NSMenuItem(title: "Toggle Panel", action: #selector(AppDelegate.togglePanel(_:)), keyEquivalent: "") let openPreferences = NSMenuItem(title: "Preferences", action: #selector(AppDelegate.openPreferencesWindow), keyEquivalent: ",") - + [toggleMenuItem, openPreferences].forEach { $0.isEnabled = true menu.addItem($0) } - + return menu } - + @objc private func openPreferencesWindow() { let displayMode = UserDefaults.standard.integer(forKey: CLShowAppInForeground) - + if displayMode == 1 { let floatingWindow = FloatingWindowController.shared() floatingWindow.openPreferences(NSButton()) @@ -94,7 +94,7 @@ open class AppDelegate : NSObject, NSApplicationDelegate { panelController.openPreferences(NSButton()) } } - + private lazy var controller: OnboardingController? = { let s = NSStoryboard(name: NSStoryboard.Name("Onboarding"), bundle: nil) return s.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("onboardingFlow")) as? OnboardingController @@ -102,42 +102,42 @@ open class AppDelegate : NSObject, NSApplicationDelegate { private func showOnboardingFlow() { let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty) || (ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument)) - + shouldLaunchOnboarding ? controller?.launch() : continueUsually() } - + func continueUsually() { // Check if another instance of the app is already running. If so, then stop this one. checkIfAppIsAlreadyOpen() - + // Make sure the old models are not used anymore TimezoneData.convert() - + // Install the menubar item! statusBarHandler = StatusItemHandler() - + if UserDefaults.standard.object(forKey: CLInstallHomeIndicatorObject) == nil { fetchLocalTimezone() UserDefaults.standard.set(1, forKey: CLInstallHomeIndicatorObject) } - + if ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) { RateController.setPreviewMode(true) } - + UserDefaults.standard.register(defaults: ["NSApplicationCrashOnExceptions": true]) - + assignShortcut() - + panelController.addObserver(self, forKeyPath: "hasActivePanel", options: [.new], context: &kContextActivePanel) - + let defaults = UserDefaults.standard - + setActivationPolicy() - + // Set the display mode default as panel! if let displayMode = defaults.object(forKey: CLShowAppInForeground) as? NSNumber, displayMode.intValue == 1 { showFloatingWindow() @@ -145,22 +145,22 @@ open class AppDelegate : NSObject, NSApplicationDelegate { showFloatingWindow() } } - + // Should we have a dock icon or just stay in the menubar? private func setActivationPolicy() { let defaults = UserDefaults.standard - + let activationPolicy: NSApplication.ActivationPolicy = defaults.integer(forKey: CLAppDislayOptions) == 0 ? .accessory : .regular NSApp.setActivationPolicy(activationPolicy) } - + private func checkIfAppIsAlreadyOpen() { guard let bundleID = Bundle.main.bundleIdentifier else { return } - + let apps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID) - + if apps.count > 1 { let currentApplication = NSRunningApplication.current for app in apps where app != currentApplication { @@ -168,13 +168,13 @@ open class AppDelegate : NSObject, NSApplicationDelegate { } } } - + private func showAppAlreadyOpenMessage() { showAlert(message: "An instance of Clocker is already open 😅", informativeText: "This instance of Clocker will terminate now.", buttonTitle: "Close") } - + private func showAlert(message: String, informativeText: String, buttonTitle: String) { NSApplication.shared.activate(ignoringOtherApps: true) let alert = NSAlert() @@ -183,24 +183,24 @@ open class AppDelegate : NSObject, NSApplicationDelegate { alert.addButton(withTitle: buttonTitle) alert.runModal() } - + private func fetchLocalTimezone() { let identifier = TimeZone.autoupdatingCurrent.identifier - + let currentTimezone = TimezoneData() currentTimezone.timezoneID = identifier currentTimezone.setLabel(identifier) currentTimezone.formattedAddress = identifier currentTimezone.isSystemTimezone = true currentTimezone.placeID = "Home" - + let operations = TimezoneDataOperations(with: currentTimezone) operations.saveObject(at: 0) - + // Retrieve Location // retrieveLatestLocation() } - + @IBAction func ping(_ sender: Any) { togglePanel(sender) } @@ -209,29 +209,29 @@ open class AppDelegate : NSObject, NSApplicationDelegate { let locationController = LocationController.sharedController() locationController.determineAndRequestLocationAuthorization() } - + private func showFloatingWindow() { // Display the Floating Window! floatingWindow.showWindow(nil) floatingWindow.updateTableContent() floatingWindow.startWindowTimer() - + NSApp.activate(ignoringOtherApps: true) } - + private func assignShortcut() { NSUserDefaultsController.shared.addObserver(self, forKeyPath: "values.globalPing", options: [.initial, .new], context: nil) } - + private func checkIfRunFromApplicationsFolder() { - + if let shortCircuit = UserDefaults.standard.object(forKey: "AllowOutsideApplicationsFolder") as? Bool, shortCircuit == true { return } - + let bundlePath = Bundle.main.bundlePath let applicationDirectory = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationDirectory, FileManager.SearchPathDomainMask.localDomainMask, @@ -241,21 +241,21 @@ open class AppDelegate : NSObject, NSApplicationDelegate { return } } - + // Clocker is installed out of Applications directory // This breaks start at login! Time to show an alert and terminate showAlert(message: "Move Clocker to the Applications folder", informativeText: "Clocker must be run from the Applications folder in order to work properly.\n\nPlease quit Clocker, move it to the Applications folder, and relaunch. Current folder: \(applicationDirectory)", buttonTitle: "Quit") - + // Terminate NSApp.terminate(nil) } - + @IBAction open func togglePanel(_ sender: Any) { - + let displayMode = UserDefaults.standard.integer(forKey: CLShowAppInForeground) - + if displayMode == 1 { floatingWindow.showWindow(nil) floatingWindow.updateTableContent() @@ -271,23 +271,23 @@ open class AppDelegate : NSObject, NSApplicationDelegate { open func setupFloatingWindow() { showFloatingWindow() } - + open func closeFloatingWindow() { floatingWindow.window?.close() } - + func statusItemForPanel() -> StatusItemHandler { return statusBarHandler } - + open func setPanelDefaults() { panelController.updateDefaultPreferences() } - + open func setupMenubarTimer() { statusBarHandler.setupStatusItem() } - + open func invalidateMenubarTimer(_ showIcon: Bool) { statusBarHandler.invalidateTimer(showIcon: showIcon, isSyncing: true) } diff --git a/Clocker/Clocker/LocationController.swift b/Clocker/Clocker/LocationController.swift index 1078813..abd8c04 100644 --- a/Clocker/Clocker/LocationController.swift +++ b/Clocker/Clocker/LocationController.swift @@ -89,7 +89,7 @@ class LocationController: NSObject { extension LocationController: CLLocationManagerDelegate { func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - + guard locations.count > 0, let coordinates = locations.first?.coordinate else { return } let reverseGeoCoder = CLGeocoder() @@ -99,7 +99,7 @@ extension LocationController: CLLocationManagerDelegate { guard let customLabel = placemarks?.first?.locality else { return } self.updateHomeObject(with: customLabel, coordinates: coordinates) - + self.locationManager.stopUpdatingLocation() } } diff --git a/Clocker/ClockerUITests/AboutUsTests.swift b/Clocker/ClockerUITests/AboutUsTests.swift index 2921a98..86494de 100644 --- a/Clocker/ClockerUITests/AboutUsTests.swift +++ b/Clocker/ClockerUITests/AboutUsTests.swift @@ -29,17 +29,17 @@ class AboutUsTests: XCTestCase { func testMockingFeedback() { app.tapMenubarIcon() app.buttons["Preferences"].click() - + tapAboutTab() - + let expectedVersion = "Clocker 1.6.09 (64)" guard let presentVersion = app.windows["Clocker"].staticTexts["ClockerVersion"].value as? String else { XCTFail("Present version not present") return } - + XCTAssertEqual(expectedVersion, presentVersion) - + app.checkBoxes["ClockerPrivateFeedback"].click() app.buttons["Send Feedback"].click() diff --git a/Clocker/ClockerUITests/FloatingWindowTests.swift b/Clocker/ClockerUITests/FloatingWindowTests.swift index fae111e..a84e54a 100644 --- a/Clocker/ClockerUITests/FloatingWindowTests.swift +++ b/Clocker/ClockerUITests/FloatingWindowTests.swift @@ -88,7 +88,7 @@ class FloatingWindowTests: XCTestCase { let floatingSlider = app.sliders["FloatingSlider"].exists app.buttons["FloatingPreferences"].click() - + let appearanceTab = app.toolbars.buttons.element(boundBy: 1) appearanceTab.click() diff --git a/Clocker/ClockerUITests/NetworkDisconnectionTests.swift b/Clocker/ClockerUITests/NetworkDisconnectionTests.swift index 07a272e..49cb5d2 100644 --- a/Clocker/ClockerUITests/NetworkDisconnectionTests.swift +++ b/Clocker/ClockerUITests/NetworkDisconnectionTests.swift @@ -3,18 +3,18 @@ import XCTest class NetworkDisconnectionTests: XCTestCase { - + var app: XCUIApplication! - + override func setUp() { super.setUp() continueAfterFailure = false app = XCUIApplication() } - + func precondition() { app.launch() - + if !app.tables["FloatingTableView"].exists { app.tapMenubarIcon() app.buttons["Pin"].click() @@ -25,38 +25,38 @@ class NetworkDisconnectionTests: XCTestCase { app.launchArguments.append("mockNetworkDown") precondition() app.buttons["FloatingPreferences"].click() - + if app.sheets.count == 0 { app.windows["Clocker"].checkBoxes["AddTimezone"].click() } - + XCTAssertFalse(app.sheets.staticTexts["ErrorPlaceholder"].exists) - + let searchField = app.searchFields["AvailableSearchField"] searchField.reset(text: "Uganda") sleep(1) XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists) - + app.sheets.buttons["Close"].click() } - + func testFetchingATimezone() { - + app.launchArguments.append("mockTimezoneDown") precondition() app.buttons["FloatingPreferences"].click() - + if app.sheets.count == 0 { app.windows["Clocker"].checkBoxes["AddTimezone"].click() } - + XCTAssertFalse(app.sheets.staticTexts["ErrorPlaceholder"].exists) - + let searchField = app.searchFields["AvailableSearchField"] searchField.reset(text: "Uganda") let firstResult = app.tables["AvailableTimezoneTableView"].tableRows.firstMatch - + let waiter = XCTWaiter() let isHittable = NSPredicate(format: "exists == true", "") let addExpectation = expectation(for: isHittable, @@ -65,11 +65,10 @@ class NetworkDisconnectionTests: XCTestCase { waiter.wait(for: [addExpectation], timeout: 5) app.tables["AvailableTimezoneTableView"].click() app.buttons["AddAvailableTimezone"].click() - - + sleep(1) XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists) app.sheets.buttons["Close"].click() } - + } diff --git a/Clocker/ClockerUITests/OnboardingTests.swift b/Clocker/ClockerUITests/OnboardingTests.swift index 392058e..c0c05d5 100644 --- a/Clocker/ClockerUITests/OnboardingTests.swift +++ b/Clocker/ClockerUITests/OnboardingTests.swift @@ -5,7 +5,7 @@ import XCTest let CLOnboaringTestsLaunchArgument = "isTestingTheOnboardingFlow" class OnboardingTests: XCTestCase { - + var app: XCUIApplication! override func setUp() { @@ -19,142 +19,142 @@ class OnboardingTests: XCTestCase { // 1. The flow (forward button and back button take the user to the correct screen) // 2. Static texts and button title's are appropriate func testForwardButton() { - + welcomeControllerTests() - + // Let's go to the Permissions View moveForward() permissionsControllerTests() - + // Time to test the launchAtLoginView moveForward() startupControllerTests() - + // Let's go to OnboardingSearchController moveForward() searchControllerTests() - + // Let's go to the FinalOnboardingController moveForward() finalOnboardingControllerTests() - + backButtonTests() } - + func backButtonTests() { - + moveBackward() searchControllerTests() - + moveBackward() startupControllerTests() - + moveBackward() permissionsControllerTests() - + moveBackward() welcomeControllerTests() - + alternateStartupFlowTests() } - + func alternateStartupFlowTests() { - + // Let's go to the Permissions View moveForward() permissionsControllerTests() - + // Time to test the launchAtLoginView moveForward() startupControllerTests() - + // Let's go to OnboardingSearchController alternateMoveForward() searchControllerTests() - + // Let's go to the FinalOnboardingController moveForward() finalOnboardingControllerTests() - + moveForward() XCTAssertTrue(app.statusItems.count > 0, "Status item was not installed in the menubar") } - + private func moveForward() { let onboardingWindow = app.windows["OnboardingWindow"] onboardingWindow.buttons["Forward"].click() sleep(1) } - + private func alternateMoveForward() { let onboardingWindow = app.windows["OnboardingWindow"] onboardingWindow.buttons["Alternate"].click() sleep(1) } - + private func moveBackward() { let onboardingWindow = app.windows["OnboardingWindow"] onboardingWindow.buttons["Backward"].click() sleep(1) } - + private func welcomeControllerTests() { let onboardingWindow = app.windows["OnboardingWindow"] - + // Tests static texts XCTAssertTrue(onboardingWindow.staticTexts["Clocker"].exists, "Static text Clocker was unexpectedly missing") XCTAssertTrue(onboardingWindow.staticTexts["It only takes 3 steps to set up Clocker."].exists, "Accessory label's static text was unexpectedly wrong.") - + let button = onboardingWindow.buttons["Forward"] - + // Test the button title XCTAssertTrue(button.exists, "Button title was unexpectedly wrong. Expected \"Get Started\", Actual: \"\(onboardingWindow.buttons.firstMatch.title)\" ") - + XCTAssertTrue(onboardingWindow.buttons.count == 1, "More than 1 button on Welcome screen!") } - + private func permissionsControllerTests() { - + let onboardingWindow = app.windows["OnboardingWindow"] - + XCTAssertTrue(onboardingWindow.staticTexts["Permissions"].exists, "Header label's static text was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.staticTexts["These can be configured later in System Preferences."].exists, "Onboarding Info label's static text was unexpectedly wrong.") - + XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Continue", "Forward button title's was unexpectedly wrong") XCTAssertTrue(onboardingWindow.buttons["Backward"].exists, "Back button was unexpectedly missing") XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.") } - + private func startupControllerTests() { - + let onboardingWindow = app.windows["OnboardingWindow"] - + XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Open Clocker At Login", "Forward button title's was unexpectedly wrong") XCTAssertTrue(onboardingWindow.buttons["Alternate"].title == "Don't Open", "Alternate button title's was unexpectedly wrong") - + XCTAssertTrue(onboardingWindow.staticTexts["Launch at Login"].exists, "Header label's static text was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.staticTexts["Should Clocker open automatically on startup?"].exists, "Accessory label's static text was unexpectedly wrong.") } - + private func searchControllerTests() { - + let onboardingWindow = app.windows["OnboardingWindow"] - + XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.") XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Continue", "Forward button title's was unexpectedly wrong") - + XCTAssertTrue(onboardingWindow.staticTexts["Quick Add Locations"].exists, "Header label's static text was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.staticTexts["More search options in Clocker Preferences."].exists, "Accessory label's static text was unexpectedly wrong.") } - + private func finalOnboardingControllerTests() { - + let onboardingWindow = app.windows["OnboardingWindow"] - + // Let's test the buttons XCTAssertTrue(onboardingWindow.staticTexts["You're all set!"].exists, "Header label's static text was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.staticTexts["Thank you for the details."].exists, "Accessory label's static text was unexpectedly wrong.") - + XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.") XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Launch Clocker", "Forward button's title was unexpectedly wrong.") } diff --git a/Clocker/ClockerUITests/PanelTests.swift b/Clocker/ClockerUITests/PanelTests.swift index 22f0a65..4d98ab4 100644 --- a/Clocker/ClockerUITests/PanelTests.swift +++ b/Clocker/ClockerUITests/PanelTests.swift @@ -107,15 +107,15 @@ class PanelTests: XCTestCase { XCTAssertNotEqual(afterUpcomingEventViewExists, beforeUpcomingEventViewExist) } - + func testRightMouseDownToShowPopover() { - + app.tapMenubarIcon() - + let cell = app.tables["mainTableView"].cells.firstMatch cell.rightClick() - + XCTAssert(app.popovers.count > 0) } - + } diff --git a/Clocker/ClockerUITests/PreferencesTest.swift b/Clocker/ClockerUITests/PreferencesTest.swift index 8354326..41da0e3 100644 --- a/Clocker/ClockerUITests/PreferencesTest.swift +++ b/Clocker/ClockerUITests/PreferencesTest.swift @@ -54,36 +54,36 @@ class PreferencesTest: XCTestCase { deleteAPlace(place: "UTC", for: app) } - + func testEditingLabel() { - + let placeToAdd = "Auckland" - + app.tapMenubarIcon() app.tables["mainTableView"].typeKey(",", modifierFlags: .command) - + if app.sheets.count == 0 { app.windows["Clocker"].checkBoxes["AddTimezone"].click() } - + addAPlace(place: placeToAdd, to: app) - + let matchPredicate = NSPredicate(format: "value == %@", placeToAdd) let matchingFields = app.windows["Clocker"].textFields.matching(matchPredicate) XCTAssertTrue(matchingFields.count > 1, "Matching Fields count was zero") - + matchingFields.element(boundBy: 1).doubleClick() matchingFields.element(boundBy: 1).typeText("NZ") app.typeKey(XCUIKeyboardKey.return, modifierFlags: []) app.tapMenubarIcon() - + let labelPredicate = NSPredicate(format: "label == %@", "NZ") let cells = app.tables["mainTableView"].cells.matching(labelPredicate) XCTAssert(cells.count > 0) - + app.tables["mainTableView"].typeKey(",", modifierFlags: .command) deleteAPlace(place: placeToAdd, for: app) - + } func testSortingByTimezoneDifference() { @@ -129,7 +129,7 @@ class PreferencesTest: XCTestCase { addAPlace(place: "Omaha", to: app) addAPlace(place: "Mumbai", to: app) } - + func testSortingByTimezoneName() { app.tapMenubarIcon() app.tables["mainTableView"].typeKey(",", modifierFlags: .command) @@ -220,22 +220,22 @@ class PreferencesTest: XCTestCase { func testSearchingWithMisspelledName() { app.tapMenubarIcon() app.tables["mainTableView"].typeKey(",", modifierFlags: .command) - + if app.sheets.count == 0 { app.windows["Clocker"].checkBoxes["AddTimezone"].click() } - + let searchField = app.searchFields["AvailableSearchField"] searchField.reset(text: "StuJjlqh7AcJFnBuOdgNa2dQ4WrIajP9Mo8R83FV7fIZ3B8zE2n") - + sleep(1) - + let maxCharacterCountPredicate = NSPredicate(format: "value like %@", "Only 50 characters allowed!") let currentSheets = app.sheets.firstMatch.staticTexts let maxCharacterQuery = currentSheets.matching(maxCharacterCountPredicate) - + XCTAssertTrue(maxCharacterQuery.count > 0) - + addAPlace(place: "asdakjhdasdahsdasd", to: app, shouldSleep: false) XCTAssertTrue(app.sheets.staticTexts["Please select a timezone!"].exists) @@ -276,86 +276,86 @@ class PreferencesTest: XCTestCase { XCTAssertTrue(newPlaceholder.exists, "Search Field doesn't exist") XCTAssertEqual(newPlaceholder.placeholderValue!, newPlaceholderValue) } - + func testNoTimezone() { app.tapMenubarIcon() app.buttons["Preferences"].click() - + deleteAllTimezones() - + XCTAssertTrue(app.staticTexts["NoTimezoneEmoji"].exists) XCTAssertTrue(app.staticTexts["NoTimezoneMessage"].exists) - + app.tapMenubarIcon() XCTAssertTrue(app.buttons["EmptyAddTimezone"].exists) - + addAPlace(place: "Omaha", to: app) addAPlace(place: "Mumbai", to: app) - + deleteAllTimezones() - + XCTAssertTrue(app.staticTexts["NoTimezoneEmoji"].exists) XCTAssertTrue(app.staticTexts["NoTimezoneMessage"].exists) - + addAPlace(place: "Omaha", to: app) addAPlace(place: "Mumbai", to: app) } - + func testWarningIfMoreThanOneMenubarIsSelected() { app.tapMenubarIcon() app.buttons["Preferences"].click() - + let preferencesTable = app.tables["TimezoneTableView"] XCTAssertTrue(preferencesTable.exists) - + // Let's reset all checkboxes let favouritedMenubarsQuery = preferencesTable.checkBoxes.matching(NSPredicate(format: "value == 1", "")) - + if favouritedMenubarsQuery.count > 1 { for _ in 0.. 1 timezones first let favourites = preferencesTable.tableRows XCTAssertTrue(favourites.count > 1) - + // Select two timezones let unfavouritedMenubarsQuery = preferencesTable.checkBoxes.matching(NSPredicate(format: "value == 0", "")) - + if unfavouritedMenubarsQuery.count > 1 { - for _ in 0..<2{ + for _ in 0..<2 { let checkbox = unfavouritedMenubarsQuery.element(boundBy: 0) checkbox.click() } } - + XCTAssertTrue(app.dialogs.count > 0) - + let compactModeButton = app.dialogs.buttons["Enable Compact Mode"] - + if compactModeButton.isHittable { compactModeButton.click() XCTAssertTrue(app.dialogs.count == 0) } } - + private func deleteAllTimezones() { let clockerWindow = app.windows["Clocker"] let rowQueryCount = clockerWindow.tables["TimezoneTableView"].tableRows.count - + if rowQueryCount > 0 { - + let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch currentElement.click() - + for _ in 0 ..< rowQueryCount { clockerWindow.typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) } - + } } } diff --git a/Clocker/ClockerUITests/ReviewTests.swift b/Clocker/ClockerUITests/ReviewTests.swift index 304acba..fd516d0 100644 --- a/Clocker/ClockerUITests/ReviewTests.swift +++ b/Clocker/ClockerUITests/ReviewTests.swift @@ -3,23 +3,23 @@ import XCTest class ReviewTests: XCTestCase { - + var app: XCUIApplication! - + override func setUp() { super.setUp() continueAfterFailure = false app = XCUIApplication() app.launchArguments.append(CLUITestingLaunchArgument) app.launch() - + app.tapMenubarIcon() app.tapMenubarIcon() app.tapMenubarIcon() } func testIfReviewIsNegativeAndUserWantsToProvideFeedback() { - + guard app.buttons["Not Really"].exists else { return } XCTAssertTrue(app.staticTexts["ReviewLabel"].exists) app.buttons["Not Really"].click() @@ -28,7 +28,7 @@ class ReviewTests: XCTestCase { XCTAssertFalse(app.staticTexts["ReviewLabel"].exists) XCTAssertTrue(app.windows["Clocker Feedback"].exists) } - + func testIfReviewIsNegativeAndNoFeedback() { guard app.buttons["Not Really"].exists else { return } XCTAssertTrue(app.staticTexts["ReviewLabel"].exists) @@ -37,7 +37,7 @@ class ReviewTests: XCTestCase { app.buttons["No, thanks"].click() XCTAssertFalse(app.staticTexts["ReviewLabel"].exists) } - + func testOnPositiveReviewAndNoAction() { guard app.buttons["Yes!"].exists else { return } XCTAssertTrue(app.staticTexts["ReviewLabel"].exists) @@ -46,7 +46,7 @@ class ReviewTests: XCTestCase { app.buttons["No, thanks"].click() XCTAssertFalse(app.staticTexts["ReviewLabel"].exists) } - + func testOnPositiveReviewAndAction() { guard app.buttons["Yes!"].exists else { return } XCTAssertTrue(app.staticTexts["ReviewLabel"].exists) diff --git a/Clocker/ClockerUITests/ShortcutTests.swift b/Clocker/ClockerUITests/ShortcutTests.swift index 86bc5f0..79f19c6 100644 --- a/Clocker/ClockerUITests/ShortcutTests.swift +++ b/Clocker/ClockerUITests/ShortcutTests.swift @@ -3,47 +3,47 @@ import XCTest class ShortcutTests: XCTestCase { - + var app: XCUIApplication! - + let randomIndex = Int(arc4random_uniform(26)) - + override func setUp() { super.setUp() continueAfterFailure = false app = XCUIApplication() app.launch() app.tapMenubarIcon() - + if !app.tables["mainTableView"].exists { app.buttons["FloatingPin"].click() app.tapMenubarIcon() } } - + func testShortcuts() { app.tables["mainTableView"].typeKey(",", modifierFlags: .command) - + XCTAssertFalse(app.tables["mainTableView"].exists) - + let randomAlphabet = randomLetter() - + app.windows["Clocker"].buttons["ShortcutControl"].click() app.windows["Clocker"].buttons["ShortcutControl"].typeKey(randomAlphabet, modifierFlags: [.shift, .command]) - + // Close the window to really test app.windows["Clocker"].buttons["_XCUI:CloseWindow"].click() - + app.typeKey(randomAlphabet, modifierFlags: [.shift, .command]) XCTAssertTrue(app.tables["mainTableView"].exists) - + app.terminate() app.launch() - + app.typeKey(randomAlphabet, modifierFlags: [.shift, .command]) XCTAssertTrue(app.tables["mainTableView"].exists) - + // Reset the shortcut app.tables["mainTableView"].typeKey(",", modifierFlags: .command) app.windows["Clocker"].buttons["ShortcutControl"].click() @@ -51,7 +51,7 @@ class ShortcutTests: XCTestCase { app.windows["Clocker"].typeKey(randomAlphabet, modifierFlags: [.shift, .command]) XCTAssertFalse(app.tables["mainTableView"].exists) } - + private func randomLetter() -> String { let alphabet: [String] = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] return alphabet[randomIndex] diff --git a/Clocker/ClockerUnitTests/ClockerUnitTests.swift b/Clocker/ClockerUnitTests/ClockerUnitTests.swift index 8990ca3..538069b 100644 --- a/Clocker/ClockerUnitTests/ClockerUnitTests.swift +++ b/Clocker/ClockerUnitTests/ClockerUnitTests.swift @@ -4,7 +4,7 @@ import XCTest @testable import Clocker class ClockerUnitTests: XCTestCase { - + private let california = ["customLabel" : "Test", "formattedAddress" : "San Francisco", "place_id" : "TestIdentifier", @@ -12,7 +12,7 @@ class ClockerUnitTests: XCTestCase { "nextUpdate" : "", "latitude" : "37.7749295", "longitude" : "-122.4194155"] - + private let mumbai = ["customLabel" : "Ghar", "formattedAddress" : "Mumbai", "place_id" : "ChIJwe1EZjDG5zsRaYxkjY_tpF0", @@ -20,7 +20,7 @@ class ClockerUnitTests: XCTestCase { "nextUpdate" : "", "latitude" : "19.0759837", "longitude" : "72.8776559"] - + private let auckland = ["customLabel" : "Auckland", "formattedAddress" : "New Zealand", "place_id" : "ChIJh5Z3Fw4gLG0RM0dqdeIY1rE", @@ -28,7 +28,7 @@ class ClockerUnitTests: XCTestCase { "nextUpdate" : "", "latitude" : "-40.900557", "longitude" : "174.885971"] - + private let florida = ["customLabel" : "Gainesville", "formattedAddress" : "Florida", "place_id" : "ChIJvypWkWV2wYgR0E7HW9MTLvc", @@ -36,37 +36,37 @@ class ClockerUnitTests: XCTestCase { "nextUpdate" : "", "latitude" : "27.664827", "longitude" : "-81.5157535"] - + private let onlyTimezone: [String: Any] = ["timezoneID": "Africa/Algiers", "formattedAddress" : "Africa/Algiers", "place_id": "", "customLabel": "", "latitude": "", "longitude": ""] - + private let omaha: [String: Any] = ["timezoneID": "America/Chicago", "formattedAddress" : "Omaha", "place_id": "ChIJ7fwMtciNk4cRBLY3rk9NQkY", "customLabel": "", "latitude": "41.2565369", "longitude": "-95.9345034"] - + private var operations: TimezoneDataOperations { return TimezoneDataOperations(with: TimezoneData(with: mumbai)) } - + private var californiaOperations: TimezoneDataOperations { return TimezoneDataOperations(with: TimezoneData(with: california)) } - + private var floridaOperations: TimezoneDataOperations { return TimezoneDataOperations(with: TimezoneData(with: florida)) } - + private var aucklandOperations: TimezoneDataOperations { return TimezoneDataOperations(with: TimezoneData(with: auckland)) } - + private var omahaOperations: TimezoneDataOperations { return TimezoneDataOperations(with: TimezoneData(with: omaha)) } @@ -74,32 +74,32 @@ class ClockerUnitTests: XCTestCase { func testAddingATimezoneToDefaults() { let timezoneData = TimezoneData(with: california) - + let defaults = UserDefaults.standard let currentFavourites = (defaults.object(forKey: CLDefaultPreferenceKey) as? [Data]) ?? [] let oldCount = currentFavourites.count - + let operationsObject = TimezoneDataOperations(with: timezoneData) operationsObject.saveObject() - + let newDefaults = UserDefaults.standard.object(forKey: CLDefaultPreferenceKey) as? [Data] - + XCTAssert(newDefaults != nil) XCTAssert(newDefaults?.count == oldCount + 1) } - + func testDeletingATimezone() { let defaults = UserDefaults.standard var currentFavourites = defaults.object(forKey: CLDefaultPreferenceKey) as! [Data] let oldCount = currentFavourites.count - + currentFavourites = currentFavourites.filter { let timezone = TimezoneData.customObject(from: $0) return timezone?.placeID != "TestIdentifier" } - + defaults.set(currentFavourites, forKey: CLDefaultPreferenceKey) - + XCTAssertTrue(currentFavourites.count == oldCount - 1) } @@ -111,108 +111,108 @@ class ClockerUnitTests: XCTestCase { XCTAssertTrue(aucklandOperations.timeDifference() == ", 19 hours ahead", "Difference was unexpectedly: \(aucklandOperations.timeDifference())") XCTAssertTrue(omahaOperations.timeDifference() == ", 2 hours ahead", "Difference was unexpectedly: \(omahaOperations.timeDifference())") } - + func testSunriseSunset() { let dataObject = TimezoneData(with: mumbai) let operations = TimezoneDataOperations(with: dataObject) - + XCTAssertNotNil(operations.formattedSunriseTime(with: 0)) XCTAssertNotNil(dataObject.sunriseTime) XCTAssertNotNil(dataObject.sunriseTime) - + let timezoneObject = TimezoneData(with: onlyTimezone) timezoneObject.selectionType = .timezone let timezoneOperations = TimezoneDataOperations(with: timezoneObject) - + XCTAssertTrue(timezoneOperations.formattedSunriseTime(with: 0) == "") XCTAssertNil(timezoneObject.sunriseTime) XCTAssertNil(timezoneObject.sunsetTime) } - + func testDateWithSliderValue() { - + let dataObject = TimezoneData(with: mumbai) let operations = TimezoneDataOperations(with: dataObject) - + XCTAssertNotNil(operations.date(with: 0, displayType: .menuDisplay)) } - + func testTimezoneFormat() { let dataObject = TimezoneData(with: mumbai) UserDefaults.standard.set(NSNumber(value: 0), forKey: CLShowSecondsInMenubar) // Set to show seconds UserDefaults.standard.set(NSNumber(value: 0), forKey: CL24hourFormatSelectedKey) // Set to 12 hour format - + dataObject.setShouldOverrideGlobalTimeFormat(0) XCTAssertTrue(dataObject.timezoneFormat() == "h:mm:ss a") - + dataObject.setShouldOverrideGlobalTimeFormat(1) XCTAssertTrue(dataObject.timezoneFormat() == "HH:mm:ss") - + dataObject.setShouldOverrideGlobalTimeFormat(2) XCTAssertTrue(dataObject.timezoneFormat() == "h:mm:ss a") - + UserDefaults.standard.set(NSNumber(value: 1), forKey: CL24hourFormatSelectedKey) // Set to 24-Hour Format XCTAssertTrue(dataObject.timezoneFormat() == "HH:mm:ss") - + UserDefaults.standard.set(NSNumber(value: 1), forKey: CLShowSecondsInMenubar) - + dataObject.setShouldOverrideGlobalTimeFormat(0) XCTAssertTrue(dataObject.timezoneFormat() == "h:mm a") - + dataObject.setShouldOverrideGlobalTimeFormat(1) XCTAssertTrue(dataObject.timezoneFormat() == "HH:mm") - + dataObject.setShouldOverrideGlobalTimeFormat(2) XCTAssertTrue(dataObject.timezoneFormat() == "HH:mm") - + UserDefaults.standard.set(NSNumber(value: 0), forKey: CL24hourFormatSelectedKey) XCTAssertTrue(dataObject.timezoneFormat() == "h:mm a") } - + func testFormattedLabel() { - + let dataObject = TimezoneData(with: mumbai) XCTAssertTrue(dataObject.formattedTimezoneLabel() == "Ghar", "Incorrect custom label returned by model.") - + dataObject.customLabel = nil XCTAssertTrue(dataObject.formattedTimezoneLabel() == "Mumbai", "Incorrect custom label returned by model.") - + dataObject.formattedAddress = nil XCTAssertTrue(dataObject.formattedTimezoneLabel() == "Asia", "Incorrect custom label returned by model.") } - + func testEquality() { let dataObject1 = TimezoneData(with: mumbai) let dataObject2 = TimezoneData(with: auckland) - + XCTAssertFalse(dataObject1 == dataObject2) XCTAssertFalse(dataObject1.isEqual(dataObject2)) - + let dataObject3 = TimezoneData(with: mumbai) XCTAssertTrue(dataObject1 == dataObject3) XCTAssertTrue(dataObject1.isEqual(dataObject3)) - + XCTAssertFalse(dataObject1.isEqual(nil)) } - + func testWithAllLocales() { - + let dataObject1 = TimezoneData(with: mumbai) let operations = TimezoneDataOperations(with: dataObject1) - + for locale in Locale.availableIdentifiers { let currentLocale = Locale(identifier: locale) let localizedDate = operations.todaysDate(with: 0, locale: currentLocale) XCTAssertNotNil(localizedDate) } } - + func testTimeWithAllLocales() { - + let dataObject = TimezoneData(with: mumbai) - + let cal = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian) - + guard let newDate = cal?.date(byAdding: .minute, value: 0, to: Date(), @@ -220,7 +220,7 @@ class ClockerUnitTests: XCTestCase { XCTFail("Unable to add dates!") return } - + for locale in Locale.availableIdentifiers { let currentLocale = Locale(identifier: locale) let dateFormatter = DateFormatterManager.dateFormatterWithFormat(with: .none, diff --git a/Clocker/ClockerUnitTests/RateTests.swift b/Clocker/ClockerUnitTests/RateTests.swift index 6155298..27f1572 100644 --- a/Clocker/ClockerUnitTests/RateTests.swift +++ b/Clocker/ClockerUnitTests/RateTests.swift @@ -4,7 +4,7 @@ import XCTest @testable import Clocker class RateTests: XCTestCase { - + let rateController = RateController.applicationDidLaunch(UserDefaults()) override func setUp() { diff --git a/Clocker/Dependencies/Date Additions/Constants.swift b/Clocker/Dependencies/Date Additions/Constants.swift index 7d71317..5df94f0 100755 --- a/Clocker/Dependencies/Date Additions/Constants.swift +++ b/Clocker/Dependencies/Date Additions/Constants.swift @@ -6,7 +6,6 @@ // Copyright © 2016 Grayson Webster. All rights reserved. // - import Foundation /** @@ -24,6 +23,6 @@ public class Constants { public static let SecondsInHour: TimeInterval = 3600 public static let SecondsInMinute: TimeInterval = 60 public static let MillisecondsInDay: TimeInterval = 86400000 - + public static let AllCalendarUnitFlags: Set = [.year, .quarter, .month, .weekOfYear, .weekOfMonth, .day, .hour, .minute, .second, .era, .weekday, .weekdayOrdinal, .weekOfYear] } diff --git a/Clocker/Dependencies/Date Additions/Date+Bundle.swift b/Clocker/Dependencies/Date Additions/Date+Bundle.swift index 9bec556..33cfd42 100755 --- a/Clocker/Dependencies/Date Additions/Date+Bundle.swift +++ b/Clocker/Dependencies/Date Additions/Date+Bundle.swift @@ -9,10 +9,9 @@ import Foundation public extension Bundle { - + class func dateToolsBundle() -> Bundle { let assetPath = Bundle(for: Constants.self).resourcePath! return Bundle(path: NSString(string: assetPath).appendingPathComponent("DateTools.bundle"))! } } - diff --git a/Clocker/Dependencies/Date Additions/Date+Comparators.swift b/Clocker/Dependencies/Date Additions/Date+Comparators.swift index 5793631..aa56ec2 100755 --- a/Clocker/Dependencies/Date Additions/Date+Comparators.swift +++ b/Clocker/Dependencies/Date Additions/Date+Comparators.swift @@ -15,9 +15,9 @@ import Foundation * for a given unit of time. */ public extension Date { - + // MARK: - Comparisons - + /** * Given a date, returns a `TimeChunk` with components in their most natural form. Example: * @@ -48,7 +48,7 @@ public extension Date { return TimeChunk(seconds: compenentsBetween.second!, minutes: compenentsBetween.minute!, hours: compenentsBetween.hour!, days: compenentsBetween.day!, weeks: 0, months: compenentsBetween.month!, years: compenentsBetween.year!) // TimeChunk(seconds: secondDelta, minutes: minuteDelta, hours: hourDelta, days: dayDelta, weeks: 0, months: monthDelta, years: yearDelta) } - + /** * Returns a true if receiver is equal to provided comparison date, otherwise returns false * @@ -59,7 +59,7 @@ public extension Date { func equals(_ date: Date) -> Bool { return self.compare(date) == .orderedSame } - + /** * Returns a true if receiver is later than provided comparison date, otherwise * returns false @@ -71,7 +71,7 @@ public extension Date { func isLater(than date: Date) -> Bool { return self.compare(date) == .orderedDescending } - + /** * Returns a true if receiver is later than or equal to provided comparison date, * otherwise returns false @@ -83,7 +83,7 @@ public extension Date { func isLaterThanOrEqual(to date: Date) -> Bool { return self.compare(date) == .orderedDescending || self.compare(date) == .orderedSame } - + /** * Returns a true if receiver is earlier than provided comparison date, otherwise * returns false @@ -95,7 +95,7 @@ public extension Date { func isEarlier(than date: Date) -> Bool { return self.compare(date) == .orderedAscending } - + /** * Returns a true if receiver is earlier than or equal to the provided comparison date, * otherwise returns false @@ -107,7 +107,7 @@ public extension Date { func isEarlierThanOrEqual(to date: Date) -> Bool { return self.compare(date) == .orderedAscending || self.compare(date) == .orderedSame } - + /** * Returns whether two dates fall on the same day. * @@ -118,7 +118,7 @@ public extension Date { func isSameDay(date : Date ) -> Bool { return Date.isSameDay(date: self, as: date) } - + /** * Returns whether two dates fall on the same day. * @@ -131,18 +131,17 @@ public extension Date { let calendar = Calendar.autoupdatingCurrent var components = calendar.dateComponents([.era, .year, .month, .day], from: date) let dateOne = calendar.date(from: components) - + components = calendar.dateComponents([.era, .year, .month, .day], from: compareDate) let dateTwo = calendar.date(from: components) - + return (dateOne?.equals(dateTwo!))! } - - + // MARK: - Date Comparison - + // MARK: Time From - + /** * Returns an Int representing the amount of time in years between the receiver and * the provided date. @@ -157,7 +156,7 @@ public extension Date { func years(from date: Date) -> Int { return years(from: date, calendar:nil) } - + /** * Returns an Int representing the amount of time in months between the receiver and * the provided date. @@ -172,7 +171,7 @@ public extension Date { func months(from date: Date) -> Int { return months(from: date, calendar:nil) } - + /** * Returns an Int representing the amount of time in weeks between the receiver and * the provided date. @@ -187,7 +186,7 @@ public extension Date { func weeks(from date: Date) -> Int { return weeks(from: date, calendar:nil) } - + /** * Returns an Int representing the amount of time in days between the receiver and * the provided date. @@ -202,7 +201,7 @@ public extension Date { func days(from date: Date) -> Int { return days(from: date, calendar:nil) } - + /** * Returns an Int representing the amount of time in hours between the receiver and * the provided date. @@ -214,9 +213,9 @@ public extension Date { * - returns: The hours between receiver and provided date */ func hours(from date: Date) -> Int { - return Int(self.timeIntervalSince(date)/Constants.SecondsInHour); + return Int(self.timeIntervalSince(date)/Constants.SecondsInHour) } - + /** * Returns an Int representing the amount of time in minutes between the receiver and * the provided date. @@ -230,7 +229,7 @@ public extension Date { func minutes(from date: Date) -> Int { return Int(self.timeIntervalSince(date)/Constants.SecondsInMinute) } - + /** * Returns an Int representing the amount of time in seconds between the receiver and * the provided date. @@ -244,10 +243,9 @@ public extension Date { func seconds(from date: Date) -> Int { return Int(timeIntervalSince(date)) } - - + // MARK: Time From With Calendar - + /** * Returns an Int representing the amount of time in years between the receiver and * the provided date. @@ -264,14 +262,14 @@ public extension Date { if (calendar == nil) { calendarCopy = Calendar.autoupdatingCurrent } - + let earliest = earlierDate(date) - let latest = (earliest == self) ? date : self; - let multiplier = (earliest == self) ? -1 : 1; + let latest = (earliest == self) ? date : self + let multiplier = (earliest == self) ? -1 : 1 let components = calendarCopy!.dateComponents([.year], from: earliest, to: latest) - return multiplier * components.year!; + return multiplier * components.year! } - + /** * Returns an Int representing the amount of time in months between the receiver and * the provided date. @@ -283,19 +281,19 @@ public extension Date { * * - returns: The months between receiver and provided date */ - func months(from date: Date, calendar: Calendar?) -> Int{ + func months(from date: Date, calendar: Calendar?) -> Int { var calendarCopy = calendar if (calendar == nil) { calendarCopy = Calendar.autoupdatingCurrent } - + let earliest = earlierDate(date) - let latest = (earliest == self) ? date : self; - let multiplier = (earliest == self) ? -1 : 1; + let latest = (earliest == self) ? date : self + let multiplier = (earliest == self) ? -1 : 1 let components = calendarCopy!.dateComponents(Constants.AllCalendarUnitFlags, from: earliest, to: latest) - return multiplier*(components.month! + 12*components.year!); + return multiplier*(components.month! + 12*components.year!) } - + /** * Returns an Int representing the amount of time in weeks between the receiver and * the provided date. @@ -307,19 +305,19 @@ public extension Date { * * - returns: The weeks between receiver and provided date */ - func weeks(from date: Date, calendar: Calendar?) -> Int{ + func weeks(from date: Date, calendar: Calendar?) -> Int { var calendarCopy = calendar if (calendar == nil) { calendarCopy = Calendar.autoupdatingCurrent } - + let earliest = earlierDate(date) - let latest = (earliest == self) ? date : self; - let multiplier = (earliest == self) ? -1 : 1; + let latest = (earliest == self) ? date : self + let multiplier = (earliest == self) ? -1 : 1 let components = calendarCopy!.dateComponents([.weekOfYear], from: earliest, to: latest) - return multiplier*components.weekOfYear!; + return multiplier*components.weekOfYear! } - + /** * Returns an Int representing the amount of time in days between the receiver and * the provided date. @@ -336,17 +334,16 @@ public extension Date { if (calendar == nil) { calendarCopy = Calendar.autoupdatingCurrent } - + let earliest = earlierDate(date) let latest = (earliest == self) ? date : self let multiplier = (earliest == self) ? -1 : 1 let components = calendarCopy!.dateComponents([.day], from: earliest, to: latest) return multiplier*components.day! } - - + // MARK: Time Until - + /** * The number of years until the receiver's date (0 if the receiver is the same or * earlier than now). @@ -354,7 +351,7 @@ public extension Date { var yearsUntil: Int { return yearsLater(than: Date()) } - + /** * The number of months until the receiver's date (0 if the receiver is the same or * earlier than now). @@ -362,7 +359,7 @@ public extension Date { var monthsUntil: Int { return monthsLater(than: Date()) } - + /** * The number of weeks until the receiver's date (0 if the receiver is the same or * earlier than now). @@ -370,7 +367,7 @@ public extension Date { var weeksUntil: Int { return weeksLater(than: Date()) } - + /** * The number of days until the receiver's date (0 if the receiver is the same or * earlier than now). @@ -378,34 +375,33 @@ public extension Date { var daysUntil: Int { return daysLater(than: Date()) } - + /** * The number of hours until the receiver's date (0 if the receiver is the same or * earlier than now). */ - var hoursUntil: Int{ + var hoursUntil: Int { return hoursLater(than: Date()) } - + /** * The number of minutes until the receiver's date (0 if the receiver is the same or * earlier than now). */ - var minutesUntil: Int{ + var minutesUntil: Int { return minutesLater(than: Date()) } - + /** * The number of seconds until the receiver's date (0 if the receiver is the same or * earlier than now). */ - var secondsUntil: Int{ + var secondsUntil: Int { return secondsLater(than: Date()) } - - + // MARK: Time Ago - + /** * The number of years the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -413,7 +409,7 @@ public extension Date { var yearsAgo: Int { return yearsEarlier(than: Date()) } - + /** * The number of months the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -421,7 +417,7 @@ public extension Date { var monthsAgo: Int { return monthsEarlier(than: Date()) } - + /** * The number of weeks the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -429,7 +425,7 @@ public extension Date { var weeksAgo: Int { return weeksEarlier(than: Date()) } - + /** * The number of days the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -437,7 +433,7 @@ public extension Date { var daysAgo: Int { return daysEarlier(than: Date()) } - + /** * The number of hours the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -445,7 +441,7 @@ public extension Date { var hoursAgo: Int { return hoursEarlier(than: Date()) } - + /** * The number of minutes the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). @@ -453,18 +449,17 @@ public extension Date { var minutesAgo: Int { return minutesEarlier(than: Date()) } - + /** * The number of seconds the receiver's date is earlier than now (0 if the receiver is * the same or earlier than now). */ - var secondsAgo: Int{ + var secondsAgo: Int { return secondsEarlier(than: Date()) } - - + // MARK: Earlier Than - + /** * Returns the number of years the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -476,7 +471,7 @@ public extension Date { func yearsEarlier(than date: Date) -> Int { return abs(min(years(from: date), 0)) } - + /** * Returns the number of months the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -486,9 +481,9 @@ public extension Date { * - returns: The number of months */ func monthsEarlier(than date: Date) -> Int { - return abs(min(months(from: date), 0)); + return abs(min(months(from: date), 0)) } - + /** * Returns the number of weeks the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -500,7 +495,7 @@ public extension Date { func weeksEarlier(than date: Date) -> Int { return abs(min(weeks(from: date), 0)) } - + /** * Returns the number of days the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -512,7 +507,7 @@ public extension Date { func daysEarlier(than date: Date) -> Int { return abs(min(days(from: date), 0)) } - + /** * Returns the number of hours the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -524,7 +519,7 @@ public extension Date { func hoursEarlier(than date: Date) -> Int { return abs(min(hours(from: date), 0)) } - + /** * Returns the number of minutes the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -536,7 +531,7 @@ public extension Date { func minutesEarlier(than date: Date) -> Int { return abs(min(minutes(from: date), 0)) } - + /** * Returns the number of seconds the receiver's date is earlier than the provided * comparison date, 0 if the receiver's date is later than or equal to the provided comparison date. @@ -548,10 +543,9 @@ public extension Date { func secondsEarlier(than date: Date) -> Int { return abs(min(seconds(from: date), 0)) } - - + // MARK: Later Than - + /** * Returns the number of years the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -564,7 +558,7 @@ public extension Date { func yearsLater(than date: Date) -> Int { return max(years(from: date), 0) } - + /** * Returns the number of months the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -577,7 +571,7 @@ public extension Date { func monthsLater(than date: Date) -> Int { return max(months(from: date), 0) } - + /** * Returns the number of weeks the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -590,7 +584,7 @@ public extension Date { func weeksLater(than date: Date) -> Int { return max(weeks(from: date), 0) } - + /** * Returns the number of days the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -603,7 +597,7 @@ public extension Date { func daysLater(than date: Date) -> Int { return max(days(from: date), 0) } - + /** * Returns the number of hours the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -616,7 +610,7 @@ public extension Date { func hoursLater(than date: Date) -> Int { return max(hours(from: date), 0) } - + /** * Returns the number of minutes the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided @@ -629,7 +623,7 @@ public extension Date { func minutesLater(than date: Date) -> Int { return max(minutes(from: date), 0) } - + /** * Returns the number of seconds the receiver's date is later than the provided * comparison date, 0 if the receiver's date is earlier than or equal to the provided diff --git a/Clocker/Dependencies/Date Additions/Date+Components.swift b/Clocker/Dependencies/Date Additions/Date+Components.swift index a2e22cb..b38eed6 100755 --- a/Clocker/Dependencies/Date Additions/Date+Components.swift +++ b/Clocker/Dependencies/Date Additions/Date+Components.swift @@ -27,7 +27,7 @@ public extension Date { let calendar = Calendar.autoupdatingCurrent return calendar.component(component, from: self) } - + /** * Convenient accessor of the date's `Calendar` components ordinality. * @@ -41,7 +41,7 @@ public extension Date { let calendar = Calendar.autoupdatingCurrent return calendar.ordinality(of: smaller, in: larger, for: self) } - + /** * Use calendar components to determine how many units of a smaller component are inside 1 larger unit. * @@ -60,10 +60,10 @@ public extension Date { var unitRange: Range? if larger.hashValue < smaller.hashValue { for x in larger.hashValue.. String { return format(with: dateStyle, timeZone: TimeZone.autoupdatingCurrent, locale: locale) } - + /** * Get string representation of date. * Locale and time zone are automatically selected as the current locale and time zone of the system. @@ -77,10 +77,9 @@ public extension Date { return format(with: dateStyle, timeZone: TimeZone.autoupdatingCurrent, locale: Locale.autoupdatingCurrent) #endif } - - + // MARK: - Formatted Date - String - + /** * Get string representation of date. * @@ -95,10 +94,10 @@ public extension Date { dateFormatter.dateFormat = dateFormat dateFormatter.timeZone = timeZone dateFormatter.locale = locale - + return dateFormatter.string(from: self) } - + /** * Get string representation of date. * Locale is automatically selected as the current locale of the system. @@ -115,7 +114,7 @@ public extension Date { return format(with: dateFormat, timeZone: timeZone, locale: Locale.autoupdatingCurrent) #endif } - + /** * Get string representation of date. * Time zone is automatically selected as the current time zone of the system. @@ -128,7 +127,7 @@ public extension Date { func format(with dateFormat: String, locale: Locale) -> String { return format(with: dateFormat, timeZone: TimeZone.autoupdatingCurrent, locale: locale) } - + /** * Get string representation of date. * Locale and time zone are automatically selected as the current locale and time zone of the system. diff --git a/Clocker/Dependencies/Date Additions/Date+Inits.swift b/Clocker/Dependencies/Date Additions/Date+Inits.swift index aeef8e5..3ab49b1 100755 --- a/Clocker/Dependencies/Date Additions/Date+Inits.swift +++ b/Clocker/Dependencies/Date Additions/Date+Inits.swift @@ -14,9 +14,9 @@ import Foundation */ public extension Date { - + // MARK: - Initializers - + /** * Init date with components. * @@ -35,14 +35,14 @@ public extension Date { dateComponents.hour = hour dateComponents.minute = minute dateComponents.second = second - + guard let date = Calendar.current.date(from: dateComponents) else { self = Date() return } self = date } - + /** * Init date with components. Hour, minutes, and seconds set to zero. * @@ -53,7 +53,7 @@ public extension Date { init(year: Int, month: Int, day: Int) { self.init(year: year, month: month, day: day, hour: 0, minute: 0, second: 0) } - + /** * Init date from string, given a format string, according to Apple's date formatting guide, and time zone. * @@ -63,18 +63,18 @@ public extension Date { */ init(dateString: String, format: String, timeZone: TimeZone) { let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .none; - dateFormatter.timeStyle = .none; - dateFormatter.timeZone = timeZone; - dateFormatter.dateFormat = format; - + dateFormatter.dateStyle = .none + dateFormatter.timeStyle = .none + dateFormatter.timeZone = timeZone + dateFormatter.dateFormat = format + guard let date = dateFormatter.date(from: dateString) else { self = Date() return } self = date } - + /** * Init date from string, given a format string, according to Apple's date formatting guide. * Time Zone automatically selected as the current time zone. @@ -84,5 +84,5 @@ public extension Date { */ init (dateString: String, format: String) { self.init(dateString: dateString, format: format, timeZone: TimeZone.autoupdatingCurrent) - } + } } diff --git a/Clocker/Dependencies/Date Additions/Date+Manipulations.swift b/Clocker/Dependencies/Date Additions/Date+Manipulations.swift index 1d46307..7a10d64 100755 --- a/Clocker/Dependencies/Date Additions/Date+Manipulations.swift +++ b/Clocker/Dependencies/Date Additions/Date+Manipulations.swift @@ -12,9 +12,9 @@ import Foundation * Extends the Date class by adding manipulation methods for transforming dates */ public extension Date { - + // MARK: - StartOf - + /** * Return a date set to the start of a given component. * @@ -24,11 +24,10 @@ public extension Date { * with all smaller components set to their minimum */ func start(of component: Component) -> Date { - var newDate = self; + var newDate = self if component == .second { newDate.second(self.second) - } - else if component == .minute { + } else if component == .minute { newDate.second(0) } else if component == .hour { newDate.second(0) @@ -51,7 +50,7 @@ public extension Date { } return newDate } - + /** * Return a date set to the end of a given component. * @@ -61,12 +60,11 @@ public extension Date { * with all smaller components set to their maximum */ func end(of component: Component) -> Date { - var newDate = self; + var newDate = self if component == .second { newDate.second(newDate.second + 1) newDate = newDate - 0.001 - } - else if component == .minute { + } else if component == .minute { newDate.second(60) newDate = newDate - 0.001 } else if component == .hour { @@ -92,10 +90,10 @@ public extension Date { newDate.month(12) newDate.day(31) } - + return newDate } - + func daysInMonth(date: Date) -> Int { let month = date.month if month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 { @@ -112,10 +110,9 @@ public extension Date { return 30 } } - - + // MARK: - Addition / Subtractions - + /** * # Add (TimeChunk to Date) * Increase a date by the value of a given `TimeChunk`. @@ -136,7 +133,7 @@ public extension Date { components.second = chunk.seconds return calendar.date(byAdding: components, to: self)! } - + /** * # Subtract (TimeChunk from Date) * Decrease a date by the value of a given `TimeChunk`. @@ -157,36 +154,35 @@ public extension Date { components.second = -chunk.seconds return calendar.date(byAdding: components, to: self)! } - - + // MARK: - Operator Overloads - + /** * Operator overload for adding a `TimeChunk` to a date. */ static func +(leftAddend: Date, rightAddend: TimeChunk) -> Date { return leftAddend.add(rightAddend) } - + /** * Operator overload for subtracting a `TimeChunk` from a date. */ static func -(minuend: Date, subtrahend: TimeChunk) -> Date { return minuend.subtract(subtrahend) } - + /** * Operator overload for adding a `TimeInterval` to a date. */ static func +(leftAddend: Date, rightAddend: Int) -> Date { return leftAddend.addingTimeInterval((TimeInterval(rightAddend))) } - + /** * Operator overload for subtracting a `TimeInterval` from a date. */ static func -(minuend: Date, subtrahend: Int) -> Date { return minuend.addingTimeInterval(-(TimeInterval(subtrahend))) } - + } diff --git a/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift b/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift index cdde937..a2752bf 100755 --- a/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift +++ b/Clocker/Dependencies/Date Additions/Date+TimeAgo.swift @@ -13,9 +13,9 @@ import Foundation * time in String format. */ public extension Date { - - //MARK: - Time Ago - + + // MARK: - Time Ago + /** * Takes in a date and returns a string with the most convenient unit of time representing * how far in the past that date is from now. @@ -24,10 +24,10 @@ public extension Date { * * - returns String - Formatted return string */ - static func timeAgo(since date:Date) -> String{ + static func timeAgo(since date:Date) -> String { return date.timeAgo(since: Date(), numericDates: false, numericTimes: false) } - + /** * Takes in a date and returns a shortened string with the most convenient unit of time representing * how far in the past that date is from now. @@ -39,7 +39,7 @@ public extension Date { static func shortTimeAgo(since date:Date) -> String { return date.shortTimeAgo(since:Date()) } - + /** * Returns a string with the most convenient unit of time representing * how far in the past that date is from now. @@ -49,7 +49,7 @@ public extension Date { var timeAgoSinceNow: String { return self.timeAgo(since:Date()) } - + /** * Returns a shortened string with the most convenient unit of time representing * how far in the past that date is from now. @@ -59,184 +59,156 @@ public extension Date { var shortTimeAgoSinceNow: String { return self.shortTimeAgo(since:Date()) } - + func timeAgo(since date:Date, numericDates: Bool = false, numericTimes: Bool = false) -> String { let calendar = NSCalendar.current let unitFlags = Set([.second,.minute,.hour,.day,.weekOfYear,.month,.year]) let earliest = self.earlierDate(date) let latest = (earliest == self) ? date : self //Should be triple equals, but not extended to Date at this time - - + let components = calendar.dateComponents(unitFlags, from: earliest, to: latest) let yesterday = date.subtract(1.days) let isYesterday = yesterday.day == self.day - + //Not Yet Implemented/Optional //The following strings are present in the translation files but lack logic as of 2014.04.05 //@"Today", @"This week", @"This month", @"This year" //and @"This morning", @"This afternoon" - + if (components.year! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@years ago", value: components.year!) - } - else if (components.year! >= 1) { - + } else if (components.year! >= 1) { + if (numericDates) { - return DateToolsLocalizedStrings("1 year ago"); + return DateToolsLocalizedStrings("1 year ago") } - - return DateToolsLocalizedStrings("Last year"); - } - else if (components.month! >= 2) { + + return DateToolsLocalizedStrings("Last year") + } else if (components.month! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@months ago", value: components.month!) - } - else if (components.month! >= 1) { - + } else if (components.month! >= 1) { + if (numericDates) { - return DateToolsLocalizedStrings("1 month ago"); + return DateToolsLocalizedStrings("1 month ago") } - - return DateToolsLocalizedStrings("Last month"); - } - else if (components.weekOfYear! >= 2) { + + return DateToolsLocalizedStrings("Last month") + } else if (components.weekOfYear! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@weeks ago", value: components.weekOfYear!) - } - else if (components.weekOfYear! >= 1) { - + } else if (components.weekOfYear! >= 1) { + if (numericDates) { - return DateToolsLocalizedStrings("1 week ago"); + return DateToolsLocalizedStrings("1 week ago") } - - return DateToolsLocalizedStrings("Last week"); - } - else if (components.day! >= 2) { + + return DateToolsLocalizedStrings("Last week") + } else if (components.day! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@days ago", value: components.day!) - } - else if (isYesterday) { + } else if (isYesterday) { if (numericDates) { - return DateToolsLocalizedStrings("1 day ago"); + return DateToolsLocalizedStrings("1 day ago") } - - return DateToolsLocalizedStrings("Yesterday"); - } - else if (components.hour! >= 2) { + + return DateToolsLocalizedStrings("Yesterday") + } else if (components.hour! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@hours ago", value: components.hour!) - } - else if (components.hour! >= 1) { - + } else if (components.hour! >= 1) { + if (numericTimes) { - return DateToolsLocalizedStrings("1 hour ago"); + return DateToolsLocalizedStrings("1 hour ago") } - - return DateToolsLocalizedStrings("An hour ago"); - } - else if (components.minute! >= 2) { + + return DateToolsLocalizedStrings("An hour ago") + } else if (components.minute! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d %@minutes ago", value: components.minute!) - } - else if (components.minute! >= 1) { - + } else if (components.minute! >= 1) { + if (numericTimes) { - return DateToolsLocalizedStrings("1 minute ago"); + return DateToolsLocalizedStrings("1 minute ago") } - - return DateToolsLocalizedStrings("A minute ago"); - } - else if (components.second! >= 3) { + + return DateToolsLocalizedStrings("A minute ago") + } else if (components.second! >= 3) { return self.logicalLocalizedStringFromFormat(format: "%%d %@seconds ago", value: components.second!) - } - else { - + } else { + if (numericTimes) { - return DateToolsLocalizedStrings("1 second ago"); + return DateToolsLocalizedStrings("1 second ago") } - - return DateToolsLocalizedStrings("Just now"); + + return DateToolsLocalizedStrings("Just now") } } - - + func shortTimeAgo(since date:Date) -> String { let calendar = NSCalendar.current let unitFlags = Set([.second,.minute,.hour,.day,.weekOfYear,.month,.year]) let earliest = self.earlierDate(date) let latest = (earliest == self) ? date : self //Should pbe triple equals, but not extended to Date at this time - - + let components = calendar.dateComponents(unitFlags, from: earliest, to: latest) let yesterday = date.subtract(1.days) let isYesterday = yesterday.day == self.day - - + if (components.year! >= 1) { return self.logicalLocalizedStringFromFormat(format: "%%d%@y", value: components.year!) - } - else if (components.month! >= 1) { + } else if (components.month! >= 1) { return self.logicalLocalizedStringFromFormat(format: "%%d%@M", value: components.month!) - } - else if (components.weekOfYear! >= 1) { + } else if (components.weekOfYear! >= 1) { return self.logicalLocalizedStringFromFormat(format: "%%d%@w", value: components.weekOfYear!) - } - else if (components.day! >= 2) { + } else if (components.day! >= 2) { return self.logicalLocalizedStringFromFormat(format: "%%d%@d", value: components.day!) - } - else if (isYesterday) { + } else if (isYesterday) { return self.logicalLocalizedStringFromFormat(format: "%%d%@d", value: 1) - } - else if (components.hour! >= 1) { + } else if (components.hour! >= 1) { return self.logicalLocalizedStringFromFormat(format: "%%d%@h", value: components.hour!) - } - else if (components.minute! >= 1) { + } else if (components.minute! >= 1) { return self.logicalLocalizedStringFromFormat(format: "%%d%@m", value: components.minute!) - } - else if (components.second! >= 3) { + } else if (components.second! >= 3) { return self.logicalLocalizedStringFromFormat(format: "%%d%@s", value: components.second!) - } - else { + } else { return self.logicalLocalizedStringFromFormat(format: "%%d%@s", value: components.second!) //return DateToolsLocalizedStrings(@"Now"); //string not yet translated 2014.04.05 } } - - - private func logicalLocalizedStringFromFormat(format: String, value: Int) -> String{ + + private func logicalLocalizedStringFromFormat(format: String, value: Int) -> String { #if os(Linux) let localeFormat = String.init(format: format, getLocaleFormatUnderscoresWithValue(Double(value)) as! CVarArg) // this may not work, unclear!! #else let localeFormat = String.init(format: format, getLocaleFormatUnderscoresWithValue(Double(value))) #endif - + return String.init(format: DateToolsLocalizedStrings(localeFormat), value) } - - - private func getLocaleFormatUnderscoresWithValue(_ value: Double) -> String{ + + private func getLocaleFormatUnderscoresWithValue(_ value: Double) -> String { let localCode = Bundle.main.preferredLocalizations[0] if (localCode == "ru" || localCode == "uk") { let XY = Int(floor(value).truncatingRemainder(dividingBy: 100)) let Y = Int(floor(value).truncatingRemainder(dividingBy: 10)) - + if(Y == 0 || Y > 4 || (XY > 10 && XY < 15)) { return "" } - - if(Y > 1 && Y < 5 && (XY < 10 || XY > 20)) { + + if(Y > 1 && Y < 5 && (XY < 10 || XY > 20)) { return "_" } - + if(Y == 1 && XY != 11) { return "__" } } - + return "" } - - + // MARK: - Localization - + private func DateToolsLocalizedStrings(_ string: String) -> String { //let classBundle = Bundle(for:TimeChunk.self as! AnyClass.Type).resourcePath!.appending("DateTools.bundle") - + //let bundelPath = Bundle(path:classBundle)! #if os(Linux) // NSLocalizedString() is not available yet, see: https://github.com/apple/swift-corelibs-foundation/blob/16f83ddcd311b768e30a93637af161676b0a5f2f/Foundation/NSData.swift @@ -246,10 +218,9 @@ public extension Date { return NSLocalizedString(string, tableName: "DateTools", bundle: Bundle.dateToolsBundle(), value: "", comment: "") #endif } - - + // MARK: - Date Earlier/Later - + /** * Return the earlier of two dates, between self and a given date. * @@ -257,10 +228,10 @@ public extension Date { * * - returns: The date that is earlier */ - func earlierDate(_ date:Date) -> Date{ + func earlierDate(_ date:Date) -> Date { return (self.timeIntervalSince1970 <= date.timeIntervalSince1970) ? self : date } - + /** * Return the later of two dates, between self and a given date. * @@ -268,8 +239,8 @@ public extension Date { * * - returns: The date that is later */ - func laterDate(_ date:Date) -> Date{ + func laterDate(_ date:Date) -> Date { return (self.timeIntervalSince1970 >= date.timeIntervalSince1970) ? self : date } - + } diff --git a/Clocker/Dependencies/Date Additions/Enums.swift b/Clocker/Dependencies/Date Additions/Enums.swift index 8274784..2c26fa9 100755 --- a/Clocker/Dependencies/Date Additions/Enums.swift +++ b/Clocker/Dependencies/Date Additions/Enums.swift @@ -34,7 +34,6 @@ public enum Relation { case none // One or more of the dates does not exist } - /** * Whether the time period is Open or Closed * diff --git a/Clocker/Dependencies/Date Additions/Integer+DateTools.swift b/Clocker/Dependencies/Date Additions/Integer+DateTools.swift index 55e4baa..efed5e6 100755 --- a/Clocker/Dependencies/Date Additions/Integer+DateTools.swift +++ b/Clocker/Dependencies/Date Additions/Integer+DateTools.swift @@ -9,51 +9,51 @@ import Foundation public extension Int { - - //MARK: TimePeriod - + + // MARK: TimePeriod + /** * A `TimeChunk` with its seconds component set to the value of self */ var seconds: TimeChunk { return TimeChunk(seconds: self, minutes: 0, hours: 0, days: 0, weeks: 0, months: 0, years: 0) } - + /** * A `TimeChunk` with its minutes component set to the value of self */ var minutes: TimeChunk { return TimeChunk(seconds: 0, minutes: self, hours: 0, days: 0, weeks: 0, months: 0, years: 0) } - + /** * A `TimeChunk` with its hours component set to the value of self */ var hours: TimeChunk { return TimeChunk(seconds: 0, minutes: 0, hours: self, days: 0, weeks: 0, months: 0, years: 0) } - + /** * A `TimeChunk` with its days component set to the value of self */ var days: TimeChunk { return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: self, weeks: 0, months: 0, years: 0) } - + /** * A `TimeChunk` with its weeks component set to the value of self */ var weeks: TimeChunk { return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 0, weeks: self, months: 0, years: 0) } - + /** * A `TimeChunk` with its months component set to the value of self */ var months: TimeChunk { return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 0, weeks: 0, months: self, years: 0) } - + /** * A `TimeChunk` with its years component set to the value of self */ diff --git a/Clocker/Dependencies/Date Additions/TimeChunk.swift b/Clocker/Dependencies/Date Additions/TimeChunk.swift index afc72bd..76f4377 100755 --- a/Clocker/Dependencies/Date Additions/TimeChunk.swift +++ b/Clocker/Dependencies/Date Additions/TimeChunk.swift @@ -20,9 +20,9 @@ import Foundation * For more information about the utility of TimeChunks in relation to Dates, see the `Date+Manipulations` class. */ public struct TimeChunk { - + // MARK: - Variables - + public var seconds = 0 public var minutes = 0 public var hours = 0 @@ -30,9 +30,9 @@ public struct TimeChunk { public var weeks = 0 public var months = 0 public var years = 0 - + public init() {} - + public init(seconds: Int, minutes: Int, hours: Int, days: Int, weeks: Int, months: Int, years: Int) { self.seconds = seconds self.minutes = minutes @@ -42,10 +42,9 @@ public struct TimeChunk { self.months = months self.years = years } - - + // MARK: - Comparisons - + /** * Check if two `TimeChunk`s are equal. * @@ -56,10 +55,9 @@ public struct TimeChunk { public func equals(chunk: TimeChunk) -> Bool { return (seconds == chunk.seconds && minutes == chunk.minutes && hours == chunk.hours && days == chunk.days && weeks == chunk.weeks && months == chunk.months && years == chunk.years) } - - + // MARK: - Conversion - + /** * Generic conversion method. Years are taken to mean * 365 days. This method should not be used for accurate @@ -124,24 +122,23 @@ public struct TimeChunk { } return 0 } - - + // MARK: - Date Creation - + /** * Returns the current date decreased by the amount in self */ public var earlier: Date { return earlier(than: Date()) } - + /** * Returns the current date increased by the amount in self */ public var later: Date { return later(than: Date()) } - + /** * Returns the given date decreased by the amount in self. * @@ -152,7 +149,7 @@ public struct TimeChunk { public func earlier(than date: Date) -> Date { return date.subtract(self) } - + /** * Returns the given date increased by the amount in self. * @@ -163,11 +160,11 @@ public struct TimeChunk { public func later(than date: Date) -> Date { return date.add(self) } - + // MARK: - Lengthen / Shorten - + // MARK: In Place - + /** * Increase the variables of self (`TimeChunk`) by the variables of the given `TimeChunk`. * @@ -184,10 +181,10 @@ public struct TimeChunk { newChunk.weeks = weeks + chunk.weeks newChunk.months = months + chunk.months newChunk.years = years + chunk.years - + return newChunk } - + /** * Decrease the variables of self (`TimeChunk`) by the variables of the given `TimeChunk`. * @@ -204,13 +201,12 @@ public struct TimeChunk { newChunk.weeks = weeks - chunk.weeks newChunk.months = months - chunk.months newChunk.years = years - chunk.years - + return newChunk } - - + // MARK: Mutation - + /** * In place, increase the variables of self (`TimeChunk`) by the variables of the given `TimeChunk`. * @@ -225,7 +221,7 @@ public struct TimeChunk { months += chunk.months years += chunk.years } - + /** * In place, decrease the variables of self (`TimeChunk`) by the variables of the given `TimeChunk`. * @@ -240,36 +236,35 @@ public struct TimeChunk { months -= chunk.months years -= chunk.years } - - + // MARK: - Operator Overloads - + /** * Operator overload for adding two `TimeChunk`s */ public static func +(leftAddend: TimeChunk, rightAddend: TimeChunk) -> TimeChunk { return leftAddend.lengthened(by: rightAddend) } - + /** * Operator overload for subtracting two `TimeChunk`s */ public static func -(minuend: TimeChunk, subtrahend: TimeChunk) -> TimeChunk { return minuend.shortened(by: subtrahend) } - + /** * Operator overload for checking if two `TimeChunk`s are equal */ public static func ==(left: TimeChunk, right: TimeChunk) -> Bool { return left.equals(chunk: right) } - + /** * Operator overload for inverting (negating all variables) a `TimeChunk` */ public static prefix func -(chunk: TimeChunk) -> TimeChunk { - var invertedChunk = chunk; + var invertedChunk = chunk invertedChunk.seconds = -chunk.seconds invertedChunk.minutes = -chunk.minutes invertedChunk.hours = -chunk.hours @@ -279,5 +274,5 @@ public struct TimeChunk { invertedChunk.years = -chunk.years return invertedChunk } - + } diff --git a/Clocker/Dependencies/Date Additions/TimePeriod.swift b/Clocker/Dependencies/Date Additions/TimePeriod.swift index d9f660a..1e72902 100755 --- a/Clocker/Dependencies/Date Additions/TimePeriod.swift +++ b/Clocker/Dependencies/Date Additions/TimePeriod.swift @@ -8,8 +8,6 @@ import Foundation - - /** * In DateTools, time periods are represented by the TimePeriod protocol. * Required variables and method impleementations are bound below. An inheritable @@ -18,14 +16,14 @@ import Foundation * [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information. */ public protocol TimePeriodProtocol { - + // MARK: - Variables - + /** * The start date for a TimePeriod representing the starting boundary of the time period */ var beginning: Date? {get set} - + /** * The end date for a TimePeriod representing the ending boundary of the time period */ @@ -33,17 +31,16 @@ public protocol TimePeriodProtocol { } public extension TimePeriodProtocol { - - + // MARK: - Information - + /** * True if the `TimePeriod`'s duration is zero */ var isMoment: Bool { return self.beginning == self.end } - + /** * The duration of the `TimePeriod` in years. * Returns the max int if beginning or end are nil. @@ -54,7 +51,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in weeks. * Returns the max int if beginning or end are nil. @@ -65,7 +62,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in days. * Returns the max int if beginning or end are nil. @@ -76,7 +73,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in hours. * Returns the max int if beginning or end are nil. @@ -87,7 +84,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in minutes. * Returns the max int if beginning or end are nil. @@ -98,7 +95,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in seconds. * Returns the max int if beginning or end are nil. @@ -109,7 +106,7 @@ public extension TimePeriodProtocol { } return Int.max } - + /** * The duration of the `TimePeriod` in a time chunk. * Returns a time chunk with all zeroes if beginning or end are nil. @@ -120,7 +117,7 @@ public extension TimePeriodProtocol { } return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 0, weeks: 0, months: 0, years: 0) } - + /** * The length of time between the beginning and end dates of the * `TimePeriod` as a `TimeInterval`. @@ -129,13 +126,12 @@ public extension TimePeriodProtocol { if self.beginning != nil && self.end != nil { return abs(self.beginning!.timeIntervalSince(self.end!)) } - + return TimeInterval(Double.greatestFiniteMagnitude) } - - + // MARK: - Time Period Relationships - + /** * The relationship of the self `TimePeriod` to the given `TimePeriod`. * Relations are stored in Enums.swift. Formal defnitions available in the provided @@ -152,53 +148,41 @@ public extension TimePeriodProtocol { if (self.beginning != nil && self.end != nil && period.beginning != nil && period.end != nil) { //Make sure time periods are of positive durations if (self.beginning!.isEarlier(than: self.end!) && period.beginning!.isEarlier(than: period.end!)) { - + //Make comparisons if (period.end!.isEarlier(than: self.beginning!)) { return .after - } - else if (period.end!.equals(self.beginning!)) { + } else if (period.end!.equals(self.beginning!)) { return .startTouching - } - else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { + } else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { return .startInside - } - else if (period.beginning!.equals(self.beginning!) && period.end!.isLater(than: self.end!)) { + } else if (period.beginning!.equals(self.beginning!) && period.end!.isLater(than: self.end!)) { return .insideStartTouching - } - else if (period.beginning!.equals(self.beginning!) && period.end!.isEarlier(than: self.end!)) { + } else if (period.beginning!.equals(self.beginning!) && period.end!.isEarlier(than: self.end!)) { return .enclosingStartTouching - } - else if (period.beginning!.isLater(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { + } else if (period.beginning!.isLater(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { return .enclosing - } - else if (period.beginning!.isLater(than: self.beginning!) && period.end!.equals(self.end!)) { + } else if (period.beginning!.isLater(than: self.beginning!) && period.end!.equals(self.end!)) { return .enclosingEndTouching - } - else if (period.beginning!.equals(self.beginning!) && period.end!.equals(self.end!)) { + } else if (period.beginning!.equals(self.beginning!) && period.end!.equals(self.end!)) { return .exactMatch - } - else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isLater(than: self.end!)) { + } else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isLater(than: self.end!)) { return .inside - } - else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.equals(self.end!)) { + } else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.equals(self.end!)) { return .insideEndTouching - } - else if (period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)) { + } else if (period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)) { return .endInside - } - else if (period.beginning!.equals(self.end!) && period.end!.isLater(than: self.end!)) { + } else if (period.beginning!.equals(self.end!) && period.end!.isLater(than: self.end!)) { return .endTouching - } - else if (period.beginning!.isLater(than: self.end!)) { + } else if (period.beginning!.isLater(than: self.end!)) { return .before } } } - - return .none; + + return .none } - + /** * If `self.beginning` and `self.end` are equal to the beginning and end of the * given `TimePeriod`. @@ -210,7 +194,7 @@ public extension TimePeriodProtocol { func equals(_ period: TimePeriodProtocol) -> Bool { return self.beginning == period.beginning && self.end == period.end } - + /** * If the given `TimePeriod`'s beginning is before `self.beginning` and * if the given 'TimePeriod`'s end is after `self.end`. @@ -222,7 +206,7 @@ public extension TimePeriodProtocol { func isInside(of period: TimePeriodProtocol) -> Bool { return period.beginning!.isEarlierThanOrEqual(to: self.beginning!) && period.end!.isLaterThanOrEqual(to: self.end!) } - + /** * If the given Date is after `self.beginning` and before `self.end`. * @@ -234,14 +218,13 @@ public extension TimePeriodProtocol { func contains(_ date: Date, interval: Interval) -> Bool { if (interval == .open) { return self.beginning!.isEarlier(than: date) && self.end!.isLater(than: date) - } - else if (interval == .closed){ + } else if (interval == .closed) { return (self.beginning!.isEarlierThanOrEqual(to: date) && self.end!.isLaterThanOrEqual(to: date)) } - + return false } - + /** * If the given `TimePeriod`'s beginning is after `self.beginning` and * if the given 'TimePeriod`'s after is after `self.end`. @@ -253,7 +236,7 @@ public extension TimePeriodProtocol { func contains(_ period: TimePeriodProtocol) -> Bool { return self.beginning!.isEarlierThanOrEqual(to: period.beginning!) && self.end!.isLaterThanOrEqual(to: period.end!) } - + /** * If self and the given `TimePeriod` share any sub-`TimePeriod`. * @@ -267,16 +250,16 @@ public extension TimePeriodProtocol { return true } //Enclosing - else if (period.beginning!.isLaterThanOrEqual(to: self.beginning!) && period.end!.isEarlierThanOrEqual(to: self.end!)){ + else if (period.beginning!.isLaterThanOrEqual(to: self.beginning!) && period.end!.isEarlierThanOrEqual(to: self.end!)) { return true } //Inside -> Out - else if(period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)){ + else if(period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)) { return true } return false } - + /** * If self and the given `TimePeriod` overlap or the period's edges touch. * @@ -287,7 +270,7 @@ public extension TimePeriodProtocol { func intersects(with period: TimePeriodProtocol) -> Bool { return self.relation(to: period) != .after && self.relation(to: period) != .before } - + /** * If self and the given `TimePeriod` have no overlap or touching edges. * @@ -298,7 +281,7 @@ public extension TimePeriodProtocol { func hasGap(between period: TimePeriodProtocol) -> Bool { return self.isBefore(period: period) || self.isAfter(period: period) } - + /** * The period of time between self and the given `TimePeriod` not contained by either. * @@ -308,14 +291,13 @@ public extension TimePeriodProtocol { */ func gap(between period: TimePeriodProtocol) -> TimeInterval { if (self.end!.isEarlier(than: period.beginning!)) { - return abs(self.end!.timeIntervalSince(period.beginning!)); - } - else if (period.end!.isEarlier(than: self.beginning!)){ - return abs(period.end!.timeIntervalSince(self.beginning!)); + return abs(self.end!.timeIntervalSince(period.beginning!)) + } else if (period.end!.isEarlier(than: self.beginning!)) { + return abs(period.end!.timeIntervalSince(self.beginning!)) } return 0 } - + /** * The period of time between self and the given `TimePeriod` not contained by either * as a `TimeChunk`. @@ -330,7 +312,7 @@ public extension TimePeriodProtocol { } return nil } - + /** * If self is after the given `TimePeriod` chronologically. (A gap must exist between the two). * @@ -341,7 +323,7 @@ public extension TimePeriodProtocol { func isAfter(period: TimePeriodProtocol) -> Bool { return self.relation(to: period) == .after } - + /** * If self is before the given `TimePeriod` chronologically. (A gap must exist between the two). * @@ -352,11 +334,11 @@ public extension TimePeriodProtocol { func isBefore(period: TimePeriodProtocol) -> Bool { return self.relation(to: period) == .before } - + // MARK: - Shifts - - //MARK: In Place - + + // MARK: In Place + /** * In place, shift the `TimePeriod` by a `TimeInterval` * @@ -366,7 +348,7 @@ public extension TimePeriodProtocol { self.beginning?.addTimeInterval(timeInterval) self.end?.addTimeInterval(timeInterval) } - + /** * In place, shift the `TimePeriod` by a `TimeChunk` * @@ -376,12 +358,11 @@ public extension TimePeriodProtocol { self.beginning = self.beginning?.add(chunk) self.end = self.end?.add(chunk) } - + // MARK: - Lengthen / Shorten - + // MARK: In Place - - + /** * In place, lengthen the `TimePeriod`, anchored at the beginning, end or center * @@ -402,7 +383,7 @@ public extension TimePeriodProtocol { break } } - + /** * In place, lengthen the `TimePeriod`, anchored at the beginning or end * @@ -423,7 +404,7 @@ public extension TimePeriodProtocol { break } } - + /** * In place, shorten the `TimePeriod`, anchored at the beginning, end or center * @@ -444,7 +425,7 @@ public extension TimePeriodProtocol { break } } - + /** * In place, shorten the `TimePeriod`, anchored at the beginning or end * @@ -475,58 +456,56 @@ public extension TimePeriodProtocol { * [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information. */ open class TimePeriod: TimePeriodProtocol { - + // MARK: - Variables /** * The start date for a TimePeriod representing the starting boundary of the time period */ public var beginning: Date? - + /** * The end date for a TimePeriod representing the ending boundary of the time period */ public var end: Date? - - + // MARK: - Initializers - + init() { - + } - + init(beginning: Date?, end: Date?) { self.beginning = beginning self.end = end } - + init(beginning: Date, duration: TimeInterval) { self.beginning = beginning self.end = beginning + duration } - + init(end: Date, duration: TimeInterval) { self.end = end self.beginning = end.addingTimeInterval(-duration) } - + init(beginning: Date, chunk: TimeChunk) { self.beginning = beginning self.end = beginning + chunk } - + init(end: Date, chunk: TimeChunk) { self.end = end self.beginning = end - chunk } - + init(chunk: TimeChunk) { self.beginning = Date() self.end = self.beginning?.add(chunk) } - - + // MARK: - Shifted - + /** * Shift the `TimePeriod` by a `TimeInterval` * @@ -540,7 +519,7 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = self.end?.addingTimeInterval(timeInterval) return timePeriod } - + /** * Shift the `TimePeriod` by a `TimeChunk` * @@ -554,11 +533,11 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = self.end?.add(chunk) return timePeriod } - + // MARK: - Lengthen / Shorten - + // MARK: New - + /** * Lengthen the `TimePeriod` by a `TimeInterval` * @@ -583,10 +562,10 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = self.end break } - + return timePeriod } - + /** * Lengthen the `TimePeriod` by a `TimeChunk` * @@ -610,10 +589,10 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = end break } - + return timePeriod } - + /** * Shorten the `TimePeriod` by a `TimeInterval` * @@ -638,10 +617,10 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = end break } - + return timePeriod } - + /** * Shorten the `TimePeriod` by a `TimeChunk` * @@ -665,20 +644,19 @@ open class TimePeriod: TimePeriodProtocol { timePeriod.end = end break } - + return timePeriod } - - + // MARK: - Operator Overloads - + /** * Operator overload for checking if two `TimePeriod`s are equal */ static func ==(leftAddend: TimePeriod, rightAddend: TimePeriod) -> Bool { return leftAddend.equals(rightAddend) } - + // Default anchor = beginning /** * Operator overload for lengthening a `TimePeriod` by a `TimeInterval` @@ -686,14 +664,14 @@ open class TimePeriod: TimePeriodProtocol { static func +(leftAddend: TimePeriod, rightAddend: TimeInterval) -> TimePeriod { return leftAddend.lengthened(by: rightAddend, at: .beginning) } - + /** * Operator overload for lengthening a `TimePeriod` by a `TimeChunk` */ static func +(leftAddend: TimePeriod, rightAddend: TimeChunk) -> TimePeriod { return leftAddend.lengthened(by: rightAddend, at: .beginning) } - + // Default anchor = beginning /** * Operator overload for shortening a `TimePeriod` by a `TimeInterval` @@ -701,14 +679,14 @@ open class TimePeriod: TimePeriodProtocol { static func -(minuend: TimePeriod, subtrahend: TimeInterval) -> TimePeriod { return minuend.shortened(by: subtrahend, at: .beginning) } - + /** * Operator overload for shortening a `TimePeriod` by a `TimeChunk` */ static func -(minuend: TimePeriod, subtrahend: TimeChunk) -> TimePeriod { return minuend.shortened(by: subtrahend, at: .beginning) } - + /** * Operator overload for checking if a `TimePeriod` is equal to a `TimePeriodProtocol` */ diff --git a/Clocker/Dependencies/Date Additions/TimePeriodChain.swift b/Clocker/Dependencies/Date Additions/TimePeriodChain.swift index 129e664..48d4ea2 100755 --- a/Clocker/Dependencies/Date Additions/TimePeriodChain.swift +++ b/Clocker/Dependencies/Date Additions/TimePeriodChain.swift @@ -18,9 +18,9 @@ import Foundation * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-chains) for more information. */ open class TimePeriodChain: TimePeriodGroup { - + // MARK: - Chain Existence Manipulation - + /** * Append a TimePeriodProtocol to the periods array and update the Chain's * beginning and end. @@ -29,20 +29,19 @@ open class TimePeriodChain: TimePeriodGroup { */ public func append(_ period: TimePeriodProtocol) { let beginning = (self.periods.count > 0) ? self.periods.last!.end! : period.beginning - + let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration) self.periods.append(newPeriod) - + //Update updateExtremes if periods.count == 1 { _beginning = period.beginning _end = period.end - } - else { + } else { _end = _end?.addingTimeInterval(period.duration) } } - + /** * Append a TimePeriodProtocol array to the periods array and update the Chain's * beginning and end. @@ -52,21 +51,20 @@ open class TimePeriodChain: TimePeriodGroup { public func append(contentsOf group: G) { for period in group.periods { let beginning = (self.periods.count > 0) ? self.periods.last!.end! : period.beginning - + let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration) self.periods.append(newPeriod) - + //Update updateExtremes if periods.count == 1 { _beginning = period.beginning _end = period.end - } - else { + } else { _end = _end?.addingTimeInterval(period.duration) } } } - + /** * Insert period into periods array at given index. * @@ -78,16 +76,14 @@ open class TimePeriodChain: TimePeriodGroup { if index == 0 && period.beginning != nil && period.end != nil { //Insert new period periods.insert(period, at: index) - } - else if period.beginning != nil && period.end != nil { + } else if period.beginning != nil && period.end != nil { //Insert new period periods.insert(period, at: index) - } - else { + } else { print("All TimePeriods in a TimePeriodChain must contain a defined start and end date") return } - + //Shift all periods after inserted period for i in 0.. index && i > 0 { @@ -96,10 +92,10 @@ open class TimePeriodChain: TimePeriodGroup { periods[i].end = periods[i].beginning!.addingTimeInterval(currentPeriod.duration) } } - + updateExtremes() } - + /** * Remove from period array at the given index. * @@ -108,17 +104,17 @@ open class TimePeriodChain: TimePeriodGroup { public func remove(at index: Int) { //Retrieve duration of period to be removed let duration = periods[index].duration - + //Remove period periods.remove(at: index) - + //Shift all periods after inserted period for i in index..(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] { return try periods.map(transform) } - + public override func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] { return try periods.filter(isIncluded) } - + internal override func reduce(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result { return try periods.reduce(initialResult, nextPartialResult) } - + /** * Removes the last object from the `TimePeriodChain` and returns it * @@ -162,17 +158,17 @@ open class TimePeriodChain: TimePeriodGroup { public func pop() -> TimePeriodProtocol? { let period = self.periods.popLast() updateExtremes() - + return period } - + internal func updateExtremes() { _beginning = periods.first?.beginning _end = periods.last?.end } - + // MARK: - Operator Overloads - + /** * Operator overload for comparing `TimePeriodChain`s to each other */ diff --git a/Clocker/Dependencies/Date Additions/TimePeriodCollection.swift b/Clocker/Dependencies/Date Additions/TimePeriodCollection.swift index 4b455e1..b928b38 100755 --- a/Clocker/Dependencies/Date Additions/TimePeriodCollection.swift +++ b/Clocker/Dependencies/Date Additions/TimePeriodCollection.swift @@ -17,9 +17,9 @@ import Foundation * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-collections) for more information. */ open class TimePeriodCollection: TimePeriodGroup { - + // MARK: - Collection Manipulation - + /** * Append a TimePeriodProtocol to the periods array and check if the Collection's * beginning and end should change. @@ -30,7 +30,7 @@ open class TimePeriodCollection: TimePeriodGroup { periods.append(period) updateExtremes(period: period) } - + /** * Append a TimePeriodProtocol array to the periods array and check if the Collection's * beginning and end should change. @@ -43,7 +43,7 @@ open class TimePeriodCollection: TimePeriodGroup { updateExtremes(period: period) } } - + /** * Append a TimePeriodGroup's periods array to the periods array of self and check if the Collection's * beginning and end should change. @@ -56,7 +56,7 @@ open class TimePeriodCollection: TimePeriodGroup { updateExtremes(period: period) } } - + /** * Insert period into periods array at given index. * @@ -67,7 +67,7 @@ open class TimePeriodCollection: TimePeriodGroup { periods.insert(newElement, at: index) updateExtremes(period: newElement) } - + /** * Remove from period array at the given index. * @@ -77,7 +77,7 @@ open class TimePeriodCollection: TimePeriodGroup { periods.remove(at: at) updateExtremes() } - + /** * Remove all periods from period array. */ @@ -85,10 +85,9 @@ open class TimePeriodCollection: TimePeriodGroup { periods.removeAll() updateExtremes() } - - + // MARK: - Sorting - + // In place /** * Sort periods array in place by beginning @@ -106,14 +105,14 @@ open class TimePeriodCollection: TimePeriodGroup { } } } - + /** * Sort periods array in place */ public func sort(by areInIncreasingOrder: (TimePeriodProtocol, TimePeriodProtocol) -> Bool) { self.periods.sort(by: areInIncreasingOrder) } - + // New collection /** * Return collection with sorted periods array by beginning @@ -136,7 +135,7 @@ open class TimePeriodCollection: TimePeriodGroup { collection.append(array) return collection } - + /** * Return collection with sorted periods array * @@ -147,10 +146,9 @@ open class TimePeriodCollection: TimePeriodGroup { collection.append(self.periods.sorted(by: areInIncreasingOrder)) return collection } - - + // MARK: - Collection Relationship - + // Potentially use .reduce() instead of these functions /** * Returns from the `TimePeriodCollection` a sub-collection of `TimePeriod`s @@ -168,7 +166,7 @@ open class TimePeriodCollection: TimePeriodGroup { }) return collection } - + /** * Returns from the `TimePeriodCollection` a sub-collection of `TimePeriod`s containing * the given date. @@ -185,7 +183,7 @@ open class TimePeriodCollection: TimePeriodGroup { }) return collection } - + /** * Returns from the `TimePeriodCollection` a sub-collection of `TimePeriod`s * containing either the start date or the end date--or both--of the given `TimePeriod`. @@ -202,9 +200,9 @@ open class TimePeriodCollection: TimePeriodGroup { }) return collection } - + // MARK: - Map - + public func map(_ transform: (TimePeriodProtocol) throws -> TimePeriodProtocol) rethrows -> TimePeriodCollection { var mappedArray = [TimePeriodProtocol]() mappedArray = try periods.map(transform) @@ -215,18 +213,18 @@ open class TimePeriodCollection: TimePeriodGroup { } return mappedCollection } - + // MARK: - Operator Overloads - + /** * Operator overload for comparing `TimePeriodCollection`s to each other */ public static func ==(left: TimePeriodCollection, right: TimePeriodCollection) -> Bool { return left.equals(right) } - - //MARK: - Helpers - + + // MARK: - Helpers + internal func updateExtremes(period: TimePeriodProtocol) { //Check incoming period against previous beginning and end date if self.count == 1 { @@ -236,9 +234,9 @@ open class TimePeriodCollection: TimePeriodGroup { _beginning = nilOrEarlier(date1: _beginning, date2: period.beginning) _end = nilOrLater(date1: _end, date2: period.end) } - + } - + internal func updateExtremes() { if periods.count == 0 { _beginning = nil @@ -252,7 +250,7 @@ open class TimePeriodCollection: TimePeriodGroup { } } } - + internal func nilOrEarlier(date1: Date?, date2: Date?) -> Date? { if date1 == nil || date2 == nil { return nil @@ -260,7 +258,7 @@ open class TimePeriodCollection: TimePeriodGroup { return date1!.earlierDate(date2!) } } - + internal func nilOrLater(date1: Date?, date2: Date?) -> Date? { if date1 == nil || date2 == nil { return nil diff --git a/Clocker/Dependencies/Date Additions/TimePeriodGroup.swift b/Clocker/Dependencies/Date Additions/TimePeriodGroup.swift index b4a8094..8a29b69 100755 --- a/Clocker/Dependencies/Date Additions/TimePeriodGroup.swift +++ b/Clocker/Dependencies/Date Additions/TimePeriodGroup.swift @@ -16,17 +16,17 @@ import Foundation * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-groups) for more information. */ open class TimePeriodGroup: Sequence { - + // MARK: - Variables - + /** * The array of periods that define the group. */ internal var periods: [TimePeriodProtocol] = [] - + internal var _beginning: Date? internal var _end: Date? - + /** * The earliest beginning date of a `TimePeriod` in the group. * Nil if any `TimePeriod` in group has a nil beginning date (indefinite). @@ -35,7 +35,7 @@ open class TimePeriodGroup: Sequence { public var beginning: Date? { return _beginning } - + /** * The latest end date of a `TimePeriod` in the group. * Nil if any `TimePeriod` in group has a nil end date (indefinite). @@ -44,14 +44,14 @@ open class TimePeriodGroup: Sequence { public var end: Date? { return _end } - + /** * The number of periods in the periods array. */ public var count: Int { return periods.count } - + /** * The total amount of time between the earliest and latest dates stored in the * periods array. Nil if any beginning or end date in any contained period is nil. @@ -62,15 +62,15 @@ open class TimePeriodGroup: Sequence { } return nil } - + // MARK: - Initializers - + public init() { - + } - + // MARK: - Comparisons - + /** * If `self.periods` contains the exact elements as the given group's periods array. * @@ -81,45 +81,44 @@ open class TimePeriodGroup: Sequence { public func equals(_ group: TimePeriodGroup) -> Bool { return containSameElements(array1: self.periods, group.periods) } - // MARK: - Sequence Protocol - + public func makeIterator() -> IndexingIterator> { return periods.makeIterator() } - + public func map(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] { return try periods.map(transform) } - + public func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] { return try periods.filter(isIncluded) } - + public func forEach(_ body: (TimePeriodProtocol) throws -> Void) rethrows { return try periods.forEach(body) } - + public func split(maxSplits: Int, omittingEmptySubsequences: Bool, whereSeparator isSeparator: (TimePeriodProtocol) throws -> Bool) rethrows -> [AnySequence] { return try periods.split(maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences, whereSeparator: isSeparator).map(AnySequence.init) } - + subscript(index: Int) -> TimePeriodProtocol { get { return periods[index] } } - + internal func reduce(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result { return try periods.reduce(initialResult, nextPartialResult) } - + internal func containSameElements(array1: [TimePeriodProtocol], _ array2: [TimePeriodProtocol]) -> Bool { guard array1.count == array2.count else { return false // No need to sorting if they already have different counts } - + var compArray1: [TimePeriodProtocol] = array1.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in if period1.beginning == nil && period2.beginning == nil { return false diff --git a/Clocker/Dependencies/Solar.swift b/Clocker/Dependencies/Solar.swift index 3499b1d..67df70e 100644 --- a/Clocker/Dependencies/Solar.swift +++ b/Clocker/Dependencies/Solar.swift @@ -4,13 +4,13 @@ import Cocoa import CoreLocation public struct Solar { - + /// The coordinate that is used for the calculation public let coordinate: CLLocationCoordinate2D - + /// The date to generate sunrise / sunset times for public fileprivate(set) var date: Date - + public fileprivate(set) var sunrise: Date? public fileprivate(set) var sunset: Date? public fileprivate(set) var civilSunrise: Date? @@ -19,24 +19,24 @@ public struct Solar { public fileprivate(set) var nauticalSunset: Date? public fileprivate(set) var astronomicalSunrise: Date? public fileprivate(set) var astronomicalSunset: Date? - + // MARK: Init - + public init?(for date: Date = Date(), coordinate: CLLocationCoordinate2D) { self.date = date - + guard CLLocationCoordinate2DIsValid(coordinate) else { return nil } - + self.coordinate = coordinate - + // Fill this Solar object with relevant data calculate() } - + // MARK: - Public functions - + /// Sets all of the Solar object's sunrise / sunset variables, if possible. /// - Note: Can return `nil` objects if sunrise / sunset does not occur on that day. public mutating func calculate() { @@ -49,14 +49,14 @@ public struct Solar { astronomicalSunrise = calculate(.sunrise, for: date, and: .astronimical) astronomicalSunset = calculate(.sunset, for: date, and: .astronimical) } - + // MARK: - Private functions - + fileprivate enum SunriseSunset { case sunrise case sunset } - + /// Used for generating several of the possible sunrise / sunset times fileprivate enum Zenith: Double { case official = 90.83 @@ -64,85 +64,85 @@ public struct Solar { case nautical = 102 case astronimical = 108 } - + fileprivate func calculate(_ sunriseSunset: SunriseSunset, for date: Date, and zenith: Zenith) -> Date? { guard let utcTimezone = TimeZone(identifier: "UTC") else { return nil } - + // Get the day of the year var calendar = Calendar(identifier: .gregorian) calendar.timeZone = utcTimezone guard let dayInt = calendar.ordinality(of: .day, in: .year, for: date) else { return nil } let day = Double(dayInt) - + // Convert longitude to hour value and calculate an approx. time let lngHour = coordinate.longitude / 15 - + let hourTime: Double = sunriseSunset == .sunrise ? 6 : 18 let t = day + ((hourTime - lngHour) / 24) - + // Calculate the suns mean anomaly let M = (0.9856 * t) - 3.289 - + // Calculate the sun's true longitude let subexpression1 = 1.916 * sin(M.degreesToRadians) let subexpression2 = 0.020 * sin(2 * M.degreesToRadians) var L = M + subexpression1 + subexpression2 + 282.634 - + // Normalise L into [0, 360] range L = normalise(L, withMaximum: 360) - + // Calculate the Sun's right ascension var RA = atan(0.91764 * tan(L.degreesToRadians)).radiansToDegrees - + // Normalise RA into [0, 360] range RA = normalise(RA, withMaximum: 360) - + // Right ascension value needs to be in the same quadrant as L... let Lquadrant = floor(L / 90) * 90 let RAquadrant = floor(RA / 90) * 90 RA = RA + (Lquadrant - RAquadrant) - + // Convert RA into hours RA = RA / 15 - + // Calculate Sun's declination let sinDec = 0.39782 * sin(L.degreesToRadians) let cosDec = cos(asin(sinDec)) - + // Calculate the Sun's local hour angle let cosH = (cos(zenith.rawValue.degreesToRadians) - (sinDec * sin(coordinate.latitude.degreesToRadians))) / (cosDec * cos(coordinate.latitude.degreesToRadians)) - + // No sunrise guard cosH < 1 else { return nil } - + // No sunset guard cosH > -1 else { return nil } - + // Finish calculating H and convert into hours let tempH = sunriseSunset == .sunrise ? 360 - acos(cosH).radiansToDegrees : acos(cosH).radiansToDegrees let H = tempH / 15.0 - + // Calculate local mean time of rising let T = H + RA - (0.06571 * t) - 6.622 - + // Adjust time back to UTC var UT = T - lngHour - + // Normalise UT into [0, 24] range UT = normalise(UT, withMaximum: 24) - + // Calculate all of the sunrise's / sunset's date components let hour = floor(UT) let minute = floor((UT - hour) * 60.0) let second = (((UT - hour) * 60) - minute) * 60.0 - + let shouldBeYesterday = lngHour > 0 && UT > 12 && sunriseSunset == .sunrise let shouldBeTomorrow = lngHour < 0 && UT < 12 && sunriseSunset == .sunset - + let setDate: Date if shouldBeYesterday { setDate = Date(timeInterval: -(60 * 60 * 24), since: date) @@ -151,35 +151,35 @@ public struct Solar { } else { setDate = date } - + var components = calendar.dateComponents([.day, .month, .year], from: setDate) components.hour = Int(hour) components.minute = Int(minute) components.second = Int(second) - + calendar.timeZone = utcTimezone return calendar.date(from: components) } - + /// Normalises a value between 0 and `maximum`, by adding or subtracting `maximum` fileprivate func normalise(_ value: Double, withMaximum maximum: Double) -> Double { var value = value - + if value < 0 { value += maximum } - + if value > maximum { value -= maximum } - + return value } - + } extension Solar { - + /// Whether the location specified by the `latitude` and `longitude` is in daytime on `date` /// - Complexity: O(1) public var isDaytime: Bool { @@ -189,23 +189,23 @@ extension Solar { else { return false } - + let beginningOfDay = sunrise.timeIntervalSince1970 let endOfDay = sunset.timeIntervalSince1970 let currentTime = self.date.timeIntervalSince1970 - + let isSunriseOrLater = currentTime >= beginningOfDay let isBeforeSunset = currentTime < endOfDay - + return isSunriseOrLater && isBeforeSunset } - + /// Whether the location specified by the `latitude` and `longitude` is in nighttime on `date` /// - Complexity: O(1) public var isNighttime: Bool { return !isDaytime } - + } // MARK: - Helper extensions @@ -214,9 +214,8 @@ private extension Double { var degreesToRadians: Double { return Double(self) * (Double.pi / 180.0) } - + var radiansToDegrees: Double { return (Double(self) * 180.0) / Double.pi } } - diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index 4bd6c11..b80d8f9 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -116,7 +116,7 @@ extension EventCenter { if let `self` = self, entity == .event, granted { self.saveDefaultIdentifiersList() } - + completionHandler(granted) } } @@ -146,12 +146,12 @@ extension EventCenter { print("Unable to filter events because user hasn't selected calendars") } - + func saveDefaultIdentifiersList() { OperationQueue.main.addOperation { [weak self] in guard let `self` = self else { return } let allCalendars = self.retrieveAllCalendarIdentifiers() - + if !allCalendars.isEmpty { UserDefaults.standard.set(allCalendars, forKey: CLSelectedCalendars) print("Finished saving all calendar identifiers in default") @@ -165,7 +165,7 @@ extension EventCenter { return calendar.calendarIdentifier } } - + func fetchEvents(_ start: Int, _ end: Int) { if calendarAccessDenied() || calendarAccessNotDetermined() { print("Refetching aborted because we don't have permission!") @@ -258,9 +258,7 @@ extension EventCenter { for date in eventsForDateMapper.keys { let sortedEvents = eventsForDateMapper[date]?.sorted(by: { (e1, e2) -> Bool in - if e1.isAllDay { return true } - else if e2.isAllDay { return false } - else { return e1.event.startDate < e2.event.startDate } + if e1.isAllDay { return true } else if e2.isAllDay { return false } else { return e1.event.startDate < e2.event.startDate } }) eventsForDateMapper[date] = sortedEvents } diff --git a/Clocker/Menu Bar/MenubarHandler.swift b/Clocker/Menu Bar/MenubarHandler.swift index 68d7e2f..6bc0da2 100644 --- a/Clocker/Menu Bar/MenubarHandler.swift +++ b/Clocker/Menu Bar/MenubarHandler.swift @@ -5,20 +5,20 @@ import EventKit class MenubarHandler: NSObject { @objc func titleForMenubar() -> String? { - + if let nextEvent = checkForUpcomingEvents() { return nextEvent } - + guard let menubarTitles = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] else { return nil } - + // If the menubar is in compact mode, we don't need any of the below calculations; exit early if DataStore.shared().shouldDisplay(.menubarCompactMode) { return nil } - + if menubarTitles.count > 0 { let titles = menubarTitles.map({ (data) -> String? in let timezone = TimezoneData.customObject(from: data) @@ -34,7 +34,7 @@ class MenubarHandler: NSObject { } private func checkForUpcomingEvents() -> String? { - + if DataStore.shared().shouldDisplay(.showMeetingInMenubar) { let filteredDates = EventCenter.sharedCenter().eventsForDate @@ -42,20 +42,20 @@ class MenubarHandler: NSObject { guard let events = filteredDates[autoupdatingCal.startOfDay(for: Date())] else { return nil } - + for event in events { - + if event.event.startDate.timeIntervalSinceNow > 0 && !event.isAllDay { - + let timeForEventToStart = event.event.startDate.timeIntervalSinceNow / 60 - + if timeForEventToStart > 30 { - + print("Our next event: \(event.event.title ?? "Error") starts in \(timeForEventToStart) mins") - + continue } - + return EventCenter.sharedCenter().format(event: event.event) } } diff --git a/Clocker/Menu Bar/StatusContainerView.swift b/Clocker/Menu Bar/StatusContainerView.swift index 34f5419..f5e3891 100644 --- a/Clocker/Menu Bar/StatusContainerView.swift +++ b/Clocker/Menu Bar/StatusContainerView.swift @@ -4,42 +4,42 @@ import Cocoa func bufferCalculatedWidth() -> Int { var totalWidth = 55 - + if DataStore.shared().shouldShowDateInMenubar() { totalWidth += 8 } - + if DataStore.shared().shouldDisplay(.twelveHour) { totalWidth += 18 } - + if DataStore.shared().shouldDisplay(.seconds) { totalWidth += 15 } - + return totalWidth } func compactWidth(for timezone: TimezoneData) -> Int { - + var totalWidth = 55 let timeFormat = timezone.timezoneFormat() - + if DataStore.shared().shouldShowDateInMenubar() { totalWidth += 8 } - + if timeFormat == DateFormat.twelveHour || timeFormat == DateFormat.twelveHourWithSeconds { totalWidth += 18 } else if timeFormat == DateFormat.twentyFourHour || timeFormat == DateFormat.twentyFourHourWithSeconds { totalWidth += 0 } - + if timezone.shouldShowSeconds() { // Slight buffer needed when the Menubar supplementary text was Mon 9:27:58 AM totalWidth += 15 } - + return totalWidth } @@ -47,17 +47,17 @@ func compactWidth(for timezone: TimezoneData) -> Int { let bufferWidth: CGFloat = 9.5 class StatusContainerView: NSView { - + private var previousX: Int = 0 - + override func awakeFromNib() { super.awakeFromNib() wantsLayer = true layer?.backgroundColor = NSColor.clear.cgColor } - + init(with timezones: [Data]) { - + func addSubviews() { timezones.forEach { if let timezoneObject = TimezoneData.customObject(from: $0) { @@ -65,69 +65,69 @@ class StatusContainerView: NSView { } } } - + func containerWidth(for timezones: [Data]) -> CGFloat { let compressedWidth = timezones.reduce(0.0) { (result, timezone) -> CGFloat in - + if let timezoneObject = TimezoneData.customObject(from: timezone) { let precalculatedWidth = Double(compactWidth(for: timezoneObject)) let operationObject = TimezoneDataOperations(with: timezoneObject) let calculatedSize = compactModeTimeFont.size(operationObject.compactMenuHeader(), precalculatedWidth, attributes: timeAttributes) return result + calculatedSize.width + bufferWidth } - + return result + CGFloat(bufferCalculatedWidth()) } - + let calculatedWidth = min(compressedWidth, CGFloat(timezones.count * bufferCalculatedWidth())) return calculatedWidth } - + let statusItemWidth = containerWidth(for: timezones) let frame = NSRect(x: 0, y: 0, width: statusItemWidth, height: 30) super.init(frame: frame) - + addSubviews() } - + required init?(coder decoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func addTimezone(_ timezone: TimezoneData) { - + let calculatedWidth = bestWidth(for: timezone) - + let frame = NSRect(x: previousX, y: 0, width: calculatedWidth, height: 30) - + let statusItemView = StatusItemView(frame: frame) statusItemView.dataObject = timezone - + addSubview(statusItemView) - + previousX += calculatedWidth } - + private func bestWidth(for timezone: TimezoneData) -> Int { let operation = TimezoneDataOperations(with: timezone) - + let bestSize = compactModeTimeFont.size(operation.compactMenuHeader(), Double(compactWidth(for: timezone)), attributes: timeAttributes) - + return Int(bestSize.width + bufferWidth) } - + func updateTime() { - + if subviews.isEmpty { assertionFailure("Subviews count should > 0") } - + subviews.forEach { if let statusItem = $0 as? StatusItemView { statusItem.updateTimeInMenubar() } } } - + } diff --git a/Clocker/Menu Bar/StatusItemHandler.swift b/Clocker/Menu Bar/StatusItemHandler.swift index 00ac7a0..339ed6d 100644 --- a/Clocker/Menu Bar/StatusItemHandler.swift +++ b/Clocker/Menu Bar/StatusItemHandler.swift @@ -11,9 +11,9 @@ private enum MenubarState { class StatusItemHandler: NSObject { var hasActiveIcon: Bool = false - + var menubarTimer: Timer? - + var statusItem: NSStatusItem = { let s = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) s.highlightMode = false @@ -25,9 +25,9 @@ class StatusItemHandler: NSObject { private var parentView: StatusContainerView? private var nsCalendar = Calendar.autoupdatingCurrent - + private lazy var units: Set = Set([.era, .year, .month, .day, .hour, .minute]) - + // Current State is set twice when the user first launches an app. // First, when StatusItemHandler() is instantiated in AppDelegate // Second, when AppDelegate.fetchLocalTimezone() is called triggering a customLabel didSet. @@ -44,7 +44,7 @@ class StatusItemHandler: NSObject { case .icon: statusItem.button?.image = nil } - + // Now setup for the new menubar state switch currentState { case .compactText: @@ -54,7 +54,7 @@ class StatusItemHandler: NSObject { case .icon: setClockerIcon() } - + print("\nStatus Bar Current State changed: \(currentState)\n") } } @@ -120,7 +120,7 @@ class StatusItemHandler: NSObject { parentView = nil let menubarTimezones = retrieveSyncedMenubarTimezones() - + if menubarTimezones.isEmpty { currentState = .icon return @@ -130,10 +130,10 @@ class StatusItemHandler: NSObject { statusItem.view = parentView statusItem.view?.window?.backgroundColor = NSColor.clear } - + private func retrieveSyncedMenubarTimezones() -> [Data] { let defaultPreferences = DataStore.shared().retrieve(key: CLDefaultPreferenceKey) as? [Data] ?? [] - + let menubarTimezones = defaultPreferences.filter { (data) -> Bool in if let timezoneObj = TimezoneData.customObject(from: data) { return timezoneObj.isFavourite == 1 @@ -190,10 +190,10 @@ class StatusItemHandler: NSObject { RunLoop.main.add(runLoopTimer, forMode: .common) } - + private func shouldDisplaySecondsInMenubar() -> Bool { let syncedTimezones = retrieveSyncedMenubarTimezones() - + for timezone in syncedTimezones { if let timezoneObj = TimezoneData.customObject(from: timezone) { let shouldShowSeconds = timezoneObj.shouldShowSeconds() @@ -203,7 +203,7 @@ class StatusItemHandler: NSObject { } continue } - + return false } diff --git a/Clocker/Menu Bar/StatusItemView.swift b/Clocker/Menu Bar/StatusItemView.swift index eb7fb5b..7267268 100644 --- a/Clocker/Menu Bar/StatusItemView.swift +++ b/Clocker/Menu Bar/StatusItemView.swift @@ -2,7 +2,6 @@ import Cocoa - private var defaultParagraphStyle: NSMutableParagraphStyle { let p = NSMutableParagraphStyle() p.alignment = .center @@ -15,20 +14,20 @@ var compactModeTimeFont: NSFont { } var timeAttributes: [NSAttributedString.Key: AnyObject] { - + let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black - + let attributes = [ NSAttributedString.Key.font: compactModeTimeFont, NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.backgroundColor: NSColor.clear, - NSAttributedString.Key.paragraphStyle: defaultParagraphStyle, + NSAttributedString.Key.paragraphStyle: defaultParagraphStyle ] return attributes } class StatusItemView: NSView { - + // MARK: Private variables private let locationView: NSTextField = NSTextField(labelWithString: "Hello") private let timeView: NSTextField = NSTextField(labelWithString: "Mon 19:14 PM") @@ -37,7 +36,7 @@ class StatusItemView: NSView { } private var textFontAttributes: [NSAttributedString.Key: Any] { let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black - + let textFontAttributes = [ NSAttributedString.Key.font: NSFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: textColor, @@ -46,7 +45,7 @@ class StatusItemView: NSView { ] return textFontAttributes } - + // MARK: Public var dataObject: TimezoneData! { didSet { @@ -56,23 +55,23 @@ class StatusItemView: NSView { override init(frame frameRect: NSRect) { super.init(frame: frameRect) - + [timeView, locationView].forEach { $0.wantsLayer = true $0.applyDefaultStyle() $0.translatesAutoresizingMaskIntoConstraints = false addSubview($0) } - + timeView.disableWrapping() - + NSLayoutConstraint.activate([ locationView.leadingAnchor.constraint(equalTo: leadingAnchor), locationView.trailingAnchor.constraint(equalTo: trailingAnchor), locationView.topAnchor.constraint(equalTo: topAnchor, constant: 7), locationView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.35) ]) - + NSLayoutConstraint.activate([ timeView.leadingAnchor.constraint(equalTo: leadingAnchor), timeView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0), @@ -80,26 +79,26 @@ class StatusItemView: NSView { timeView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } - + func updateTimeInMenubar() { timeView.attributedStringValue = NSAttributedString(string: operationsObject.compactMenuHeader(), attributes: timeAttributes) } - + private func initialSetup() { locationView.attributedStringValue = NSAttributedString(string: dataObject.formattedTimezoneLabel(), attributes: textFontAttributes) timeView.attributedStringValue = NSAttributedString(string: operationsObject.compactMenuHeader(), attributes: timeAttributes) } - + required init?(coder decoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func mouseDown(with event: NSEvent) { super.mouseDown(with: event) guard let mainDelegate = NSApplication.shared.delegate as? AppDelegate else { return } - + mainDelegate.togglePanel(event) } } diff --git a/Clocker/Onboarding/FinalOnboardingViewController.swift b/Clocker/Onboarding/FinalOnboardingViewController.swift index 879a88b..f74304a 100644 --- a/Clocker/Onboarding/FinalOnboardingViewController.swift +++ b/Clocker/Onboarding/FinalOnboardingViewController.swift @@ -7,7 +7,7 @@ class FinalOnboardingViewController: NSViewController { @IBOutlet var subtitleLabel: NSTextField! @IBOutlet var accesoryLabel: NSTextField! @IBOutlet var accessoryImageView: NSImageView! - + override func viewDidLoad() { super.viewDidLoad() titleLabel.stringValue = "You're all set!" diff --git a/Clocker/Onboarding/OnboardingController.swift b/Clocker/Onboarding/OnboardingController.swift index 1142ac9..e6f9d2d 100644 --- a/Clocker/Onboarding/OnboardingController.swift +++ b/Clocker/Onboarding/OnboardingController.swift @@ -20,7 +20,7 @@ class OnboardingController: NSWindowController { super.showWindow(sender) window?.center() } - + func launch() { showWindow(nil) NSApp.activate(ignoringOtherApps: true) diff --git a/Clocker/Onboarding/OnboardingParentViewController.swift b/Clocker/Onboarding/OnboardingParentViewController.swift index cbe30b1..326efc7 100644 --- a/Clocker/Onboarding/OnboardingParentViewController.swift +++ b/Clocker/Onboarding/OnboardingParentViewController.swift @@ -60,14 +60,14 @@ class OnboardingParentViewController: NSViewController { private func setupUI() { setIdentifiersForTests() - + positiveButton.title = "Get Started" positiveButton.tag = OnboardingType.welcome.rawValue backButton.tag = OnboardingType.welcome.rawValue [negativeButton, backButton].forEach { $0?.isHidden = true } } - + private func setIdentifiersForTests() { positiveButton.setAccessibilityIdentifier("Forward") negativeButton.setAccessibilityIdentifier("Alternate") @@ -162,21 +162,21 @@ class OnboardingParentViewController: NSViewController { } } else { - + self.positiveButton.tag = OnboardingType.complete.rawValue - + // Install the menubar option! let appDelegate = NSApplication.shared.delegate as? AppDelegate appDelegate?.continueUsually() - + view.window?.close() - + if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) == false { UserDefaults.standard.set(true, forKey: CLShowOnboardingFlow) } } } - + private func addChildIfNeccessary(_ vc: NSViewController) { if children.contains(vc) == false { addChild(vc) @@ -228,12 +228,12 @@ class OnboardingParentViewController: NSViewController { self.negativeButton.isHidden = false } } else if backButton.tag == OnboardingType.search.rawValue { - + guard let fromViewController = finalOnboardingVC, let toViewController = onboardingSearchVC else { assertionFailure() return } - + transition(from: fromViewController, to: toViewController, options: .slideRight) { @@ -242,17 +242,17 @@ class OnboardingParentViewController: NSViewController { self.positiveButton.title = "Continue" self.negativeButton.isHidden = true } - + } } private func shouldStartAtLogin(_ shouldStart: Bool) { - + // If tests are going on, we don't want to enable/disable launch at login! if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) { return } - + UserDefaults.standard.set(shouldStart ? 1 : 0, forKey: CLStartAtLogin) if !SMLoginItemSetEnabled("com.abhishek.ClockerHelper" as CFString, shouldStart) { @@ -260,22 +260,22 @@ class OnboardingParentViewController: NSViewController { } else { Logger.log(object: ["Successful": "YES"], for: "Start Clocker Login") } - + if shouldStart { Logger.log(object: [:], for: "Enable Launch at Login while Onboarding") } else { Logger.log(object: [:], for: "Disable Launch at Login while Onboarding") } } - + func logExitPoint() { let currentViewController = currentController() print(currentViewController) Logger.log(object: currentViewController, for: "Onboarding Process Exit") } - + private func currentController() -> [String: String] { - + switch positiveButton.tag { case 0: return ["Onboarding Process Interrupted": "Welcome View"] diff --git a/Clocker/Onboarding/OnboardingPermissionsViewController.swift b/Clocker/Onboarding/OnboardingPermissionsViewController.swift index b0a75e2..2f53763 100644 --- a/Clocker/Onboarding/OnboardingPermissionsViewController.swift +++ b/Clocker/Onboarding/OnboardingPermissionsViewController.swift @@ -94,16 +94,16 @@ class OnboardingPermissionsViewController: NSViewController { guard let `self` = self else { return } self.calendarActivityIndicator.stopAnimation(nil) - + if granted { self.calendarGrantButton.title = "Granted" - + self.view.window?.orderBack(nil) NSApp.activate(ignoringOtherApps: true) // Used to update CalendarViewController's view NotificationCenter.default.post(name: .calendarAccessGranted, object: nil) - + } else { Logger.log(object: ["Reminder Access Not Granted": "YES"], for: "Reminder Access Not Granted") } @@ -130,10 +130,10 @@ class OnboardingPermissionsViewController: NSViewController { if granted { OperationQueue.main.addOperation { - + self.view.window?.orderBack(nil) NSApp.activate(ignoringOtherApps: true) - + self.reminderGrantButton.title = "Granted" } } else { diff --git a/Clocker/Onboarding/OnboardingSearchController.swift b/Clocker/Onboarding/OnboardingSearchController.swift index 71c4132..0ccdaa9 100644 --- a/Clocker/Onboarding/OnboardingSearchController.swift +++ b/Clocker/Onboarding/OnboardingSearchController.swift @@ -15,7 +15,7 @@ class OnboardingSearchController: NSViewController { @IBOutlet private var resultsTableView: NSTableView! @IBOutlet private var accessoryLabel: NSTextField! @IBOutlet weak var undoButton: NSButton! - + private var results: [TimezoneData] = [] private var dataTask: URLSessionDataTask? = .none @@ -36,29 +36,29 @@ class OnboardingSearchController: NSViewController { } resultsTableView.reloadData() - + func setupUndoButton() { let font = NSFont(name: "Avenir", size: 13)! let attributes = [NSAttributedString.Key.foregroundColor: NSColor.linkColor, NSAttributedString.Key.font: font] undoButton.attributedTitle = NSAttributedString(string: "UNDO", attributes: attributes) } - + setupUndoButton() } @objc func doubleClickAction(_: NSTableView?) { [accessoryLabel].forEach { $0?.isHidden = false } - + if resultsTableView.selectedRow >= 0 && resultsTableView.selectedRow < results.count { let selectedTimezone = results[resultsTableView.selectedRow] - + addTimezoneToDefaults(selectedTimezone) } } private func addTimezoneToDefaults(_ timezone: TimezoneData) { - + func setupLabelHidingTimer() { Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { _ in @@ -67,8 +67,7 @@ class OnboardingSearchController: NSViewController { } } } - - + if resultsTableView.selectedRow == -1 { setInfoLabel(PreferencesConstants.noTimezoneSelectedErrorMessage) setupLabelHidingTimer() @@ -88,7 +87,7 @@ class OnboardingSearchController: NSViewController { fetchTimezone(for: latitude, and: longitude, timezone) } - + // We want to display the undo button only if we've added a timezone. // If else, we want it hidden. This below method ensures that. private func setInfoLabel(_ text: String) { @@ -98,12 +97,12 @@ class OnboardingSearchController: NSViewController { /// Returns true if there's an error. private func handleEdgeCase(for response: Data?) -> Bool { - + func setErrorPlaceholders() { setInfoLabel("No timezone found! Try entering an exact name.") searchBar.placeholderString = placeholders.randomElement() } - + guard let json = response, let jsonUnserialized = try? JSONSerialization.jsonObject(with: json, options: .allowFragments), let unwrapped = jsonUnserialized as? [String: Any] else { setErrorPlaceholders() return true @@ -143,7 +142,7 @@ class OnboardingSearchController: NSViewController { let urlString = "https://maps.googleapis.com/maps/api/timezone/json?location=\(tuple)×tamp=\(timeStamp)&key=\(CLGeocodingKey)" NetworkManager.task(with: urlString) { [weak self] response, error in - + guard let `self` = self else { return } OperationQueue.main.addOperation { @@ -166,7 +165,7 @@ class OnboardingSearchController: NSViewController { "latitude": latitude, "longitude": longitude, "nextUpdate": CLEmptyString, - CLCustomLabel: filteredAddress, + CLCustomLabel: filteredAddress ] as [String: Any] DataStore.shared().addTimezone(TimezoneData(with: newTimeZone)) @@ -175,10 +174,10 @@ class OnboardingSearchController: NSViewController { self.accessoryLabel.stringValue = "Added \(filteredAddress)." self.undoButton.isHidden = false - + Logger.log(object: ["Place Name": filteredAddress], for: "Added Timezone while Onboarding") } - + // Cleanup. self.resetSearchView() } else { @@ -210,7 +209,7 @@ class OnboardingSearchController: NSViewController { } @IBAction func search(_ sender: NSSearchField) { - + resultsTableView.deselectAll(nil) let searchString = sender.stringValue @@ -235,87 +234,87 @@ class OnboardingSearchController: NSViewController { NSObject.cancelPreviousPerformRequests(withTarget: self) perform(#selector(OnboardingSearchController.actualSearch), with: nil, afterDelay: 0.5) } - + @objc func actualSearch() { - + func setupForError() { self.resultsTableView.isHidden = true } - + let userPreferredLanguage = Locale.preferredLanguages.first ?? "en-US" - + var searchString = searchBar.stringValue - + let words = searchString.components(separatedBy: CharacterSet.whitespacesAndNewlines) - + searchString = words.joined(separator: CLEmptyString) - + let urlString = "https://maps.googleapis.com/maps/api/geocode/json?address=\(searchString)&key=\(CLGeocodingKey)&language=\(userPreferredLanguage)" - + dataTask = NetworkManager.task(with: urlString, completionHandler: { [weak self] response, error in - + guard let `self` = self else { return } - + OperationQueue.main.addOperation { - + print("Search string was: \(searchString)") - + let currentSearchBarValue = self.searchBar.stringValue - + let words = currentSearchBarValue.components(separatedBy: CharacterSet.whitespacesAndNewlines) - + if words.joined(separator: CLEmptyString) != searchString { return } - + self.results = [] - + if let errorPresent = error { if errorPresent.localizedDescription == PreferencesConstants.offlineErrorMessage { self.setInfoLabel(PreferencesConstants.noInternetConnectivityError) } else { self.setInfoLabel(PreferencesConstants.tryAgainMessage) } - + setupForError() return } - + guard let data = response else { self.setInfoLabel(PreferencesConstants.tryAgainMessage) setupForError() return } - + let searchResults = self.decode(from: data) - + if searchResults?.status == "ZERO_RESULTS" { self.setInfoLabel("No results! 😔 Try entering the exact name.") setupForError() return } - + for result in searchResults!.results { let location = result.geometry.location let latitude = location.lat let longitude = location.lng let formattedAddress = result.formattedAddress - + let totalPackage = [ "latitude": latitude, "longitude": longitude, CLTimezoneName: formattedAddress, CLCustomLabel: formattedAddress, CLTimezoneID: CLEmptyString, - CLPlaceIdentifier: result.placeId, + CLPlaceIdentifier: result.placeId ] as [String: Any] - + self.results.append(TimezoneData(with: totalPackage)) } - + self.setInfoLabel(CLEmptyString) - + self.resultsTableView.reloadData() } }) @@ -339,13 +338,12 @@ class OnboardingSearchController: NSViewController { searchBar.stringValue = CLEmptyString searchBar.placeholderString = placeholders.randomElement() } - - + @IBAction func undoAction(_ sender: Any) { DataStore.shared().removeLastTimezone() setInfoLabel("Removed.") } - + } extension OnboardingSearchController: NSTableViewDataSource { diff --git a/Clocker/Overall App/AppDefaults.swift b/Clocker/Overall App/AppDefaults.swift index fa4d0f4..cf61a46 100644 --- a/Clocker/Overall App/AppDefaults.swift +++ b/Clocker/Overall App/AppDefaults.swift @@ -3,82 +3,82 @@ import Cocoa class AppDefaults { - + class func initialize() { initializeDefaults() } - + private class func deleteOldUserDefaults() { let userDefaults = UserDefaults.standard - + // Now delete the old preferences if let bundleID = Bundle.main.bundleIdentifier, userDefaults.object(forKey: "PreferencesHaveBeenWiped") == nil { userDefaults.removePersistentDomain(forName: bundleID) userDefaults.set(true, forKey: "PreferencesHaveBeenWiped") } } - + private class func initializeDefaults() { - + let userDefaults = UserDefaults.standard - + let menubarFavourites = userDefaults.object(forKey: CLMenubarFavorites) let timezones = userDefaults.object(forKey: CLDefaultPreferenceKey) let selectedCalendars = userDefaults.object(forKey: CLSelectedCalendars) - + // Now delete the old preferences userDefaults.wipeIfNeccesary() - + // Register the usual suspects userDefaults.register(defaults: defaultsDictionary()) - + // Set arrays and our custom objects userDefaults.set(menubarFavourites, forKey: CLMenubarFavorites) userDefaults.set(timezones, forKey: CLDefaultPreferenceKey) userDefaults.set(selectedCalendars, forKey: CLSelectedCalendars) - + // Set the theme default as Light! setDefaultTheme() - + // If we already have timezones to display in menubar, do nothing. // Else, we switch the menubar mode default to compact mode for new users if userDefaults.bool(forKey: CLDefaultMenubarMode) == false { - + if let menubarFavourites = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data], menubarFavourites.isEmpty == false { userDefaults.set(1, forKey: CLMenubarCompactMode) } else { userDefaults.set(0, forKey: CLMenubarCompactMode) } - + userDefaults.set(true, forKey: CLDefaultMenubarMode) } - + if userDefaults.bool(forKey: CLSwitchToCompactModeAlert) == false { - + userDefaults.set(true, forKey: CLSwitchToCompactModeAlert) - + if let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data], menubarFavourites.count > 1 { - + // If the user is already using the compact mode, abort. if DataStore.shared().shouldDisplay(.menubarCompactMode) { return } - + showCompactModeAlert() } } } - + private class func setDefaultTheme() { let defaults = UserDefaults.standard - + if defaults.object(forKey: CLThemeKey) == nil { Themer.shared().set(theme: 0) } - + // Set default to System theme for Mojave if #available(macOS 10.14, *) { - + if defaults.bool(forKey: CLDefaultThemeOnMojave) == false { if isDarkModeOn() { Themer.shared().set(theme: 2) @@ -88,34 +88,34 @@ class AppDefaults { } } } - + private class func showCompactModeAlert() { // Time to display the alert. NSApplication.shared.activate(ignoringOtherApps: true) - + let alert = NSAlert() alert.messageText = "Save space on your menu bar" alert.informativeText = "Enable Menubar Compact Mode to fit in more timezones in less space!" alert.addButton(withTitle: "Enable Compact Mode") alert.addButton(withTitle: "Cancel") - + let response = alert.runModal() - + if response.rawValue == 1000 { OperationQueue.main.addOperation { UserDefaults.standard.set(0, forKey: CLMenubarCompactMode) - + guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else { return } - + statusItem.setupStatusItem() - + Logger.log(object: ["Context": "On Launch"], for: "Switched to Compact Mode") } } } - + private class func defaultsDictionary() -> [String: Any] { return [CLThemeKey: 0, CLDisplayFutureSliderKey: 0, @@ -143,12 +143,12 @@ class AppDefaults { } extension UserDefaults { - + func wipeIfNeccesary() { if let bundleID = Bundle.main.bundleIdentifier, object(forKey: "PreferencesHaveBeenWiped") == nil { removePersistentDomain(forName: bundleID) set(true, forKey: "PreferencesHaveBeenWiped") } } - + } diff --git a/Clocker/Overall App/AppKit + Additions.swift b/Clocker/Overall App/AppKit + Additions.swift index aca4c49..9ca8fe6 100644 --- a/Clocker/Overall App/AppKit + Additions.swift +++ b/Clocker/Overall App/AppKit + Additions.swift @@ -1,42 +1,42 @@ // Copyright © 2015 Abhishek Banthia extension NSTextField { - + func applyDefaultStyle() { backgroundColor = NSColor.clear isEditable = false isBordered = false allowsDefaultTighteningForTruncation = true - + if #available(OSX 10.12.2, *) { isAutomaticTextCompletionEnabled = false allowsCharacterPickerTouchBarItem = false } } - + func disableWrapping() { usesSingleLineMode = false cell?.wraps = false cell?.isScrollable = true } - + } extension NSFont { - + func size(_ string: String, _ width: Double, attributes: [NSAttributedString.Key: AnyObject]) -> CGSize { let size = CGSize(width: width, height: Double.greatestFiniteMagnitude) - + var otherAttributes: [NSAttributedString.Key: AnyObject] = [NSAttributedString.Key.font: self] - + attributes.forEach { (arg) in let (key, value) = arg; otherAttributes[key] = value } - + return NSString(string: string).boundingRect(with: size, options: NSString.DrawingOptions.usesLineFragmentOrigin, attributes: attributes).size } - + } class ClockerSearchField: NSSearchField { diff --git a/Clocker/Overall App/DataStore.swift b/Clocker/Overall App/DataStore.swift index 9d4a75e..f210c7f 100644 --- a/Clocker/Overall App/DataStore.swift +++ b/Clocker/Overall App/DataStore.swift @@ -20,7 +20,7 @@ enum ViewType { class DataStore: NSObject { private static var sharedStore = DataStore(with: UserDefaults.standard) private var userDefaults: UserDefaults! - + // Since this pref can accessed every second, let's cache this private var shouldDisplayDateInMenubar: Bool = false @@ -41,7 +41,7 @@ class DataStore: NSObject { return preferences } - + func updateDayPreference() { shouldDisplayDateInMenubar = shouldDisplay(.dayInMenubar) } @@ -49,7 +49,7 @@ class DataStore: NSObject { func shouldShowDateInMenubar() -> Bool { return shouldDisplayDateInMenubar } - + func setTimezones(_ timezones: [Data]) { userDefaults.set(timezones, forKey: CLDefaultPreferenceKey) } @@ -66,18 +66,18 @@ class DataStore: NSObject { userDefaults.set(defaults, forKey: CLDefaultPreferenceKey) } - + func removeLastTimezone() { var currentLineup = timezones() - + if currentLineup.isEmpty { return } - + currentLineup.removeLast() - + Logger.log(object: [:], for: "Undo Action Executed during Onboarding") - + userDefaults.set(currentLineup, forKey: CLDefaultPreferenceKey) } @@ -157,13 +157,13 @@ class DataStore: NSObject { return false } return value.isEqual(to: NSNumber(value: 0)) - + case .menubarCompactMode: - + guard let value = retrieve(key: CLMenubarCompactMode) as? Int else { return false } - + return value == 0 } } diff --git a/Clocker/Overall App/DateFormatterManager.swift b/Clocker/Overall App/DateFormatterManager.swift index 82a019d..edee5e2 100644 --- a/Clocker/Overall App/DateFormatterManager.swift +++ b/Clocker/Overall App/DateFormatterManager.swift @@ -45,7 +45,7 @@ class DateFormatterManager: NSObject { specializedFormatter.locale = locale return specializedFormatter } - + @objc class func localizedFormatter(with format: String, for timezoneIdentifier: String, locale: Locale = Locale.autoupdatingCurrent) -> DateFormatter { dateFormatter.dateStyle = .none dateFormatter.timeStyle = .none @@ -54,7 +54,7 @@ class DateFormatterManager: NSObject { dateFormatter.timeZone = TimeZone(identifier: timezoneIdentifier) return dateFormatter } - + @objc class func localizedCalendaricalDateFormatter(with format: String) -> DateFormatter { calendarDateFormatter.dateStyle = .none calendarDateFormatter.timeStyle = .none @@ -63,7 +63,7 @@ class DateFormatterManager: NSObject { calendarDateFormatter.calendar = gregorianCalendar return calendarDateFormatter } - + @objc class func localizedSimpleFormatter(_ format: String) -> DateFormatter { localizedSimpleFormatter.dateStyle = .none localizedSimpleFormatter.timeStyle = .none diff --git a/Clocker/Overall App/NetworkManager.swift b/Clocker/Overall App/NetworkManager.swift index 8be44c3..22d8950 100644 --- a/Clocker/Overall App/NetworkManager.swift +++ b/Clocker/Overall App/NetworkManager.swift @@ -15,7 +15,7 @@ class NetworkManager: NSObject { let error = NSError(domain: "APIError", code: 100, userInfo: userInfoDictionary) return error }() - + static let unableToGenerateURL: NSError = { let userInfoDictionary: [String: Any] = [NSLocalizedDescriptionKey: "Unable to generate URL", NSLocalizedFailureReasonErrorKey: "There was a problem searching the location. Please try again later. If the problem continues please contact App Support."] diff --git a/Clocker/Overall App/Themer.swift b/Clocker/Overall App/Themer.swift index 0099db6..931810a 100644 --- a/Clocker/Overall App/Themer.swift +++ b/Clocker/Overall App/Themer.swift @@ -6,7 +6,7 @@ func isDarkModeOn() -> Bool { if #available(macOS 10.14, *) { return NSAppearance.current.name == NSAppearance.Name.darkAqua } - + return false } @@ -42,11 +42,11 @@ class Themer: NSObject { default: themeIndex = Theme.light } - + super.init() - + setAppAppearance() - + DistributedNotificationCenter.default.addObserver(self, selector: #selector(respondToInterfaceStyle), name: .interfaceStyleDidChange, @@ -74,17 +74,17 @@ extension Themer { default: themeIndex = Theme.light } - + setAppAppearance() } - + @objc func respondToInterfaceStyle() { OperationQueue.main.addOperation { self.setAppAppearance() } } - + private func setAppAppearance() { if #available(OSX 10.14, *) { var appAppearance = NSAppearance(named: .aqua) @@ -270,7 +270,7 @@ extension Themer { return themeIndex == .light ? NSImage(named: NSImage.Name("Extra"))! : NSImage(named: NSImage.Name("ExtraWhite"))! } - + func menubarOnboardingImage() -> NSImage { if #available(macOS 10.14, *) { switch themeIndex { @@ -280,7 +280,7 @@ extension Themer { return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSImage(named: NSImage.Name("Dark Menubar"))! : NSImage(named: NSImage.Name("Light Menubar"))! } } - + return UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSImage(named: NSImage.Name("Dark Menubar"))! : NSImage(named: NSImage.Name("Light Menubar"))! } @@ -315,7 +315,7 @@ extension Themer { } func currentLocationImage() -> NSImage { - + if #available(macOS 10.14, *) { switch themeIndex { case .light: @@ -326,7 +326,7 @@ extension Themer { return NSImage(named: NSImage.Name("CurrentLocationDynamic"))! } } - + return themeIndex == .light ? NSImage(named: NSImage.Name("CurrentLocation"))! : NSImage(named: NSImage.Name("CurrentLocationWhite"))! } @@ -365,7 +365,7 @@ extension Themer { } func privacyTabImage() -> NSImage { - + if #available(macOS 10.14, *) { switch themeIndex { case .light: @@ -376,12 +376,12 @@ extension Themer { return NSImage(named: .permissionTabIcon)! } } - + return themeIndex == .light ? NSImage(named: NSImage.Name("Privacy"))! : NSImage(named: NSImage.Name("Privacy Dark"))! } func appearanceTabImage() -> NSImage { - + if #available(macOS 10.14, *) { switch themeIndex { case .light: @@ -392,12 +392,12 @@ extension Themer { return NSImage(named: .appearanceTabIcon)! } } - + return themeIndex == .light ? NSImage(named: NSImage.Name("Appearance"))! : NSImage(named: NSImage.Name("Appearance Dark"))! } func calendarTabImage() -> NSImage { - + if #available(macOS 10.14, *) { switch themeIndex { case .light: @@ -408,7 +408,7 @@ extension Themer { return NSImage(named: .calendarTabIcon)! } } - + return themeIndex == .light ? NSImage(named: NSImage.Name("Calendar Tab Icon"))! : NSImage(named: NSImage.Name("Calendar Tab Dark"))! } diff --git a/Clocker/Panel/Data Layer/TimezoneData.swift b/Clocker/Panel/Data Layer/TimezoneData.swift index c0ee9cd..086fba3 100644 --- a/Clocker/Panel/Data Layer/TimezoneData.swift +++ b/Clocker/Panel/Data Layer/TimezoneData.swift @@ -12,7 +12,7 @@ struct DateFormat { // Non-class type cannot conform to NSCoding! class TimezoneData: NSObject, NSCoding { - + enum SelectionType: Int { case city case timezone @@ -28,7 +28,7 @@ class TimezoneData: NSObject, NSCoding { case twentyFourFormat case globalFormat } - + enum SecondsOverride: Int { case yes case no @@ -170,7 +170,7 @@ class TimezoneData: NSObject, NSCoding { let override = aDecoder.decodeInteger(forKey: "overrideFormat") overrideFormat = TimezoneOverride(rawValue: override)! - + let secondsOverride = aDecoder.decodeInteger(forKey: "secondsOverrideFormat") overrideSecondsFormat = SecondsOverride(rawValue: secondsOverride)! } @@ -189,20 +189,20 @@ class TimezoneData: NSObject, NSCoding { return nil } - + private class func logOldModelUsage() { guard let shortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String, let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String else { return } - + let operatingSystem = ProcessInfo.processInfo.operatingSystemVersion let osVersion = "\(operatingSystem.majorVersion).\(operatingSystem.minorVersion).\(operatingSystem.patchVersion)" let versionInfo = "Clocker \(shortVersion) (\(appVersion))" - + let feedbackInfo = [ AppFeedbackConstants.CLOperatingSystemVersion: osVersion, - AppFeedbackConstants.CLClockerVersion: versionInfo, + AppFeedbackConstants.CLClockerVersion: versionInfo ] Logger.log(object: feedbackInfo, for: "CLTimezoneData is still being used!") @@ -290,7 +290,7 @@ class TimezoneData: NSObject, NSCoding { aCoder.encode(isSystemTimezone, forKey: "isSystemTimezone") aCoder.encode(overrideFormat.rawValue, forKey: "overrideFormat") - + aCoder.encode(overrideSecondsFormat.rawValue, forKey: "secondsOverrideFormat") } @@ -335,7 +335,7 @@ class TimezoneData: NSObject, NSCoding { overrideFormat = .globalFormat } } - + func setShouldOverrideSecondsFormat(_ shouldOverride: Int) { if shouldOverride == 0 { overrideSecondsFormat = .yes @@ -361,7 +361,7 @@ class TimezoneData: NSObject, NSCoding { let errorDictionary = [ "Formatted Address": name, "Place Identifier": placeIdentifier, - "TimezoneID": timezoneIdentifier, + "TimezoneID": timezoneIdentifier ] Logger.log(object: errorDictionary, for: "Error fetching timezone() in TimezoneData") @@ -396,15 +396,15 @@ class TimezoneData: NSObject, NSCoding { return timeFormat } - + func shouldDisplayTwelveHourFormat() -> Bool { if overrideSecondsFormat == .globalFormat { return DataStore.shared().shouldDisplay(.twelveHour) } - + return overrideFormat == .twelveHourFormat } - + func shouldShowSeconds() -> Bool { if overrideSecondsFormat == .globalFormat { return DataStore.shared().shouldDisplay(.seconds) diff --git a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift index 6f0a537..cff33df 100644 --- a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift +++ b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift @@ -31,20 +31,20 @@ extension TimezoneDataOperations { return dateFormatter.string(from: newDate) } - + func compactMenuHeader() -> String { - + var subtitle = CLEmptyString - + let shouldDayBeShown = DataStore.shared().shouldShowDateInMenubar() - + if shouldDayBeShown { let substring = date(with: 0, displayType: CLDateDisplayType.menuDisplay) subtitle.append(substring) } - + subtitle.isEmpty ? subtitle.append(time(with: 0)) : subtitle.append(" \(time(with: 0))") - + return subtitle } @@ -99,26 +99,26 @@ extension TimezoneDataOperations { return menuTitle } - + private func timezoneDate(with sliderValue: Int, _ calendar: Calendar) -> Date { let source = timezoneDateByAdding(minutesToAdd: sliderValue, calendar) let sourceTimezone = TimeZone.current let destinationTimezone = TimeZone(identifier: dataObject.timezone()) - + let sourceGMTOffset: Double = Double(sourceTimezone.secondsFromGMT(for: source)) let destinationGMTOffset: Double = Double(destinationTimezone?.secondsFromGMT(for: source) ?? 0) let interval = destinationGMTOffset - sourceGMTOffset - + return Date(timeInterval: interval, since: source) } - + // calendar.dateByAdding takes a 0.1% or 0.2% according to TimeProfiler // Let's not use it unless neccesary! private func timezoneDateByAdding(minutesToAdd: Int, _ calendar: Calendar?) -> Date { if minutesToAdd == 0 { return Date() } - + return calendar?.date(byAdding: .minute, value: minutesToAdd, to: Date()) ?? Date() @@ -127,7 +127,7 @@ extension TimezoneDataOperations { func date(with sliderValue: Int, displayType: CLDateDisplayType) -> String { var currentCalendar = Calendar(identifier: .gregorian) currentCalendar.locale = Locale.autoupdatingCurrent - + let convertedDate = timezoneDate(with: sliderValue, currentCalendar) guard let relativeDayPreference = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber else { @@ -136,17 +136,17 @@ extension TimezoneDataOperations { } if displayType == CLDateDisplayType.panelDisplay { - + // Yesterday, tomorrow, etc if relativeDayPreference.intValue == 0 { - + let localFormatter = DateFormatterManager.localizedSimpleFormatter("EEEE") let local = localFormatter.date(from: localeDate(with: "EEEE")) - + // Gets local week day number and timezone's week day number for comparison let weekDay = currentCalendar.component(.weekday, from: local!) let timezoneWeekday = currentCalendar.component(.weekday, from: convertedDate) - + if weekDay == timezoneWeekday + 1 { return "Yesterday\(timeDifference())" } else if weekDay == timezoneWeekday { @@ -157,7 +157,7 @@ extension TimezoneDataOperations { return "\(weekdayText(from: convertedDate))\(timeDifference())" } } - + // Day name: Thursday, Friday etc if relativeDayPreference.intValue == 1 { return "\(weekdayText(from: convertedDate))\(timeDifference())" @@ -167,27 +167,27 @@ extension TimezoneDataOperations { if relativeDayPreference.intValue == 2 { return "\(todaysDate(with: sliderValue))\(timeDifference())" } - + let errorDictionary: [String: Any] = ["Timezone" : dataObject.timezone(), "Current Locale": Locale.autoupdatingCurrent.identifier, "Slider Value": sliderValue, "Today's Date": Date()] Logger.log(object: errorDictionary, for: "Unable to get date") - + return "Error" - + } else { return "\(shortWeekdayText(convertedDate))" } } - + // Returns shortened weekday given a date // For eg. Thu or Thursday, Tues for Tuesday etc private func shortWeekdayText(_ date: Date) -> String { let localizedFormatter = DateFormatterManager.localizedSimpleFormatter("E") return localizedFormatter.string(from: date) } - + // Returns proper weekday given a date // For eg. Thursday, Sunday, Friday etc private func weekdayText(from date: Date) -> String { @@ -207,12 +207,12 @@ extension TimezoneDataOperations { let unableToConvertDateParameters = [ "New Date": newDate, "Timezone": dataObject.timezone(), - "Locale": dateFormatter.locale.identifier, + "Locale": dateFormatter.locale.identifier ] as [String: Any] Logger.log(object: unableToConvertDateParameters, for: "Date conversion failure - New Date is nil") return CLEmptyString } - + let timeDifference = local.timeAgo(since: timezoneDate) if timeDifference.contains("Just now") { @@ -310,12 +310,12 @@ extension TimezoneDataOperations { return date } - + private func localDate() -> String { let dateFormatter = DateFormatterManager.dateFormatter(with: .medium, for: TimeZone.autoupdatingCurrent.identifier) return dateFormatter.string(from: Date()) } - + private func localeDate(with format: String) -> String { let dateFormatter = DateFormatterManager.localizedFormatter(with: format, for: TimeZone.autoupdatingCurrent.identifier) return dateFormatter.string(from: Date()) diff --git a/Clocker/Panel/Notes Popover/NotesPopover.swift b/Clocker/Panel/Notes Popover/NotesPopover.swift index fcc60ba..02731fa 100644 --- a/Clocker/Panel/Notes Popover/NotesPopover.swift +++ b/Clocker/Panel/Notes Popover/NotesPopover.swift @@ -3,12 +3,12 @@ import Cocoa class NotesPopover: NSViewController { - + private enum OverrideType { case timezoneFormat case seconds } - + var dataObject: TimezoneData? var timezoneObjects: [Data]? @@ -38,7 +38,7 @@ class NotesPopover: NSViewController { @IBOutlet var timeFormatControl: NSSegmentedControl! @IBOutlet weak var secondsFormatControl: NSSegmentedControl! - + @IBOutlet var notesTextView: TextViewWithPlaceholder! override func viewDidLoad() { @@ -62,7 +62,7 @@ class NotesPopover: NSViewController { "1 hour before", "2 hour before", "1 day before", - "2 days before", + "2 days before" ] alertPopupButton.removeAllItems() @@ -200,7 +200,7 @@ class NotesPopover: NSViewController { let attributesDictionary = [ NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(), - NSAttributedString.Key.paragraphStyle: style, + NSAttributedString.Key.paragraphStyle: style ] button.attributedTitle = NSAttributedString(string: title, @@ -268,7 +268,7 @@ class NotesPopover: NSViewController { updateTimezoneInDefaultPreferences(with: sender.selectedSegment, .timezoneFormat) updateMenubarTimezoneInDefaultPreferences(with: sender.selectedSegment, .timezoneFormat) refreshMainTableView() - + // Update the display if the chosen menubar mode is compact! if let delegate = NSApplication.shared.delegate as? AppDelegate { let handler = delegate.statusItemForPanel() @@ -288,24 +288,24 @@ class NotesPopover: NSViewController { private func updateMenubarTitles() { guard let model = dataObject, model.isFavourite == 1, var timezones = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] else { return } - + let menubarIndex = timezones.firstIndex { (menubarLocation) -> Bool in - + if let convertedObject = TimezoneData.customObject(from: menubarLocation) { return convertedObject == dataObject } - + return false } - + if let index = menubarIndex { - + let encodedObject = NSKeyedArchiver.archivedData(withRootObject: model) - + timezones[index] = encodedObject - + UserDefaults.standard.set(timezones, forKey: CLMenubarFavorites) - + } } @@ -452,19 +452,19 @@ class NotesPopover: NSViewController { updateContent() } } - + @IBAction func customizeSecondsFormat(_ sender: NSSegmentedControl) { updateTimezoneInDefaultPreferences(with: sender.selectedSegment, .seconds) updateMenubarTimezoneInDefaultPreferences(with: sender.selectedSegment, .seconds) refreshMainTableView() - + // Update the display if the chosen menubar mode is compact! if let delegate = NSApplication.shared.delegate as? AppDelegate { let handler = delegate.statusItemForPanel() handler.setupStatusItem() } } - + } @objc extension NotesPopover { @@ -513,7 +513,7 @@ class NotesPopover: NSViewController { setInitialReminderTime() updateTimeFormat() - + updateSecondsFormat() } @@ -526,7 +526,7 @@ class NotesPopover: NSViewController { timeFormatControl.setSelected(true, forSegment: 2) } } - + private func updateSecondsFormat() { if dataObject?.overrideSecondsFormat.rawValue == 0 { secondsFormatControl.setSelected(true, forSegment: 0) diff --git a/Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift b/Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift index 25cd54e..6bb3f04 100644 --- a/Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift +++ b/Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift @@ -18,7 +18,7 @@ class TextViewWithPlaceholder: NSTextView { if let placeHolderFont = NSFont(name: "Avenir", size: 14) { let textDict = [ NSAttributedString.Key.foregroundColor: NSColor.gray, - NSAttributedString.Key.font: placeHolderFont, + NSAttributedString.Key.font: placeHolderFont ] return NSAttributedString(string: " Add your notes here.", attributes: textDict) } diff --git a/Clocker/Panel/PanelController.swift b/Clocker/Panel/PanelController.swift index b09026e..df55d4f 100644 --- a/Clocker/Panel/PanelController.swift +++ b/Clocker/Panel/PanelController.swift @@ -42,20 +42,20 @@ class PanelController: ParentPanelController { @objc override func updateDefaultPreferences() { super.updateDefaultPreferences() } - + func setFrameTheNewWay(_ rect: NSRect, _ maxX: CGFloat) { // Calculate window's top left point. // First, center window under status item. let w = CGFloat(NSWidth((window?.frame)!)) - var x = CGFloat(roundf(Float(NSMidX(rect) - w / 2))) - let y = CGFloat(NSMinY(rect) - 2) + var x = CGFloat(roundf(Float(rect.midX - w / 2))) + let y = CGFloat(rect.minY - 2) let kMinimumSpaceBetweenWindowAndScreenEdge: CGFloat = 10 if x + w + kMinimumSpaceBetweenWindowAndScreenEdge > maxX { x = maxX - w - kMinimumSpaceBetweenWindowAndScreenEdge } - window?.setFrameTopLeftPoint(NSMakePoint(x, y)) + window?.setFrameTopLeftPoint(NSPoint(x: x, y: y)) window?.invalidateShadow() } @@ -99,20 +99,20 @@ class PanelController: ParentPanelController { // New way to set the panel's frame. // This takes into account the screen's dimensions. private func setPanelFrame() { - + guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return } - + var statusBackgroundWindow = appDelegate.statusItemForPanel().statusItem.view?.window var statusView = appDelegate.statusItemForPanel().statusItem.view - + // This below is a better way than actually checking if the menubar compact mode is set. if statusBackgroundWindow == nil || statusView == nil { statusBackgroundWindow = appDelegate.statusItemForPanel().statusItem.button?.window statusView = appDelegate.statusItemForPanel().statusItem.button } - + if let statusWindow = statusBackgroundWindow, let statusButton = statusView { var statusItemFrame = statusWindow.convertToScreen(statusButton.frame) @@ -175,16 +175,16 @@ class PanelController: ParentPanelController { "Show Upcoming Event View": showUpcomingEventView == "YES" ? "Yes" : "No", "Country": country, "Calendar Access Provided": EventCenter.sharedCenter().calendarAccessGranted() ? "Yes" : "No", - "Number of Timezones": preferences.count, + "Number of Timezones": preferences.count ] Logger.log(object: panelEvent, for: "openedPanel") } private func startWindowTimer() { - + stopMenubarTimerIfNeccesary() - + if let timer = parentTimer, timer.state == .paused { parentTimer?.start() return @@ -194,9 +194,9 @@ class PanelController: ParentPanelController { } private func startTimer() { - + print("Start timer called") - + parentTimer = Repeater(interval: .seconds(1), mode: .infinite) { _ in OperationQueue.main.addOperation { self.updateTime() @@ -205,14 +205,14 @@ class PanelController: ParentPanelController { parentTimer!.start() } - + private func stopMenubarTimerIfNeccesary() { let count = (DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data])?.count ?? 0 - + if count >= 1 || DataStore.shared().shouldDisplay(.showMeetingInMenubar) { if let delegate = NSApplication.shared.delegate as? AppDelegate { print("\nWe will be invalidating the menubar timer as we want the parent timer to take care of both panel and menubar ") - + delegate.invalidateMenubarTimer(false) } } diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index 6fc1f9b..ffa1899 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -27,7 +27,7 @@ class ParentPanelController: NSWindowController { var morePopover: NSPopover? var datasource: TimezoneDataSource? - + private lazy var feedbackWindow: AppFeedbackWindowController = AppFeedbackWindowController.shared() private var note: NotesPopover? @@ -71,7 +71,7 @@ class ParentPanelController: NSWindowController { @IBOutlet var calendarButton: NSButton! @IBOutlet var sliderDatePicker: NSDatePicker! - + @IBOutlet var debugVersionView: NSView! var defaultPreferences: [Data] { @@ -132,17 +132,17 @@ class ParentPanelController: NSWindowController { selector: #selector(timezoneGonnaChange), name: NSNotification.Name.NSSystemTimeZoneDidChange, object: nil) - + showDebugVersionViewIfNeccesary() } - + private func showDebugVersionViewIfNeccesary() { - + if debugVersionView != nil { debugVersionView.wantsLayer = true debugVersionView.layer?.backgroundColor = NSColor.systemRed.cgColor } - + #if RELEASE if debugVersionView != nil && stackView.arrangedSubviews.contains(debugVersionView) { stackView.removeView(debugVersionView) @@ -248,7 +248,7 @@ class ParentPanelController: NSWindowController { let styleAttributes = [ NSAttributedString.Key.paragraphStyle: paragraphStyle, - NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13) ?? NSFont.systemFont(ofSize: 13), + NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13) ?? NSFont.systemFont(ofSize: 13) ] let leftButtonAttributedTitle = NSAttributedString(string: leftButton.title, attributes: styleAttributes) @@ -562,13 +562,13 @@ class ParentPanelController: NSWindowController { target.image = Themer.shared().extraOptionsHighlightedImage() popover.animates = true - + if note == nil { note = NotesPopover(nibName: NSNib.Name.notesPopover, bundle: nil) popover.behavior = .applicationDefined popover.delegate = self } - + // Found a case where row number was 8 but we had only 2 timezones if correctRow >= defaults.count { correctRow = defaults.count - 1 @@ -812,7 +812,7 @@ class ParentPanelController: NSWindowController { let styleAttributes = [ NSAttributedString.Key.paragraphStyle: paragraphStyle, - NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)!, + NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)! ] leftButton.attributedTitle = NSAttributedString(string: "Not Really", attributes: styleAttributes) rightButton.attributedTitle = NSAttributedString(string: "Yes!", attributes: styleAttributes) @@ -842,7 +842,7 @@ class ParentPanelController: NSWindowController { let styleAttributes = [ NSAttributedString.Key.paragraphStyle: paragraphStyle, - NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)!, + NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)! ] if self.leftButton.attributedTitle.string == "Not Really" { diff --git a/Clocker/Panel/Rate Controller/RateController.swift b/Clocker/Panel/Rate Controller/RateController.swift index 81ae466..6f7b02d 100644 --- a/Clocker/Panel/Rate Controller/RateController.swift +++ b/Clocker/Panel/Rate Controller/RateController.swift @@ -3,53 +3,53 @@ import Cocoa final class RateController { - + private static var storage = UserDefaults.standard private static let version: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "N/A" private static var debugging = false - + private enum Keys { static let lastPrompt = "last-prompt" static let lastVersion = "last-version" static let install = "install" } - + class func applicationDidLaunch(_ defaults: UserDefaults = UserDefaults.standard) { if ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) { debugging = true } - + storage = defaults - + guard defaults.object(forKey: Keys.install) == nil else { return } defaults.set(Date(), forKey: Keys.install) } - + class func setPreviewMode(_ value: Bool) { debugging = value } - + class func prompted() { storage.set(Date(), forKey: Keys.lastPrompt) storage.set(version, forKey: Keys.lastVersion) } - + class func canPrompt() -> Bool { guard debugging == false else { return true } - + let day: TimeInterval = -1 * 60 * 60 * 24 let minInstall: TimeInterval = day * 7 - + // Check if the app has been installed for atleast 7 days guard let install = storage.object(forKey: Keys.install) as? Date, install.timeIntervalSinceNow < minInstall else { return false } - + // If we have never been prompted before, go ahead and prompt guard let lastPrompt = storage.object(forKey: Keys.lastPrompt) as? Date, let lastVersion = storage.object(forKey: Keys.lastVersion) as? String else { return true } - + // Minimum interval between two versions should be 45 let minInterval: TimeInterval = day * 45 @@ -58,7 +58,7 @@ final class RateController { // limit all types of prompts to at least 1mo intervals && lastPrompt.timeIntervalSinceNow < minInterval } - + class func prompt() { guard let ratingsURL = URL(string: AboutUsConstants.AppStoreLink) else { return diff --git a/Clocker/Panel/UI/PanelTableView.swift b/Clocker/Panel/UI/PanelTableView.swift index bcaf366..52a17ec 100644 --- a/Clocker/Panel/UI/PanelTableView.swift +++ b/Clocker/Panel/UI/PanelTableView.swift @@ -31,7 +31,7 @@ class PanelTableView: NSTableView { let options: NSTrackingArea.Options = [ .mouseMoved, .mouseEnteredAndExited, - .activeAlways, + .activeAlways ] let clipRect = enclosingScrollView?.contentView.bounds ?? .zero diff --git a/Clocker/Panel/UI/TimezoneCellView.swift b/Clocker/Panel/UI/TimezoneCellView.swift index 0af5456..44322ad 100644 --- a/Clocker/Panel/UI/TimezoneCellView.swift +++ b/Clocker/Panel/UI/TimezoneCellView.swift @@ -150,14 +150,14 @@ class TimezoneCellView: NSTableCellView { relativeTo: superview?.convert(bounds, to: nil) ?? relativeRect, andButton: sender) } - + Logger.log(object: [:], for: "Open Extra Options") } override func mouseDown(with _: NSEvent) { window?.endEditing(for: nil) } - + override func rightMouseDown(with event: NSEvent) { super.rightMouseDown(with: event) showExtraOptions(extraOptions) diff --git a/Clocker/Preferences/About/AboutViewController.swift b/Clocker/Preferences/About/AboutViewController.swift index e286dd5..af7a770 100644 --- a/Clocker/Preferences/About/AboutViewController.swift +++ b/Clocker/Preferences/About/AboutViewController.swift @@ -29,10 +29,10 @@ class AboutViewController: ParentViewController { let longVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "N/A" versionField.stringValue = "Clocker \(shortVersion) (\(longVersion))" - + setup() - - NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in + + NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in self.setup() } } @@ -49,12 +49,12 @@ class AboutViewController: ParentViewController { supportClocker, openSourceButton ] - + let localizedKeys = ["1. @n0shake on Twitter for quick comments", "2. For Private Feedback", "You can support Clocker by leaving a review on the App Store! :)", "Clocker is Open Source. You can check out the source code here."] - + zip(buttonsInOrder, localizedKeys).forEach { arg in let (button, title) = arg button?.title = title @@ -68,7 +68,7 @@ class AboutViewController: ParentViewController { private func setUnderline(for button: UnderlinedButton?, range: NSRange) { guard let underlinedButton = button else { return } - + let mutableParaghStyle = NSMutableParagraphStyle() mutableParaghStyle.alignment = .center @@ -78,13 +78,13 @@ class AboutViewController: ParentViewController { range: range) originalText.addAttribute(NSAttributedString.Key.foregroundColor, value: Themer.shared().mainTextColor(), - range: NSMakeRange(0, underlinedButton.attributedTitle.string.count)) + range: NSRange(location: 0, length: underlinedButton.attributedTitle.string.count)) originalText.addAttribute(NSAttributedString.Key.font, value: (button?.font)! , - range: NSMakeRange(0, underlinedButton.attributedTitle.string.count)) + range: NSRange(location: 0, length: underlinedButton.attributedTitle.string.count)) originalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: mutableParaghStyle , - range: NSMakeRange(0, underlinedButton.attributedTitle.string.count)) + range: NSRange(location: 0, length: underlinedButton.attributedTitle.string.count)) underlinedButton.attributedTitle = originalText } @@ -131,9 +131,9 @@ class AboutViewController: ParentViewController { let custom: [String: Any] = ["Country": countryCode] Logger.log(object: custom, for: "Opened GitHub") } - + @IBOutlet weak var feedbackLabel: NSTextField! - + private func setup() { feedbackLabel.stringValue = "Feedback is always welcome:" feedbackLabel.textColor = Themer.shared().mainTextColor() diff --git a/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift b/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift index 4186667..6132b46 100644 --- a/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift +++ b/Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift @@ -146,7 +146,7 @@ class AppFeedbackWindowController: NSWindowController { AppFeedbackConstants.CLAppFeedbackEmailProperty: email, AppFeedbackConstants.CLAppFeedbackFeedbackProperty: appFeedbackProperty, AppFeedbackConstants.CLOperatingSystemVersion: osVersion, - AppFeedbackConstants.CLClockerVersion: versionInfo, + AppFeedbackConstants.CLClockerVersion: versionInfo ] return feedbackInfo diff --git a/Clocker/Preferences/Appearance/AppearanceViewController.swift b/Clocker/Preferences/Appearance/AppearanceViewController.swift index 3d6c748..bb8d09e 100644 --- a/Clocker/Preferences/Appearance/AppearanceViewController.swift +++ b/Clocker/Preferences/Appearance/AppearanceViewController.swift @@ -12,7 +12,7 @@ class AppearanceViewController: ParentViewController { @IBOutlet weak var includeDayInMenubarControl: NSSegmentedControl! @IBOutlet weak var includeDateInMenubarControl: NSSegmentedControl! @IBOutlet weak var includePlaceNameControl: NSSegmentedControl! - + override func viewDidLoad() { super.viewDidLoad() @@ -31,16 +31,16 @@ class AppearanceViewController: ParentViewController { "6 days", "7 days" ]) - + setup() - NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in + NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in self.setup() self.animateBackgroundColorChange() self.view.needsDisplay = true // Let's make the color change permanent. } } - + private func animateBackgroundColorChange() { let colorAnimation = CABasicAnimation(keyPath: "backgroundColor") colorAnimation.duration = 0.25 @@ -60,18 +60,18 @@ class AppearanceViewController: ParentViewController { if let selectedIndex = DataStore.shared().retrieve(key: CLFutureSliderRange) as? NSNumber { sliderDayRangePopup.selectItem(at: selectedIndex.intValue) } - + if #available(macOS 10.14, *) { theme.setEnabled(true, forSegment: 2) } else { theme.setEnabled(false, forSegment: 2) } - + let shouldDisplayCompact = DataStore.shared().shouldDisplay(.menubarCompactMode) menubarMode.setSelected(true, forSegment: shouldDisplayCompact ? 0 : 1) updateMenubarControls(!shouldDisplayCompact) } - + @IBOutlet weak var headerLabel: NSTextField! @IBOutlet weak var timeFormatLabel: NSTextField! @IBOutlet weak var panelTheme: NSTextField! @@ -87,7 +87,7 @@ class AppearanceViewController: ParentViewController { @IBOutlet weak var menubarDisplayOptionsLabel: NSTextField! @IBOutlet weak var appDisplayLabel: NSTextField! @IBOutlet weak var menubarModeLabel: NSTextField! - + private func setup() { headerLabel.stringValue = "Main Panel Options" timeFormatLabel.stringValue = "Time Format" @@ -103,7 +103,7 @@ class AppearanceViewController: ParentViewController { includePlaceLabel.stringValue = "Include Place Name" menubarDisplayOptionsLabel.stringValue = "Menubar Display Options" menubarModeLabel.stringValue = "Menubar Mode" - + [headerLabel, timeFormatLabel, panelTheme, dayDisplayOptionsLabel, showSliderLabel, showSecondsLabel, showSunriseLabel, largerTextLabel, futureSliderRangeLabel, includeDayLabel, includeDateLabel, includePlaceLabel, menubarDisplayOptionsLabel, appDisplayLabel, menubarModeLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() } @@ -117,16 +117,16 @@ class AppearanceViewController: ParentViewController { Logger.log(object: ["Time Format": sender.selectedSegment == 0 ? "12 Hour Format" : "24 Hour Format"], for: "Time Format Selected") refresh(panel: true, floating: true) - + updateStatusItem() } - + private var previousBackgroundColor: NSColor = NSColor.white @IBAction func themeChanged(_ sender: NSSegmentedControl) { - + previousBackgroundColor = Themer.shared().mainBackgroundColor() - + Themer.shared().set(theme: sender.selectedSegment) refresh(panel: false, floating: true) @@ -194,9 +194,9 @@ class AppearanceViewController: ParentViewController { updateStatusItem() } - + @IBAction func changeAppDisplayOptions(_ sender: NSSegmentedControl) { - + if sender.selectedSegment == 0 { Logger.log(object: ["Selection": "Menubar"], for: "Dock Mode") NSApp.setActivationPolicy(.accessory) @@ -205,7 +205,6 @@ class AppearanceViewController: ParentViewController { NSApp.setActivationPolicy(.regular) } } - private func refresh(panel: Bool, floating: Bool) { OperationQueue.main.addOperation { @@ -234,54 +233,52 @@ class AppearanceViewController: ParentViewController { } } } - + @IBAction func displayDayInMenubarAction(_ sender: Any) { DataStore.shared().updateDayPreference() updateStatusItem() } - + @IBAction func displayDateInMenubarAction(_ sender: Any) { updateStatusItem() } - + @IBAction func displayPlaceInMenubarAction(_ sender: Any) { updateStatusItem() } - - + private func updateStatusItem() { guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else { return } - + if DataStore.shared().shouldDisplay(.menubarCompactMode) { statusItem.setupStatusItem() } else { statusItem.performTimerWork() } } - + @IBAction func menubarModeChanged(_ sender: NSSegmentedControl) { updateMenubarControls(sender.selectedSegment == 1) - + guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else { return } - + statusItem.setupStatusItem() - - + if sender.selectedSegment == 0 { Logger.log(object: ["Context": "In Appearance View"], for: "Switched to Compact Mode") } else { Logger.log(object: ["Context": "In Appearance View"], for: "Switched to Standard Mode") } - + } - + // We don't support showing day or date in the menubar for compact mode yet. // Disable those options to let the user know. private func updateMenubarControls(_ isEnabled: Bool) { - [includePlaceNameControl, includeDateInMenubarControl].forEach{ $0?.isEnabled = isEnabled } + [includePlaceNameControl, includeDateInMenubarControl].forEach { $0?.isEnabled = isEnabled } } } diff --git a/Clocker/Preferences/Calendar/CalendarViewController.swift b/Clocker/Preferences/Calendar/CalendarViewController.swift index d267d70..2d82c68 100644 --- a/Clocker/Preferences/Calendar/CalendarViewController.swift +++ b/Clocker/Preferences/Calendar/CalendarViewController.swift @@ -4,18 +4,18 @@ import Cocoa import EventKit class ClockerTextBackgroundView: NSView { - + override func awakeFromNib() { wantsLayer = true layer?.cornerRadius = 8.0 layer?.masksToBounds = false layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor - - NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in + + NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in self.layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor } } - + override func updateLayer() { super.updateLayer() layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor @@ -23,7 +23,7 @@ class ClockerTextBackgroundView: NSView { } class CalendarViewController: ParentViewController { - + @IBOutlet var showSegmentedControl: NSSegmentedControl! @IBOutlet var allDaysSegmentedControl: NSSegmentedControl! @IBOutlet var truncateTextField: NSTextField! @@ -31,35 +31,35 @@ class CalendarViewController: ParentViewController { @IBOutlet var informationField: NSTextField! @IBOutlet var grantAccessButton: NSButton! @IBOutlet weak var calendarsTableView: NSTableView! - + @IBOutlet weak var showNextMeetingInMenubarControl: NSSegmentedControl! @IBOutlet weak var backgroundView: NSView! @IBOutlet weak var nextMeetingBackgroundView: NSView! - + private lazy var calendars: [Any] = EventCenter.sharedCenter().fetchSourcesAndCalendars() override func viewDidLoad() { super.viewDidLoad() - + setup() - + NotificationCenter.default.addObserver(self, selector: #selector(calendarAccessStatusChanged), name: .calendarAccessGranted, object: nil) - - NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in + + NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in self.setup() } - + if #available(macOS 10.14, *) { noAccessView.material = .underWindowBackground } } - + @objc func calendarAccessStatusChanged() { verifyCalendarAccess() - + view.window?.windowController?.showWindow(nil) view.window?.makeKeyAndOrderFront(nil) } @@ -74,7 +74,7 @@ class CalendarViewController: ParentViewController { } else { showSegmentedControl.selectedSegment = 1 } - + // If the menubar mode is compact, we can't show meetings in the menubar. So disable toggling that option. showNextMeetingInMenubarControl.isEnabled = !(DataStore.shared().shouldDisplay(.menubarCompactMode)) } @@ -129,32 +129,32 @@ class CalendarViewController: ParentViewController { NSWorkspace.shared.launchApplication("System Preferences") } } - + @IBAction func showNextMeetingAction(_ sender: NSSegmentedControl) { - + // We need to start the menubar timer if it hasn't been started already guard let delegate = NSApplication.shared.delegate as? AppDelegate else { assertionFailure() return } - + let statusItemHandler = delegate.statusItemForPanel() - + if sender.selectedSegment == 0 { - + if let isValid = statusItemHandler.menubarTimer?.isValid, isValid == true { print("Timer is already in progress") updateStatusItem() return } - + } else { - + statusItemHandler.invalidateTimer(showIcon: true, isSyncing: false) } - + } - + @IBAction func showUpcomingEventView(_ sender: NSSegmentedControl) { var showUpcomingEventView = "YES" @@ -179,15 +179,15 @@ class CalendarViewController: ParentViewController { Logger.log(object: ["Show": "YES"], for: "Upcoming Event View") } } - + private func updateStatusItem() { guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else { return } - + statusItem.performTimerWork() } - + @IBOutlet weak var headerLabel: NSTextField! @IBOutlet weak var upcomingEventView: NSTextField! @IBOutlet weak var allDayMeetingsLabel: NSTextField! @@ -197,7 +197,7 @@ class CalendarViewController: ParentViewController { @IBOutlet weak var showEventsFromLabel: NSTextField! @IBOutlet weak var charactersField: NSTextField! @IBOutlet weak var truncateAccessoryLabel: NSTextField! - + private func setup() { // Grant access button's text color is taken care above. headerLabel.stringValue = "Upcoming Event View Options" @@ -208,7 +208,7 @@ class CalendarViewController: ParentViewController { charactersField.stringValue = "characters" showEventsFromLabel.stringValue = "Show events from" truncateAccessoryLabel.stringValue = "If meeting title is \"Meeting with Neel\" and truncate length is set to 5, text in menubar will appear as \"Meeti...\"" - + [headerLabel, upcomingEventView, allDayMeetingsLabel, showNextMeetingLabel, nextMeetingAccessoryLabel, truncateTextLabel, showEventsFromLabel, charactersField, truncateAccessoryLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() } } } @@ -222,28 +222,27 @@ extension CalendarViewController: NSTableViewDataSource { } extension CalendarViewController: NSTableViewDelegate { - + func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { return false } - + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { - + guard let currentSource = calendars[row] as? String, !currentSource.isEmpty else { return 30.0 } - + return 24.0 } - + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - + if let currentSource = calendars[row] as? String, let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell { message.sourceName.stringValue = currentSource return message } - - + if let currentSource = calendars[row] as? CalendarInfo, let calendarCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "calendarCellView"), owner: self) as? CalendarTableViewCell { calendarCell.calendarName.stringValue = currentSource.calendar.title calendarCell.calendarSelected.state = currentSource.selected ? NSControl.StateValue.on : NSControl.StateValue.off @@ -253,16 +252,16 @@ extension CalendarViewController: NSTableViewDelegate { calendarCell.calendarSelected.action = #selector(calendarSelected(_:)) return calendarCell } - + return nil - + } - + @objc func calendarSelected(_ checkbox: NSButton) { let currentSelection = checkbox.tag var sourcesAndCalendars = calendars - + if var calInfo = sourcesAndCalendars[currentSelection] as? CalendarInfo { calInfo.selected = (checkbox.state == .on) sourcesAndCalendars[currentSelection] = calInfo @@ -270,21 +269,21 @@ extension CalendarViewController: NSTableViewDelegate { updateSelectedCalendars(sourcesAndCalendars) } - + private func updateSelectedCalendars(_ selection: [Any]) { - + var selectedCalendars: [String] = [] - + for obj in selection { if let calInfo = obj as? CalendarInfo, calInfo.selected { selectedCalendars.append(calInfo.calendar.calendarIdentifier) } } - + UserDefaults.standard.set(selectedCalendars, forKey: CLSelectedCalendars) - + calendars = EventCenter.sharedCenter().fetchSourcesAndCalendars() - + EventCenter.sharedCenter().filterEvents() } } @@ -297,5 +296,3 @@ class CalendarTableViewCell: NSTableCellView { @IBOutlet var calendarName: NSTextField! @IBOutlet var calendarSelected: NSButton! } - - diff --git a/Clocker/Preferences/General/PreferencesViewController.swift b/Clocker/Preferences/General/PreferencesViewController.swift index f3df594..dcef69b 100644 --- a/Clocker/Preferences/General/PreferencesViewController.swift +++ b/Clocker/Preferences/General/PreferencesViewController.swift @@ -74,7 +74,7 @@ class PreferencesViewController: ParentViewController { @IBOutlet private var tableview: NSView! @IBOutlet private var additionalSortOptions: NSView! @IBOutlet weak var startAtLoginLabel: NSTextField! - + @IBOutlet var startupCheckbox: NSButton! @IBOutlet var headerLabel: NSTextField! @@ -198,7 +198,7 @@ class PreferencesViewController: ParentViewController { } UserDefaults.standard.set(archivedObjects, forKey: CLMenubarFavorites) - + // Update appereance if in compact menubar mode if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.setupMenubarTimer() @@ -240,7 +240,7 @@ class PreferencesViewController: ParentViewController { [timezoneNameSortButton, labelSortButton, timezoneSortButton].forEach { $0?.attributedTitle = NSAttributedString(string: $0?.title ?? CLEmptyString, attributes: [ NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(), - NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)!, + NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)! ]) } @@ -378,7 +378,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate } else if currentSelection == "Anywhere on Earth" { return "GMT+12" } - + return NSTimeZone(name: timezoneFilteredArray[row])?.abbreviation ?? "Error" } @@ -387,7 +387,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate if timezoneArray[row] == "UTC" { return "UTC" } - + if timezoneArray[row] == "Anywhere on Earth" { return "AoE" } @@ -410,7 +410,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate if selectedTimeZones.count > row { Logger.log(object: [ "Old Label": dataObject.customLabel ?? "Error", - "New Label": formattedValue, + "New Label": formattedValue ], for: "Custom Label Changed") @@ -423,7 +423,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate Logger.log(object: [ "MethodName": "SetObjectValue", "Selected Timezone Count": selectedTimeZones.count, - "Current Row": row, + "Current Row": row ], for: "Error in selected row count") } @@ -432,11 +432,11 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate insert(timezone: dataObject, at: row) if dataObject.isFavourite == 1, let menubarTitles = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data] { - + var mutableArray = menubarTitles let archivedObject = NSKeyedArchiver.archivedData(withRootObject: dataObject) mutableArray.append(archivedObject) - + UserDefaults.standard.set(mutableArray, forKey: CLMenubarFavorites) if dataObject.customLabel != nil { @@ -446,7 +446,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.setupMenubarTimer() } - + if mutableArray.count > 1 { showAlertIfMoreThanOneTimezoneHasBeenAddedToTheMenubar() } @@ -472,7 +472,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate if let appDelegate = NSApplication.shared.delegate as? AppDelegate, let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data], menubarFavourites.count <= 0, DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false { appDelegate.invalidateMenubarTimer(true) } - + if let appDelegate = NSApplication.shared.delegate as? AppDelegate { appDelegate.setupMenubarTimer() } @@ -487,43 +487,43 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate } private func showAlertIfMoreThanOneTimezoneHasBeenAddedToTheMenubar() { - + let isUITestRunning = ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) - + // If we have seen displayed the message before, abort! let haveWeSeenThisMessageBefore = UserDefaults.standard.bool(forKey: CLLongStatusBarWarningMessage) - + if haveWeSeenThisMessageBefore && !isUITestRunning { return } - + // If the user is already using the compact mode, abort. if DataStore.shared().shouldDisplay(.menubarCompactMode) && !isUITestRunning { return } - + // Time to display the alert. NSApplication.shared.activate(ignoringOtherApps: true) - + let alert = NSAlert() alert.showsSuppressionButton = true alert.messageText = "More than one location added to the menubar 😅" alert.informativeText = "Multiple timezones occupy space and if macOS determines Clocker is occupying too much space, it'll hide Clocker entirely! Enable Menubar Compact Mode to fit in more timezones in less space." alert.addButton(withTitle: "Enable Compact Mode") alert.addButton(withTitle: "Cancel") - + let response = alert.runModal() - + if response.rawValue == 1000 { OperationQueue.main.addOperation { UserDefaults.standard.set(0, forKey: CLMenubarCompactMode) - + if alert.suppressionButton?.state == NSControl.StateValue.on { UserDefaults.standard.set(true, forKey: CLLongStatusBarWarningMessage) } - + self.updateStatusBarAppearance() - + Logger.log(object: ["Context": ">1 Menubar Timezone in Preferences"], for: "Switched to Compact Mode") } } @@ -656,7 +656,7 @@ extension PreferencesViewController { self.dataTask = NetworkManager.task(with: urlString, completionHandler: { [weak self] response, error in - + guard let `self` = self else { return } OperationQueue.main.addOperation { @@ -696,7 +696,7 @@ extension PreferencesViewController { CLTimezoneName: formattedAddress, CLCustomLabel: formattedAddress, CLTimezoneID: CLEmptyString, - CLPlaceIdentifier: result.placeId, + CLPlaceIdentifier: result.placeId ] as [String: Any] self.filteredArray.append(TimezoneData(with: totalPackage)) @@ -765,7 +765,7 @@ extension PreferencesViewController { let urlString = "https://maps.googleapis.com/maps/api/timezone/json?location=\(tuple)×tamp=\(timeStamp)&key=\(CLGeocodingKey)" NetworkManager.task(with: urlString) { [weak self] response, error in - + guard let `self` = self else { return } OperationQueue.main.addOperation { @@ -793,7 +793,7 @@ extension PreferencesViewController { "latitude": dataObject.latitude!, "longitude": dataObject.longitude!, "nextUpdate": CLEmptyString, - CLCustomLabel: filteredAddress, + CLCustomLabel: filteredAddress ] as [String: Any] let timezoneObject = TimezoneData(with: newTimeZone) @@ -951,7 +951,7 @@ extension PreferencesViewController { } let currentSelection = timezoneFilteredArray[availableTimezoneTableView.selectedRow] - + let metaInfo = metadata(for: currentSelection) data.timezoneID = metaInfo.0 data.formattedAddress = metaInfo.1 @@ -992,7 +992,7 @@ extension PreferencesViewController { isActivityInProgress = false } } - + private func metadata(for selection: String) -> (String, String) { if selection == "Anywhere on Earth" { return ("GMT-1200", selection) @@ -1072,7 +1072,7 @@ extension PreferencesViewController { if selectedTimeZones.count == 0 { UserDefaults.standard.set(nil, forKey: CLMenubarFavorites) } - + updateStatusBarAppearance() updateStatusItem() @@ -1086,12 +1086,12 @@ extension PreferencesViewController { statusItem.performTimerWork() } - + private func updateStatusBarAppearance() { guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else { return } - + statusItem.setupStatusItem() } diff --git a/Clocker/Preferences/OneWindowController.swift b/Clocker/Preferences/OneWindowController.swift index f5984cc..c0eab66 100644 --- a/Clocker/Preferences/OneWindowController.swift +++ b/Clocker/Preferences/OneWindowController.swift @@ -3,35 +3,35 @@ import Cocoa class CenteredTabViewController: NSTabViewController { - + override func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { super.toolbarDefaultItemIdentifiers(toolbar) - + var toolbarItems: [NSToolbarItem.Identifier] = [NSToolbarItem.Identifier.flexibleSpace] - + tabViewItems.forEach { (item) in if let identifier = item.identifier as? String { toolbarItems.append(NSToolbarItem.Identifier.init(identifier)) } } - + toolbarItems.append(NSToolbarItem.Identifier.flexibleSpace) - + return toolbarItems } - + } class OneWindowController: NSWindowController { - + private static var sharedWindow: OneWindowController! - + override func windowDidLoad() { super.windowDidLoad() setup() - NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (notification) in - + NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in + NSAnimationContext.runAnimationGroup({ (context) in context.duration = 1 context.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeOut) @@ -41,17 +41,17 @@ class OneWindowController: NSWindowController { self.setupToolbarImages() } } - + private func setup() { setupWindow() setupToolbarImages() } - + private func setupWindow() { window?.titlebarAppearsTransparent = true window?.backgroundColor = Themer.shared().mainBackgroundColor() } - + class func shared() -> OneWindowController { if (sharedWindow == nil) { let prefStoryboard = NSStoryboard.init(name: "Preferences", bundle: nil) @@ -59,33 +59,33 @@ class OneWindowController: NSWindowController { } return sharedWindow } - + func openPermissions() { guard let window = window else { return } - + if !(window.isMainWindow) || !(window.isVisible) { showWindow(nil) } - + guard let tabViewController = contentViewController as? CenteredTabViewController else { return } - + tabViewController.selectedTabViewItemIndex = 3 } - + private func setupToolbarImages() { guard let tabViewController = contentViewController as? CenteredTabViewController else { return } - + let themer = Themer.shared() let identifierTOImageMapping: [String: NSImage] = ["Appearance": themer.appearanceTabImage(), "Calendar": themer.calendarTabImage(), "Permissions": themer.privacyTabImage()] - + tabViewController.tabViewItems.forEach { (tabViewItem) in let identity = (tabViewItem.identifier as? String) ?? "" if (identifierTOImageMapping[identity] != nil) { diff --git a/Clocker/Preferences/ParentViewController.swift b/Clocker/Preferences/ParentViewController.swift index 1808214..d36d942 100644 --- a/Clocker/Preferences/ParentViewController.swift +++ b/Clocker/Preferences/ParentViewController.swift @@ -8,8 +8,8 @@ class ParentViewController: NSViewController { if let view = view as? ParentView { view.wantsLayer = true } - - preferredContentSize = NSMakeSize(view.frame.size.width, view.frame.size.height) + + preferredContentSize = NSSize(width: view.frame.size.width, height: view.frame.size.height) } } diff --git a/Clocker/Preferences/Permissions/PermissionsViewController.swift b/Clocker/Preferences/Permissions/PermissionsViewController.swift index 6aebd85..0b0d808 100644 --- a/Clocker/Preferences/Permissions/PermissionsViewController.swift +++ b/Clocker/Preferences/Permissions/PermissionsViewController.swift @@ -3,7 +3,7 @@ import Cocoa class PermissionsViewController: ParentViewController { - + @IBOutlet var calendarContainerView: NSView! @IBOutlet var remindersContainerView: NSView! @@ -121,10 +121,10 @@ class PermissionsViewController: ParentViewController { if granted { OperationQueue.main.addOperation { - + self.view.window?.orderBack(nil) NSApp.activate(ignoringOtherApps: true) - + self.calendarButton.title = "Granted" // Used to update CalendarViewController's view @@ -155,10 +155,10 @@ class PermissionsViewController: ParentViewController { if granted { OperationQueue.main.addOperation { - + self.view.window?.orderBack(nil) NSApp.activate(ignoringOtherApps: true) - + self.remindersButton.title = "Granted" } } else { diff --git a/xcodebuild.log b/xcodebuild.log new file mode 100644 index 0000000..2f01f2b --- /dev/null +++ b/xcodebuild.log @@ -0,0 +1,533 @@ +note: Using new build system +note: Planning build +note: Constructing build description +ProcessInfoPlistFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework/Versions/A/Resources/Info.plist /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master/Library/Info.plist (in target: ShortcutRecorder.framework) + cd /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master + builtin-infoPlistUtility /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master/Library/Info.plist -producttype com.apple.product-type.framework -expandbuildsettings -platform macosx -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework/Versions/A/Resources/Info.plist + +GenerateDSYMFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework.dSYM /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework/Versions/A/ShortcutRecorder (in target: ShortcutRecorder.framework) + cd /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master + /Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework/Versions/A/ShortcutRecorder -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework.dSYM + +ProcessProductPackaging "" /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/ClockerHelper.build/ClockerHelper.app.xcent (in target: ClockerHelper) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + + +Entitlements: + +{ + "com.apple.security.app-sandbox" = 1; + "com.apple.security.get-task-allow" = 1; +} + + + builtin-productPackagingUtility -entitlements -format xml -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/ClockerHelper.build/ClockerHelper.app.xcent + +ProcessInfoPlistFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app/Contents/Info.plist /Users/ban/Documents/GitHub/Clocker/Clocker/ClockerHelper/Info.plist (in target: ClockerHelper) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-infoPlistUtility /Users/ban/Documents/GitHub/Clocker/Clocker/ClockerHelper/Info.plist -producttype com.apple.product-type.application -genpkginfo /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app/Contents/PkgInfo -expandbuildsettings -platform macosx -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/ClockerHelper.build/Base.lproj/Main-SBPartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/ClockerHelper.build/assetcatalog_generated_info.plist -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app/Contents/Info.plist + +CodeSign /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app (in target: ClockerHelper) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + export CODESIGN_ALLOCATE=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate + +Signing Identity: "-" + + /usr/bin/codesign --force --sign - --entitlements /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/ClockerHelper.build/ClockerHelper.app.xcent --timestamp=none /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app +/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app: replacing existing signature + +ProcessInfoPlistFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework/Versions/A/Resources/Info.plist /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master/PTHotKey/Info.plist (in target: PTHotKey.framework) + cd /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master + builtin-infoPlistUtility /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master/PTHotKey/Info.plist -producttype com.apple.product-type.framework -expandbuildsettings -platform macosx -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework/Versions/A/Resources/Info.plist + +GenerateDSYMFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework.dSYM /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework/Versions/A/PTHotKey (in target: PTHotKey.framework) + cd /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/ShortcutRecorder-master + /Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework/Versions/A/PTHotKey -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework.dSYM + +ProcessProductPackaging "" /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Clocker.app.xcent (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + + +Entitlements: + +{ + "com.apple.security.app-sandbox" = 1; + "com.apple.security.get-task-allow" = 1; + "com.apple.security.network.client" = 1; + "com.apple.security.personal-information.calendars" = 1; + "com.apple.security.temporary-exception.apple-events" = ( + "com.apple.reminders" + ); +} + + + builtin-productPackagingUtility -entitlements -format xml -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Clocker.app.xcent + +PhaseScriptExecution Run\ Script /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Script-9A4379201BEC220200F4E27F.sh (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + /bin/sh -c /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Script-9A4379201BEC220200F4E27F.sh + +PBXCp /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Library/LoginItems/ClockerHelper.app (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -exclude Headers -exclude PrivateHeaders -exclude Modules -exclude \*.tbd -resolve-src-symlinks /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ClockerHelper.app /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Library/LoginItems + +ProcessInfoPlistFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Info.plist /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/Clocker-Info.plist (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-infoPlistUtility /Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/Clocker-Info.plist -producttype com.apple.product-type.application -genpkginfo /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/PkgInfo -expandbuildsettings -platform macosx -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/AppFeedbackWindow-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Preferences-SBPartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/en.lproj/Panel-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/FloatingWindow-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/WelcomeView-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Onboarding-SBPartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/NotesPopover-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/MainMenu-PartialInfo.plist -additionalcontentfile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/assetcatalog_generated_info.plist -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Info.plist + +GenerateDSYMFile /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app.dSYM /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/MacOS/Clocker (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + /Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/MacOS/Clocker -o /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app.dSYM + +ValidateEmbeddedBinary /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Library/LoginItems/ClockerHelper.app (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-embeddedBinaryValidationUtility /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Library/LoginItems/ClockerHelper.app -signing-cert - -info-plist-path /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Info.plist + +PBXCp /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/ShortcutRecorder.framework (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -exclude Headers -exclude PrivateHeaders -exclude Modules -exclude \*.tbd -resolve-src-symlinks /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/ShortcutRecorder.framework /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks + +PBXCp /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/PTHotKey.framework (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + builtin-copy -exclude .DS_Store -exclude CVS -exclude .svn -exclude .git -exclude .hg -exclude Headers -exclude PrivateHeaders -exclude Modules -exclude \*.tbd -resolve-src-symlinks /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/PTHotKey.framework /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks + +CodeSign /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/PTHotKey.framework/Versions/A (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + export CODESIGN_ALLOCATE=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate + +Signing Identity: "-" + + /usr/bin/codesign --force --sign - --timestamp=none --preserve-metadata=identifier,entitlements,flags /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/PTHotKey.framework/Versions/A + +CodeSign /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/ShortcutRecorder.framework/Versions/A (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + export CODESIGN_ALLOCATE=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate + +Signing Identity: "-" + + /usr/bin/codesign --force --sign - --timestamp=none --preserve-metadata=identifier,entitlements,flags /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Frameworks/ShortcutRecorder.framework/Versions/A + +PhaseScriptExecution Move\ .app\ to\ Applications /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Script-C2A632A020EAC5EE00EB6BEA.sh (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + export ACTION=build + export ALTERNATE_GROUP=1876110778 + export ALTERNATE_MODE=u+w,go-w,a+rX + export ALTERNATE_OWNER=ban + export ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO + export ALWAYS_SEARCH_USER_PATHS=NO + export ALWAYS_USE_SEPARATE_HEADERMAPS=NO + export APPLE_INTERNAL_DEVELOPER_DIR=/AppleInternal/Developer + export APPLE_INTERNAL_DIR=/AppleInternal + export APPLE_INTERNAL_DOCUMENTATION_DIR=/AppleInternal/Documentation + export APPLE_INTERNAL_LIBRARY_DIR=/AppleInternal/Library + export APPLE_INTERNAL_TOOLS=/AppleInternal/Developer/Tools + export APPLICATION_EXTENSION_API_ONLY=NO + export APPLY_RULES_IN_COPY_FILES=NO + export ARCHS=x86_64 + export ARCHS_STANDARD=x86_64 + export ARCHS_STANDARD_32_64_BIT="x86_64 i386" + export ARCHS_STANDARD_32_BIT=i386 + export ARCHS_STANDARD_64_BIT=x86_64 + export ARCHS_STANDARD_INCLUDING_64_BIT=x86_64 + export ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon + export AVAILABLE_PLATFORMS="appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator" + export BITCODE_GENERATION_MODE=marker + export BUILD_ACTIVE_RESOURCES_ONLY=NO + export BUILD_COMPONENTS="headers build" + export BUILD_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products + export BUILD_ROOT=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products + export BUILD_STYLE= + export BUILD_VARIANTS=normal + export BUILT_PRODUCTS_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug + export CACHE_ROOT=/var/folders/r7/3v42n4q52fsft8jwnm5htq3s0wgch_/C/com.apple.DeveloperTools/10.2-10E125/Xcode + export CCHROOT=/var/folders/r7/3v42n4q52fsft8jwnm5htq3s0wgch_/C/com.apple.DeveloperTools/10.2-10E125/Xcode + export CHMOD=/bin/chmod + export CHOWN=/usr/sbin/chown + export CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED=YES + export CLANG_COVERAGE_MAPPING=YES + export CLANG_ENABLE_MODULES=YES + export CLANG_ENABLE_OBJC_ARC=YES + export CLANG_MODULES_BUILD_SESSION_FILE=/Users/ban/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation + export CLANG_PROFILE_DATA_DIRECTORY=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/ProfileData + export CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING=YES + export CLANG_WARN_BOOL_CONVERSION=YES + export CLANG_WARN_COMMA=YES + export CLANG_WARN_CONSTANT_CONVERSION=YES + export CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS=YES + export CLANG_WARN_EMPTY_BODY=YES + export CLANG_WARN_ENUM_CONVERSION=YES + export CLANG_WARN_INFINITE_RECURSION=YES + export CLANG_WARN_INT_CONVERSION=YES + export CLANG_WARN_NON_LITERAL_NULL_CONVERSION=YES + export CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF=YES + export CLANG_WARN_OBJC_LITERAL_CONVERSION=YES + export CLANG_WARN_RANGE_LOOP_ANALYSIS=YES + export CLANG_WARN_STRICT_PROTOTYPES=YES + export CLANG_WARN_SUSPICIOUS_MOVE=YES + export CLANG_WARN_UNREACHABLE_CODE=YES + export CLANG_WARN__DUPLICATE_METHOD_MATCH=YES + export CLASS_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/JavaClasses + export CLEAN_PRECOMPS=YES + export CLONE_HEADERS=NO + export CODESIGNING_FOLDER_PATH=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app + export CODE_SIGNING_ALLOWED=YES + export CODE_SIGN_ENTITLEMENTS=Clocker/Clocker.entitlements + export CODE_SIGN_IDENTITY=- + export CODE_SIGN_INJECT_BASE_ENTITLEMENTS=YES + export CODE_SIGN_STYLE=Manual + export COLOR_DIAGNOSTICS=NO + export COMBINE_HIDPI_IMAGES=YES + export COMPILER_INDEX_STORE_ENABLE=Default + export COMPOSITE_SDK_DIRS=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/CompositeSDKs + export COMPRESS_PNG_FILES=NO + export CONFIGURATION=Debug + export CONFIGURATION_BUILD_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug + export CONFIGURATION_TEMP_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug + export CONTENTS_FOLDER_PATH=Clocker.app/Contents + export COPYING_PRESERVES_HFS_DATA=NO + export COPY_HEADERS_RUN_UNIFDEF=NO + export COPY_PHASE_STRIP=NO + export COPY_RESOURCES_FROM_STATIC_FRAMEWORKS=YES + export CP=/bin/cp + export CREATE_INFOPLIST_SECTION_IN_BINARY=NO + export CURRENT_ARCH=undefined_arch + export CURRENT_VARIANT=normal + export DEAD_CODE_STRIPPING=NO + export DEBUGGING_SYMBOLS=YES + export DEBUG_INFORMATION_FORMAT=dwarf-with-dsym + export DEFAULT_COMPILER=com.apple.compilers.llvm.clang.1_0 + export DEFAULT_KEXT_INSTALL_PATH=/System/Library/Extensions + export DEFINES_MODULE=YES + export DEPLOYMENT_LOCATION=NO + export DEPLOYMENT_POSTPROCESSING=NO + export DEPLOYMENT_TARGET_CLANG_ENV_NAME=MACOSX_DEPLOYMENT_TARGET + export DEPLOYMENT_TARGET_CLANG_FLAG_NAME=mmacosx-version-min + export DEPLOYMENT_TARGET_LD_ENV_NAME=MACOSX_DEPLOYMENT_TARGET + export DEPLOYMENT_TARGET_LD_FLAG_NAME=macosx_version_min + export DEPLOYMENT_TARGET_SETTING_NAME=MACOSX_DEPLOYMENT_TARGET + export DEPLOYMENT_TARGET_SUGGESTED_VALUES="10.6 10.7 10.8 10.9 10.10 10.11 10.12 10.13 10.14" + export DERIVED_FILES_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/DerivedSources + export DERIVED_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/DerivedSources + export DERIVED_SOURCES_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/DerivedSources + export DEVELOPER_APPLICATIONS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications + export DEVELOPER_BIN_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr/bin + export DEVELOPER_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer + export DEVELOPER_FRAMEWORKS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Library/Frameworks + export DEVELOPER_FRAMEWORKS_DIR_QUOTED=/Applications/Xcode_10.2_fb.app/Contents/Developer/Library/Frameworks + export DEVELOPER_LIBRARY_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Library + export DEVELOPER_SDK_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs + export DEVELOPER_TOOLS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Tools + export DEVELOPER_USR_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr + export DEVELOPMENT_LANGUAGE=English + export DOCUMENTATION_FOLDER_PATH=Clocker.app/Contents/Resources/English.lproj/Documentation + export DO_HEADER_SCANNING_IN_JAM=NO + export DSTROOT=/tmp/Clocker.dst + export DT_TOOLCHAIN_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain + export DWARF_DSYM_FILE_NAME=Clocker.app.dSYM + export DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT=NO + export DWARF_DSYM_FOLDER_PATH=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug + export EMBEDDED_CONTENT_CONTAINS_SWIFT=NO + export EMBEDDED_PROFILE_NAME=embedded.provisionprofile + export EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLE=NO + export ENABLE_BITCODE=NO + export ENABLE_DEFAULT_HEADER_SEARCH_PATHS=YES + export ENABLE_HEADER_DEPENDENCIES=YES + export ENABLE_ON_DEMAND_RESOURCES=NO + export ENABLE_STRICT_OBJC_MSGSEND=YES + export ENABLE_TESTABILITY=YES + export EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS=".DS_Store .svn .git .hg CVS" + export EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES="*.nib *.lproj *.framework *.gch *.xcode* *.xcassets (*) .DS_Store CVS .svn .git .hg *.pbproj *.pbxproj" + export EXECUTABLES_FOLDER_PATH=Clocker.app/Contents/Executables + export EXECUTABLE_FOLDER_PATH=Clocker.app/Contents/MacOS + export EXECUTABLE_NAME=Clocker + export EXECUTABLE_PATH=Clocker.app/Contents/MacOS/Clocker + export EXPANDED_CODE_SIGN_IDENTITY=- + export EXPANDED_CODE_SIGN_IDENTITY_NAME=- + export FILE_LIST=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects/LinkFileList + export FIXED_FILES_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/FixedFiles + export FRAMEWORKS_FOLDER_PATH=Clocker.app/Contents/Frameworks + export FRAMEWORK_FLAG_PREFIX=-framework + export FRAMEWORK_SEARCH_PATHS="/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug /Users/ban/Documents/GitHub/Clocker/Clocker /Users/ban/Documents/GitHub/Clocker/Clocker/Frameworks" + export FRAMEWORK_VERSION=A + export FULL_PRODUCT_NAME=Clocker.app + export GCC3_VERSION=3.3 + export GCC_C_LANGUAGE_STANDARD=gnu99 + export GCC_DYNAMIC_NO_PIC=NO + export GCC_ENABLE_OBJC_EXCEPTIONS=YES + export GCC_INLINES_ARE_PRIVATE_EXTERN=YES + export GCC_NO_COMMON_BLOCKS=YES + export GCC_OPTIMIZATION_LEVEL=0 + export GCC_PFE_FILE_C_DIALECTS="c objective-c c++ objective-c++" + export GCC_PRECOMPILE_PREFIX_HEADER=YES + export GCC_PREFIX_HEADER=Clocker/Clocker-Prefix.pch + export GCC_PREPROCESSOR_DEFINITIONS=DEBUG + export GCC_SYMBOLS_PRIVATE_EXTERN=NO + export GCC_TREAT_WARNINGS_AS_ERRORS=NO + export GCC_VERSION=com.apple.compilers.llvm.clang.1_0 + export GCC_VERSION_IDENTIFIER=com_apple_compilers_llvm_clang_1_0 + export GCC_WARN_64_TO_32_BIT_CONVERSION=YES + export GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS=YES + export GCC_WARN_ABOUT_MISSING_PROTOTYPES=YES + export GCC_WARN_ABOUT_RETURN_TYPE=YES + export GCC_WARN_UNDECLARED_SELECTOR=YES + export GCC_WARN_UNINITIALIZED_AUTOS=YES + export GCC_WARN_UNUSED_FUNCTION=YES + export GCC_WARN_UNUSED_LABEL=YES + export GCC_WARN_UNUSED_PARAMETER=YES + export GCC_WARN_UNUSED_VARIABLE=YES + export GENERATE_MASTER_OBJECT_FILE=NO + export GENERATE_PKGINFO_FILE=YES + export GENERATE_PROFILING_CODE=NO + export GENERATE_TEXT_BASED_STUBS=NO + export GID=1876110778 + export GROUP=1876110778 + export HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT=YES + export HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES=YES + export HEADERMAP_INCLUDES_NONPUBLIC_NONPRIVATE_HEADERS=YES + export HEADERMAP_INCLUDES_PROJECT_HEADERS=YES + export HEADERMAP_USES_FRAMEWORK_PREFIX_ENTRIES=YES + export HEADERMAP_USES_VFS=YES + export HEADER_SEARCH_PATHS="/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/include " + export HIDE_BITCODE_SYMBOLS=YES + export HOME=/Users/ban + export ICONV=/usr/bin/iconv + export INFOPLIST_EXPAND_BUILD_SETTINGS=YES + export INFOPLIST_FILE=Clocker/Clocker-Info.plist + export INFOPLIST_OUTPUT_FORMAT=same-as-input + export INFOPLIST_PATH=Clocker.app/Contents/Info.plist + export INFOPLIST_PREPROCESS=NO + export INFOSTRINGS_PATH=Clocker.app/Contents/Resources/English.lproj/InfoPlist.strings + export INLINE_PRIVATE_FRAMEWORKS=NO + export INSTALLHDRS_COPY_PHASE=NO + export INSTALLHDRS_SCRIPT_PHASE=NO + export INSTALL_DIR=/tmp/Clocker.dst/Applications + export INSTALL_GROUP=1876110778 + export INSTALL_MODE_FLAG=u+w,go-w,a+rX + export INSTALL_OWNER=ban + export INSTALL_PATH=/Applications + export INSTALL_ROOT=/tmp/Clocker.dst + export JAVAC_DEFAULT_FLAGS="-J-Xms64m -J-XX:NewSize=4M -J-Dfile.encoding=UTF8" + export JAVA_APP_STUB=/System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub + export JAVA_ARCHIVE_CLASSES=YES + export JAVA_ARCHIVE_TYPE=JAR + export JAVA_COMPILER=/usr/bin/javac + export JAVA_FOLDER_PATH=Clocker.app/Contents/Resources/Java + export JAVA_FRAMEWORK_RESOURCES_DIRS=Resources + export JAVA_JAR_FLAGS=cv + export JAVA_SOURCE_SUBDIR=. + export JAVA_USE_DEPENDENCIES=YES + export JAVA_ZIP_FLAGS=-urg + export JIKES_DEFAULT_FLAGS="+E +OLDCSO" + export KASAN_DEFAULT_CFLAGS="-DKASAN=1 -fsanitize=address -mllvm -asan-globals-live-support -mllvm -asan-force-dynamic-shadow" + export KEEP_PRIVATE_EXTERNS=NO + export LD_DEPENDENCY_INFO_FILE=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects-normal/undefined_arch/Clocker_dependency_info.dat + export LD_GENERATE_MAP_FILE=NO + export LD_MAP_FILE_PATH=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Clocker-LinkMap-normal-undefined_arch.txt + export LD_NO_PIE=NO + export LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVER=YES + export LD_RUNPATH_SEARCH_PATHS=" @executable_path/../Frameworks" + export LEGACY_DEVELOPER_DIR=/Applications/Xcode_10.2_fb.app/Contents/PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer + export LEX=lex + export LIBRARY_FLAG_NOSPACE=YES + export LIBRARY_FLAG_PREFIX=-l + export LIBRARY_KEXT_INSTALL_PATH=/Library/Extensions + export LIBRARY_SEARCH_PATHS="/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug " + export LINKER_DISPLAYS_MANGLED_NAMES=NO + export LINK_FILE_LIST_normal_x86_64=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects-normal/x86_64/Clocker.LinkFileList + export LINK_WITH_STANDARD_LIBRARIES=YES + export LOCALIZED_RESOURCES_FOLDER_PATH=Clocker.app/Contents/Resources/English.lproj + export LOCALIZED_STRING_MACRO_NAMES="NSLocalizedString CFLocalizedString" + export LOCAL_ADMIN_APPS_DIR=/Applications/Utilities + export LOCAL_APPS_DIR=/Applications + export LOCAL_DEVELOPER_DIR=/Library/Developer + export LOCAL_LIBRARY_DIR=/Library + export LOCROOT=/Users/ban/Documents/GitHub/Clocker/Clocker + export LOCSYMROOT=/Users/ban/Documents/GitHub/Clocker/Clocker + export MACH_O_TYPE=mh_execute + export MACOSX_DEPLOYMENT_TARGET=10.12 + export MAC_OS_X_PRODUCT_BUILD_VERSION=18E226 + export MAC_OS_X_VERSION_ACTUAL=101404 + export MAC_OS_X_VERSION_MAJOR=101400 + export MAC_OS_X_VERSION_MINOR=1404 + export METAL_LIBRARY_FILE_BASE=default + export METAL_LIBRARY_OUTPUT_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app/Contents/Resources + export MODULE_CACHE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/ModuleCache.noindex + export NATIVE_ARCH=i386 + export NATIVE_ARCH_32_BIT=i386 + export NATIVE_ARCH_64_BIT=x86_64 + export NATIVE_ARCH_ACTUAL=x86_64 + export NO_COMMON=YES + export OBJECT_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects + export OBJECT_FILE_DIR_normal=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects-normal + export OBJROOT=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex + export ONLY_ACTIVE_ARCH=NO + export OS=MACOS + export OSAC=/usr/bin/osacompile + export OTHER_SWIFT_FLAGS="-D DEBUG" + export PACKAGE_TYPE=com.apple.package-type.wrapper.application + export PASCAL_STRINGS=YES + export PATH=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/libexec:/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/usr/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/usr/local/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/usr/bin:/Applications/Xcode_10.2_fb.app/Contents/Developer/usr/local/bin:/opt/facebook/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/facebook/bin/biggrep:/opt/facebook/nuclide/latest/nuclide/pkg/fb-on-demand-cli/bin:/opt/facebook/ops/scripts/chef:/opt/homebrew/bin:/usr/local/munki:/opt/facebook/hg/bin + export PATH_PREFIXES_EXCLUDED_FROM_HEADER_DEPENDENCIES="/usr/include /usr/local/include /System/Library/Frameworks /System/Library/PrivateFrameworks /Applications/Xcode_10.2_fb.app/Contents/Developer/Headers /Applications/Xcode_10.2_fb.app/Contents/Developer/SDKs /Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms" + export PBDEVELOPMENTPLIST_PATH=Clocker.app/Contents/pbdevelopment.plist + export PER_ARCH_OBJECT_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects-normal/undefined_arch + export PER_VARIANT_OBJECT_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Objects-normal + export PKGINFO_FILE_PATH=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/PkgInfo + export PKGINFO_PATH=Clocker.app/Contents/PkgInfo + export PLATFORM_DEVELOPER_APPLICATIONS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications + export PLATFORM_DEVELOPER_BIN_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr/bin + export PLATFORM_DEVELOPER_LIBRARY_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Library + export PLATFORM_DEVELOPER_SDK_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs + export PLATFORM_DEVELOPER_TOOLS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Tools + export PLATFORM_DEVELOPER_USR_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr + export PLATFORM_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform + export PLATFORM_DISPLAY_NAME=macOS + export PLATFORM_NAME=macosx + export PLATFORM_PREFERRED_ARCH=x86_64 + export PLATFORM_PRODUCT_BUILD_VERSION=10E125 + export PLIST_FILE_OUTPUT_FORMAT=same-as-input + export PLUGINS_FOLDER_PATH=Clocker.app/Contents/PlugIns + export PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR=YES + export PRECOMP_DESTINATION_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/PrefixHeaders + export PRESERVE_DEAD_CODE_INITS_AND_TERMS=NO + export PRIVATE_HEADERS_FOLDER_PATH=Clocker.app/Contents/PrivateHeaders + export PRODUCT_BUNDLE_IDENTIFIER=com.abhishek.Clocker + export PRODUCT_MODULE_NAME=Clocker + export PRODUCT_NAME=Clocker + export PRODUCT_SETTINGS_PATH=/Users/ban/Documents/GitHub/Clocker/Clocker/Clocker/Clocker-Info.plist + export PRODUCT_TYPE=com.apple.product-type.application + export PROFILING_CODE=NO + export PROJECT=Clocker + export PROJECT_DERIVED_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/DerivedSources + export PROJECT_DIR=/Users/ban/Documents/GitHub/Clocker/Clocker + export PROJECT_FILE_PATH=/Users/ban/Documents/GitHub/Clocker/Clocker/Clocker.xcodeproj + export PROJECT_NAME=Clocker + export PROJECT_TEMP_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build + export PROJECT_TEMP_ROOT=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex + export PUBLIC_HEADERS_FOLDER_PATH=Clocker.app/Contents/Headers + export RECURSIVE_SEARCH_PATHS_FOLLOW_SYMLINKS=YES + export REMOVE_CVS_FROM_RESOURCES=YES + export REMOVE_GIT_FROM_RESOURCES=YES + export REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES=YES + export REMOVE_HG_FROM_RESOURCES=YES + export REMOVE_SVN_FROM_RESOURCES=YES + export REZ_COLLECTOR_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/ResourceManagerResources + export REZ_OBJECTS_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/ResourceManagerResources/Objects + export REZ_SEARCH_PATHS="/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug " + export SCAN_ALL_SOURCE_FILES_FOR_INCLUDES=NO + export SCRIPTS_FOLDER_PATH=Clocker.app/Contents/Resources/Scripts + export SCRIPT_INPUT_FILE_COUNT=0 + export SCRIPT_INPUT_FILE_LIST_COUNT=0 + export SCRIPT_OUTPUT_FILE_COUNT=0 + export SCRIPT_OUTPUT_FILE_LIST_COUNT=0 + export SDKROOT=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk + export SDK_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk + export SDK_DIR_macosx10_14=/Applications/Xcode_10.2_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk + export SDK_NAME=macosx10.14 + export SDK_NAMES=macosx10.14 + export SDK_PRODUCT_BUILD_VERSION=18E219 + export SDK_VERSION=10.14 + export SDK_VERSION_ACTUAL=101400 + export SDK_VERSION_MAJOR=101400 + export SDK_VERSION_MINOR=1400 + export SED=/usr/bin/sed + export SEPARATE_STRIP=NO + export SEPARATE_SYMBOL_EDIT=NO + export SET_DIR_MODE_OWNER_GROUP=YES + export SET_FILE_MODE_OWNER_GROUP=NO + export SHALLOW_BUNDLE=NO + export SHARED_DERIVED_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/DerivedSources + export SHARED_FRAMEWORKS_FOLDER_PATH=Clocker.app/Contents/SharedFrameworks + export SHARED_PRECOMPS_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/PrecompiledHeaders + export SHARED_SUPPORT_FOLDER_PATH=Clocker.app/Contents/SharedSupport + export SKIP_INSTALL=NO + export SOURCE_ROOT=/Users/ban/Documents/GitHub/Clocker/Clocker + export SRCROOT=/Users/ban/Documents/GitHub/Clocker/Clocker + export STRINGS_FILE_OUTPUT_ENCODING=UTF-16 + export STRIP_BITCODE_FROM_COPIED_FILES=NO + export STRIP_INSTALLED_PRODUCT=YES + export STRIP_PNG_TEXT=NO + export STRIP_STYLE=all + export STRIP_SWIFT_SYMBOLS=YES + export SUPPORTED_PLATFORMS=macosx + export SUPPORTS_TEXT_BASED_API=NO + export SWIFT_OBJC_BRIDGING_HEADER=Clocker-Bridging-Header.h + export SWIFT_OPTIMIZATION_LEVEL=-Onone + export SWIFT_PLATFORM_TARGET_PREFIX=macosx + export SWIFT_VERSION=5.0 + export SYMROOT=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products + export SYSTEM_ADMIN_APPS_DIR=/Applications/Utilities + export SYSTEM_APPS_DIR=/Applications + export SYSTEM_CORE_SERVICES_DIR=/System/Library/CoreServices + export SYSTEM_DEMOS_DIR=/Applications/Extras + export SYSTEM_DEVELOPER_APPS_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications + export SYSTEM_DEVELOPER_BIN_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr/bin + export SYSTEM_DEVELOPER_DEMOS_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications/Utilities/Built Examples" + export SYSTEM_DEVELOPER_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer + export SYSTEM_DEVELOPER_DOC_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/ADC Reference Library" + export SYSTEM_DEVELOPER_GRAPHICS_TOOLS_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications/Graphics Tools" + export SYSTEM_DEVELOPER_JAVA_TOOLS_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications/Java Tools" + export SYSTEM_DEVELOPER_PERFORMANCE_TOOLS_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications/Performance Tools" + export SYSTEM_DEVELOPER_RELEASENOTES_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/ADC Reference Library/releasenotes" + export SYSTEM_DEVELOPER_TOOLS=/Applications/Xcode_10.2_fb.app/Contents/Developer/Tools + export SYSTEM_DEVELOPER_TOOLS_DOC_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/ADC Reference Library/documentation/DeveloperTools" + export SYSTEM_DEVELOPER_TOOLS_RELEASENOTES_DIR="/Applications/Xcode_10.2_fb.app/Contents/Developer/ADC Reference Library/releasenotes/DeveloperTools" + export SYSTEM_DEVELOPER_USR_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/usr + export SYSTEM_DEVELOPER_UTILITIES_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Applications/Utilities + export SYSTEM_DOCUMENTATION_DIR=/Library/Documentation + export SYSTEM_KEXT_INSTALL_PATH=/System/Library/Extensions + export SYSTEM_LIBRARY_DIR=/System/Library + export TAPI_VERIFY_MODE=ErrorsOnly + export TARGETNAME=Clocker + export TARGET_BUILD_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug + export TARGET_NAME=Clocker + export TARGET_TEMP_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build + export TEMP_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build + export TEMP_FILES_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build + export TEMP_FILE_DIR=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build + export TEMP_ROOT=/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex + export TEST_HOST=Clocker + export TOOLCHAINS=com.apple.dt.toolchain.XcodeDefault + export TOOLCHAIN_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain + export TREAT_MISSING_BASELINES_AS_TEST_FAILURES=NO + export UID=1103606282 + export UNLOCALIZED_RESOURCES_FOLDER_PATH=Clocker.app/Contents/Resources + export UNSTRIPPED_PRODUCT=NO + export USER=ban + export USER_APPS_DIR=/Users/ban/Applications + export USER_LIBRARY_DIR=/Users/ban/Library + export USE_DYNAMIC_NO_PIC=YES + export USE_HEADERMAP=YES + export USE_HEADER_SYMLINKS=NO + export VALIDATE_PRODUCT=NO + export VALID_ARCHS="i386 x86_64" + export VERBOSE_PBXCP=NO + export VERSIONPLIST_PATH=Clocker.app/Contents/version.plist + export VERSION_INFO_BUILDER=ban + export VERSION_INFO_FILE=Clocker_vers.c + export VERSION_INFO_STRING=""@(#)PROGRAM:Clocker PROJECT:Clocker-"" + export WRAPPER_EXTENSION=app + export WRAPPER_NAME=Clocker.app + export WRAPPER_SUFFIX=.app + export WRAP_ASSET_PACKS_IN_SEPARATE_DIRECTORIES=NO + export XCODE_APP_SUPPORT_DIR=/Applications/Xcode_10.2_fb.app/Contents/Developer/Library/Xcode + export XCODE_PRODUCT_BUILD_VERSION=10E125 + export XCODE_VERSION_ACTUAL=1020 + export XCODE_VERSION_MAJOR=1000 + export XCODE_VERSION_MINOR=1020 + export XPCSERVICES_FOLDER_PATH=Clocker.app/Contents/XPCServices + export YACC=yacc + export arch=undefined_arch + export variant=normal + /bin/sh -c /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Script-C2A632A020EAC5EE00EB6BEA.sh + +CodeSign /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app (in target: Clocker) + cd /Users/ban/Documents/GitHub/Clocker/Clocker + export CODESIGN_ALLOCATE=/Applications/Xcode_10.2_fb.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate + +Signing Identity: "-" + + /usr/bin/codesign --force --sign - --entitlements /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Intermediates.noindex/Clocker.build/Debug/Clocker.build/Clocker.app.xcent --timestamp=none /Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app +/Users/ban/Library/Developer/Xcode/DerivedData/Clocker-hedsfmwngzzibwbekcsaudypqlfo/Build/Products/Debug/Clocker.app: replacing existing signature + +** BUILD SUCCEEDED ** +