Browse Source

Swiftformatting.

pull/92/head
Abhishek Banthia 6 years ago
parent
commit
05505fc235
  1. 1
      .swiftformat
  2. 25
      Clocker/AppDelegate.swift
  3. 8
      Clocker/Clocker.xcodeproj/project.pbxproj
  4. 1
      Clocker/Clocker/LocationController.swift
  5. 3
      Clocker/ClockerUITests/NetworkDisconnectionTests.swift
  6. 9
      Clocker/ClockerUITests/OnboardingTests.swift
  7. 3
      Clocker/ClockerUITests/PanelTests.swift
  8. 5
      Clocker/ClockerUITests/PreferencesTest.swift
  9. 3
      Clocker/ClockerUITests/ReviewTests.swift
  10. 3
      Clocker/ClockerUITests/ShortcutTests.swift
  11. 9
      Clocker/ClockerUnitTests/ClockerUnitTests.swift
  12. 4
      Clocker/ClockerUnitTests/RateTests.swift
  13. 16
      Clocker/Dependencies/Date Additions/Constants.swift
  14. 1
      Clocker/Dependencies/Date Additions/Date+Bundle.swift
  15. 23
      Clocker/Dependencies/Date Additions/Date+Comparators.swift
  16. 22
      Clocker/Dependencies/Date Additions/Date+Components.swift
  17. 1
      Clocker/Dependencies/Date Additions/Date+Format.swift
  18. 1
      Clocker/Dependencies/Date Additions/Date+Inits.swift
  19. 12
      Clocker/Dependencies/Date Additions/Date+Manipulations.swift
  20. 126
      Clocker/Dependencies/Date Additions/Date+TimeAgo.swift
  21. 1
      Clocker/Dependencies/Date Additions/Integer+DateTools.swift
  22. 86
      Clocker/Dependencies/Date Additions/TimeChunk.swift
  23. 175
      Clocker/Dependencies/Date Additions/TimePeriod.swift
  24. 21
      Clocker/Dependencies/Date Additions/TimePeriodChain.swift
  25. 42
      Clocker/Dependencies/Date Additions/TimePeriodCollection.swift
  26. 25
      Clocker/Dependencies/Date Additions/TimePeriodGroup.swift
  27. 34
      Clocker/Dependencies/Solar.swift
  28. 15
      Clocker/Events and Reminders/CalendarHandler.swift
  29. 2
      Clocker/Events and Reminders/EventCenter.swift
  30. 3
      Clocker/Events and Reminders/RemindersHandler.swift
  31. 12
      Clocker/Menu Bar/MenubarHandler.swift
  32. 8
      Clocker/Menu Bar/StatusContainerView.swift
  33. 5
      Clocker/Menu Bar/StatusItemHandler.swift
  34. 15
      Clocker/Menu Bar/StatusItemView.swift
  35. 6
      Clocker/Onboarding/OnboardingParentViewController.swift
  36. 3
      Clocker/Onboarding/OnboardingPermissionsViewController.swift
  37. 30
      Clocker/Onboarding/OnboardingSearchController.swift
  38. 9
      Clocker/Overall App/AppDefaults.swift
  39. 6
      Clocker/Overall App/AppKit + Additions.swift
  40. 2
      Clocker/Overall App/DateFormatterManager.swift
  41. 2
      Clocker/Overall App/Reach.swift
  42. 5
      Clocker/Overall App/Themer.swift
  43. 4
      Clocker/Overall App/Timer.swift
  44. 1
      Clocker/Overall App/UserDefaults + KVOExtensions.swift
  45. 7
      Clocker/Panel/Data Layer/TimezoneData.swift
  46. 5
      Clocker/Panel/Data Layer/TimezoneDataOperations.swift
  47. 2
      Clocker/Panel/FloatingWindowController.swift
  48. 8
      Clocker/Panel/Notes Popover/NotesPopover.swift
  49. 4
      Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift
  50. 11
      Clocker/Panel/PanelController.swift
  51. 38
      Clocker/Panel/ParentPanelController.swift
  52. 2
      Clocker/Panel/Rate Controller/RateController.swift
  53. 4
      Clocker/Panel/Rate Controller/ReviewView.swift
  54. 2
      Clocker/Panel/UI/PanelTableView.swift
  55. 2
      Clocker/Panel/UI/TimezoneCellView.swift
  56. 4
      Clocker/Panel/UI/TimezoneDataSource.swift
  57. 11
      Clocker/Preferences/About/AboutViewController.swift
  58. 2
      Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift
  59. 57
      Clocker/Preferences/Appearance/AppearanceViewController.swift
  60. 54
      Clocker/Preferences/Calendar/CalendarViewController.swift
  61. 48
      Clocker/Preferences/General/PreferencesViewController.swift
  62. 20
      Clocker/Preferences/OneWindowController.swift
  63. 3
      Clocker/Preferences/Permissions/PermissionsViewController.swift

1
.swiftformat

@ -0,0 +1 @@
--disable

25
Clocker/AppDelegate.swift

@ -3,9 +3,8 @@
import Cocoa import Cocoa
open class AppDelegate: NSObject, NSApplicationDelegate { open class AppDelegate: NSObject, NSApplicationDelegate {
private lazy var floatingWindow: FloatingWindowController = FloatingWindowController.shared()
lazy private var floatingWindow: FloatingWindowController = FloatingWindowController.shared() private lazy var panelController: PanelController = PanelController.shared()
lazy private var panelController: PanelController = PanelController.shared()
private var statusBarHandler: StatusItemHandler! private var statusBarHandler: StatusItemHandler!
private var panelObserver: NSKeyValueObservation? private var panelObserver: NSKeyValueObservation?
@ -13,10 +12,8 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
panelObserver?.invalidate() panelObserver?.invalidate()
} }
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
if let path = keyPath, path == "values.globalPing" { if let path = keyPath, path == "values.globalPing" {
let hotKeyCenter = PTHotKeyCenter.shared() let hotKeyCenter = PTHotKeyCenter.shared()
// Unregister old hot key // Unregister old hot key
@ -36,16 +33,14 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
hotKeyCenter?.register(newHotKey) hotKeyCenter?.register(newHotKey)
} }
} }
public func applicationWillFinishLaunching(_ notification: Notification) { public func applicationWillFinishLaunching(_: Notification) {
iVersion.sharedInstance().useAllAvailableLanguages = true iVersion.sharedInstance().useAllAvailableLanguages = true
iVersion.sharedInstance().verboseLogging = false iVersion.sharedInstance().verboseLogging = false
} }
public func applicationDidFinishLaunching(_ notification: Notification) { public func applicationDidFinishLaunching(_: Notification) {
// Initializing the event store takes really long // Initializing the event store takes really long
EventCenter.sharedCenter() EventCenter.sharedCenter()
@ -64,7 +59,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
#endif #endif
} }
public func applicationDockMenu(_ sender: NSApplication) -> NSMenu? { public func applicationDockMenu(_: NSApplication) -> NSMenu? {
let menu = NSMenu(title: "Quick Access") let menu = NSMenu(title: "Quick Access")
Logger.log(object: ["Dock Menu Triggered": "YES"], for: "Dock Menu Triggered") Logger.log(object: ["Dock Menu Triggered": "YES"], for: "Dock Menu Triggered")
@ -99,7 +94,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
private func showOnboardingFlow() { private func showOnboardingFlow() {
let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty) let shouldLaunchOnboarding = (DataStore.shared().retrieve(key: CLShowOnboardingFlow) == nil && DataStore.shared().timezones().isEmpty)
|| (ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument)) || ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument)
shouldLaunchOnboarding ? controller?.launch() : continueUsually() shouldLaunchOnboarding ? controller?.launch() : continueUsually()
} }
@ -127,7 +122,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
assignShortcut() assignShortcut()
panelObserver = panelController.observe(\.hasActivePanel, options: [.new]) { (obj, _) in panelObserver = panelController.observe(\.hasActivePanel, options: [.new]) { obj, _ in
self.statusBarHandler.setHasActiveIcon(obj.hasActivePanelGetter()) self.statusBarHandler.setHasActiveIcon(obj.hasActivePanelGetter())
} }
@ -224,7 +219,6 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
} }
private func checkIfRunFromApplicationsFolder() { private func checkIfRunFromApplicationsFolder() {
if let shortCircuit = UserDefaults.standard.object(forKey: "AllowOutsideApplicationsFolder") as? Bool, shortCircuit == true { if let shortCircuit = UserDefaults.standard.object(forKey: "AllowOutsideApplicationsFolder") as? Bool, shortCircuit == true {
return return
} }
@ -255,8 +249,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
NSApp.terminate(nil) NSApp.terminate(nil)
} }
@IBAction open func togglePanel(_ sender: Any) { @IBAction open func togglePanel(_: Any) {
let displayMode = UserDefaults.standard.integer(forKey: CLShowAppInForeground) let displayMode = UserDefaults.standard.integer(forKey: CLShowAppInForeground)
if displayMode == 1 { if displayMode == 1 {

8
Clocker/Clocker.xcodeproj/project.pbxproj

@ -896,7 +896,7 @@
9A4379201BEC220200F4E27F /* ShellScript */, 9A4379201BEC220200F4E27F /* ShellScript */,
9A20A0711C4E808500FB45AB /* Login Item Helper */, 9A20A0711C4E808500FB45AB /* Login Item Helper */,
9A5E75EC204CC39700119939 /* Embed Frameworks */, 9A5E75EC204CC39700119939 /* Embed Frameworks */,
C2A632A020EAC5EE00EB6BEA /* Move .app to Applications */, C2A632A020EAC5EE00EB6BEA /* SwiftFormat */,
); );
buildRules = ( buildRules = (
); );
@ -1070,19 +1070,19 @@
shellScript = ""; shellScript = "";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
C2A632A020EAC5EE00EB6BEA /* Move .app to Applications */ = { C2A632A020EAC5EE00EB6BEA /* SwiftFormat */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
); );
name = "Move .app to Applications"; name = SwiftFormat;
outputPaths = ( outputPaths = (
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "# sh /Users/abhishek_banthia/Documents/GitLab/Check/Clocker-Private/Clocker/Move.sh\n"; shellScript = "if which swiftformat >/dev/null; then\n swiftformat . --swiftversion 5\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */

1
Clocker/Clocker/LocationController.swift

@ -87,7 +87,6 @@ class LocationController: NSObject {
extension LocationController: CLLocationManagerDelegate { extension LocationController: CLLocationManagerDelegate {
func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard !locations.isEmpty, let coordinates = locations.first?.coordinate else { return } guard !locations.isEmpty, let coordinates = locations.first?.coordinate else { return }
let reverseGeoCoder = CLGeocoder() let reverseGeoCoder = CLGeocoder()

3
Clocker/ClockerUITests/NetworkDisconnectionTests.swift

@ -3,7 +3,6 @@
import XCTest import XCTest
class NetworkDisconnectionTests: XCTestCase { class NetworkDisconnectionTests: XCTestCase {
var app: XCUIApplication! var app: XCUIApplication!
override func setUp() { override func setUp() {
@ -41,7 +40,6 @@ class NetworkDisconnectionTests: XCTestCase {
} }
func testFetchingATimezone() { func testFetchingATimezone() {
app.launchArguments.append("mockTimezoneDown") app.launchArguments.append("mockTimezoneDown")
precondition() precondition()
app.buttons["FloatingPreferences"].click() app.buttons["FloatingPreferences"].click()
@ -70,5 +68,4 @@ class NetworkDisconnectionTests: XCTestCase {
XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists) XCTAssertTrue(app.sheets.staticTexts["ErrorPlaceholder"].exists)
app.sheets.buttons["Close"].click() app.sheets.buttons["Close"].click()
} }
} }

9
Clocker/ClockerUITests/OnboardingTests.swift

@ -5,7 +5,6 @@ import XCTest
let CLOnboaringTestsLaunchArgument = "isTestingTheOnboardingFlow" let CLOnboaringTestsLaunchArgument = "isTestingTheOnboardingFlow"
class OnboardingTests: XCTestCase { class OnboardingTests: XCTestCase {
var app: XCUIApplication! var app: XCUIApplication!
override func setUp() { override func setUp() {
@ -19,7 +18,6 @@ class OnboardingTests: XCTestCase {
// 1. The flow (forward button and back button take the user to the correct screen) // 1. The flow (forward button and back button take the user to the correct screen)
// 2. Static texts and button title's are appropriate // 2. Static texts and button title's are appropriate
func testForwardButton() { func testForwardButton() {
welcomeControllerTests() welcomeControllerTests()
// Let's go to the Permissions View // Let's go to the Permissions View
@ -42,7 +40,6 @@ class OnboardingTests: XCTestCase {
} }
func backButtonTests() { func backButtonTests() {
moveBackward() moveBackward()
searchControllerTests() searchControllerTests()
@ -59,7 +56,6 @@ class OnboardingTests: XCTestCase {
} }
func alternateStartupFlowTests() { func alternateStartupFlowTests() {
// Let's go to the Permissions View // Let's go to the Permissions View
moveForward() moveForward()
permissionsControllerTests() permissionsControllerTests()
@ -114,7 +110,6 @@ class OnboardingTests: XCTestCase {
} }
private func permissionsControllerTests() { private func permissionsControllerTests() {
let onboardingWindow = app.windows["OnboardingWindow"] let onboardingWindow = app.windows["OnboardingWindow"]
XCTAssertTrue(onboardingWindow.staticTexts["Permissions"].exists, "Header label's static text was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.staticTexts["Permissions"].exists, "Header label's static text was unexpectedly wrong.")
@ -126,7 +121,6 @@ class OnboardingTests: XCTestCase {
} }
private func startupControllerTests() { private func startupControllerTests() {
let onboardingWindow = app.windows["OnboardingWindow"] let onboardingWindow = app.windows["OnboardingWindow"]
XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Open Clocker At Login", "Forward button title's was unexpectedly wrong") XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Open Clocker At Login", "Forward button title's was unexpectedly wrong")
@ -137,7 +131,6 @@ class OnboardingTests: XCTestCase {
} }
private func searchControllerTests() { private func searchControllerTests() {
let onboardingWindow = app.windows["OnboardingWindow"] let onboardingWindow = app.windows["OnboardingWindow"]
XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.") XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.")
@ -148,7 +141,6 @@ class OnboardingTests: XCTestCase {
} }
private func finalOnboardingControllerTests() { private func finalOnboardingControllerTests() {
let onboardingWindow = app.windows["OnboardingWindow"] let onboardingWindow = app.windows["OnboardingWindow"]
// Let's test the buttons // Let's test the buttons
@ -158,5 +150,4 @@ class OnboardingTests: XCTestCase {
XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.") XCTAssertFalse(onboardingWindow.buttons["Alternate"].exists, "Alternate button was unexpectedly present.")
XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Launch Clocker", "Forward button's title was unexpectedly wrong.") XCTAssertTrue(onboardingWindow.buttons["Forward"].title == "Launch Clocker", "Forward button's title was unexpectedly wrong.")
} }
} }

3
Clocker/ClockerUITests/PanelTests.swift

@ -37,7 +37,6 @@ class PanelTests: XCTestCase {
} }
func testChangingLabelFromPopover() { func testChangingLabelFromPopover() {
app.tapMenubarIcon() app.tapMenubarIcon()
let cell = app.tables["mainTableView"].cells.firstMatch let cell = app.tables["mainTableView"].cells.firstMatch
@ -109,7 +108,6 @@ class PanelTests: XCTestCase {
} }
func testRightMouseDownToShowPopover() { func testRightMouseDownToShowPopover() {
app.tapMenubarIcon() app.tapMenubarIcon()
let cell = app.tables["mainTableView"].cells.firstMatch let cell = app.tables["mainTableView"].cells.firstMatch
@ -117,5 +115,4 @@ class PanelTests: XCTestCase {
XCTAssert(app.popovers.count > 0) XCTAssert(app.popovers.count > 0)
} }
} }

5
Clocker/ClockerUITests/PreferencesTest.swift

@ -3,7 +3,6 @@
import XCTest import XCTest
class PreferencesTest: XCTestCase { class PreferencesTest: XCTestCase {
var app: XCUIApplication! var app: XCUIApplication!
override func setUp() { override func setUp() {
@ -56,7 +55,6 @@ class PreferencesTest: XCTestCase {
} }
func testEditingLabel() { func testEditingLabel() {
let placeToAdd = "Auckland" let placeToAdd = "Auckland"
app.tapMenubarIcon() app.tapMenubarIcon()
@ -83,7 +81,6 @@ class PreferencesTest: XCTestCase {
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) app.tables["mainTableView"].typeKey(",", modifierFlags: .command)
deleteAPlace(place: placeToAdd, for: app) deleteAPlace(place: placeToAdd, for: app)
} }
func testSortingByTimezoneDifference() { func testSortingByTimezoneDifference() {
@ -347,7 +344,6 @@ class PreferencesTest: XCTestCase {
let rowQueryCount = clockerWindow.tables["TimezoneTableView"].tableRows.count let rowQueryCount = clockerWindow.tables["TimezoneTableView"].tableRows.count
if rowQueryCount > 0 { if rowQueryCount > 0 {
let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch
currentElement.click() currentElement.click()
@ -355,7 +351,6 @@ class PreferencesTest: XCTestCase {
clockerWindow.typeKey(XCUIKeyboardKey.delete, clockerWindow.typeKey(XCUIKeyboardKey.delete,
modifierFlags: XCUIElement.KeyModifierFlags()) modifierFlags: XCUIElement.KeyModifierFlags())
} }
} }
} }
} }

3
Clocker/ClockerUITests/ReviewTests.swift

@ -3,7 +3,6 @@
import XCTest import XCTest
class ReviewTests: XCTestCase { class ReviewTests: XCTestCase {
var app: XCUIApplication! var app: XCUIApplication!
override func setUp() { override func setUp() {
@ -19,7 +18,6 @@ class ReviewTests: XCTestCase {
} }
func testIfReviewIsNegativeAndUserWantsToProvideFeedback() { func testIfReviewIsNegativeAndUserWantsToProvideFeedback() {
guard app.buttons["Not Really"].exists else { return } guard app.buttons["Not Really"].exists else { return }
XCTAssertTrue(app.staticTexts["ReviewLabel"].exists) XCTAssertTrue(app.staticTexts["ReviewLabel"].exists)
app.buttons["Not Really"].click() app.buttons["Not Really"].click()
@ -55,5 +53,4 @@ class ReviewTests: XCTestCase {
app.buttons["Yes"].click() app.buttons["Yes"].click()
XCTAssertFalse(app.staticTexts["ReviewLabel"].exists) XCTAssertFalse(app.staticTexts["ReviewLabel"].exists)
} }
} }

3
Clocker/ClockerUITests/ShortcutTests.swift

@ -3,7 +3,6 @@
import XCTest import XCTest
class ShortcutTests: XCTestCase { class ShortcutTests: XCTestCase {
var app: XCUIApplication! var app: XCUIApplication!
let randomIndex = Int(arc4random_uniform(26)) let randomIndex = Int(arc4random_uniform(26))
@ -22,7 +21,6 @@ class ShortcutTests: XCTestCase {
} }
func testShortcuts() { func testShortcuts() {
app.tables["mainTableView"].typeKey(",", modifierFlags: .command) app.tables["mainTableView"].typeKey(",", modifierFlags: .command)
XCTAssertFalse(app.tables["mainTableView"].exists) XCTAssertFalse(app.tables["mainTableView"].exists)
@ -56,5 +54,4 @@ class ShortcutTests: XCTestCase {
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"] 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] return alphabet[randomIndex]
} }
} }

9
Clocker/ClockerUnitTests/ClockerUnitTests.swift

@ -1,10 +1,9 @@
// Copyright © 2015 Abhishek Banthia // Copyright © 2015 Abhishek Banthia
import XCTest
@testable import Clocker @testable import Clocker
import XCTest
class ClockerUnitTests: XCTestCase { class ClockerUnitTests: XCTestCase {
private let california = ["customLabel": "Test", private let california = ["customLabel": "Test",
"formattedAddress": "San Francisco", "formattedAddress": "San Francisco",
"place_id": "TestIdentifier", "place_id": "TestIdentifier",
@ -72,7 +71,6 @@ class ClockerUnitTests: XCTestCase {
} }
func testAddingATimezoneToDefaults() { func testAddingATimezoneToDefaults() {
let timezoneData = TimezoneData(with: california) let timezoneData = TimezoneData(with: california)
let defaults = UserDefaults.standard let defaults = UserDefaults.standard
@ -134,7 +132,6 @@ class ClockerUnitTests: XCTestCase {
} }
func testDateWithSliderValue() { func testDateWithSliderValue() {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
let operations = TimezoneDataOperations(with: dataObject) let operations = TimezoneDataOperations(with: dataObject)
@ -174,7 +171,6 @@ class ClockerUnitTests: XCTestCase {
} }
func testFormattedLabel() { func testFormattedLabel() {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
XCTAssertTrue(dataObject.formattedTimezoneLabel() == "Ghar", "Incorrect custom label returned by model.") XCTAssertTrue(dataObject.formattedTimezoneLabel() == "Ghar", "Incorrect custom label returned by model.")
@ -200,7 +196,6 @@ class ClockerUnitTests: XCTestCase {
} }
func testWithAllLocales() { func testWithAllLocales() {
let dataObject1 = TimezoneData(with: mumbai) let dataObject1 = TimezoneData(with: mumbai)
let operations = TimezoneDataOperations(with: dataObject1) let operations = TimezoneDataOperations(with: dataObject1)
@ -212,7 +207,6 @@ class ClockerUnitTests: XCTestCase {
} }
func testTimeWithAllLocales() { func testTimeWithAllLocales() {
let dataObject = TimezoneData(with: mumbai) let dataObject = TimezoneData(with: mumbai)
let cal = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian) let cal = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)
@ -235,5 +229,4 @@ class ClockerUnitTests: XCTestCase {
XCTAssertNotNil(convertedDate) XCTAssertNotNil(convertedDate)
} }
} }
} }

4
Clocker/ClockerUnitTests/RateTests.swift

@ -1,10 +1,9 @@
// Copyright © 2015 Abhishek Banthia // Copyright © 2015 Abhishek Banthia
import XCTest
@testable import Clocker @testable import Clocker
import XCTest
class RateTests: XCTestCase { class RateTests: XCTestCase {
let rateController = RateController.applicationDidLaunch(UserDefaults()) let rateController = RateController.applicationDidLaunch(UserDefaults())
override func setUp() { override func setUp() {
@ -15,5 +14,4 @@ class RateTests: XCTestCase {
// This is an example of a functional test case. // This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results. // Use XCTAssert and related functions to verify your tests produce the correct results.
} }
} }

16
Clocker/Dependencies/Date Additions/Constants.swift

@ -12,17 +12,17 @@ import Foundation
* Time conversions used across DateTools * Time conversions used across DateTools
*/ */
public class Constants { public class Constants {
public static let SecondsInYear: TimeInterval = 31536000 public static let SecondsInYear: TimeInterval = 31_536_000
public static let SecondsInLeapYear: TimeInterval = 31622400 public static let SecondsInLeapYear: TimeInterval = 31_622_400
public static let SecondsInMonth28: TimeInterval = 2419200 public static let SecondsInMonth28: TimeInterval = 2_419_200
public static let SecondsInMonth29: TimeInterval = 2505600 public static let SecondsInMonth29: TimeInterval = 2_505_600
public static let SecondsInMonth30: TimeInterval = 2592000 public static let SecondsInMonth30: TimeInterval = 2_592_000
public static let SecondsInMonth31: TimeInterval = 2678400 public static let SecondsInMonth31: TimeInterval = 2_678_400
public static let SecondsInWeek: TimeInterval = 604800 public static let SecondsInWeek: TimeInterval = 604_800
public static let SecondsInDay: TimeInterval = 86400 public static let SecondsInDay: TimeInterval = 86400
public static let SecondsInHour: TimeInterval = 3600 public static let SecondsInHour: TimeInterval = 3600
public static let SecondsInMinute: TimeInterval = 60 public static let SecondsInMinute: TimeInterval = 60
public static let MillisecondsInDay: TimeInterval = 86400000 public static let MillisecondsInDay: TimeInterval = 86_400_000
public static let AllCalendarUnitFlags: Set<Calendar.Component> = [.year, .quarter, .month, .weekOfYear, .weekOfMonth, .day, .hour, .minute, .second, .era, .weekday, .weekdayOrdinal, .weekOfYear] public static let AllCalendarUnitFlags: Set<Calendar.Component> = [.year, .quarter, .month, .weekOfYear, .weekOfMonth, .day, .hour, .minute, .second, .era, .weekday, .weekdayOrdinal, .weekOfYear]
} }

1
Clocker/Dependencies/Date Additions/Date+Bundle.swift

@ -9,7 +9,6 @@
import Foundation import Foundation
public extension Bundle { public extension Bundle {
class func dateToolsBundle() -> Bundle { class func dateToolsBundle() -> Bundle {
let assetPath = Bundle(for: Constants.self).resourcePath! let assetPath = Bundle(for: Constants.self).resourcePath!
return Bundle(path: NSString(string: assetPath).appendingPathComponent("DateTools.bundle"))! return Bundle(path: NSString(string: assetPath).appendingPathComponent("DateTools.bundle"))!

23
Clocker/Dependencies/Date Additions/Date+Comparators.swift

@ -15,7 +15,6 @@ import Foundation
* for a given unit of time. * for a given unit of time.
*/ */
public extension Date { public extension Date {
// MARK: - Comparisons // MARK: - Comparisons
/** /**
@ -57,7 +56,7 @@ public extension Date {
* - returns: Bool representing comparison result * - returns: Bool representing comparison result
*/ */
func equals(_ date: Date) -> Bool { func equals(_ date: Date) -> Bool {
return self.compare(date) == .orderedSame return compare(date) == .orderedSame
} }
/** /**
@ -69,7 +68,7 @@ public extension Date {
* - returns: Bool representing comparison result * - returns: Bool representing comparison result
*/ */
func isLater(than date: Date) -> Bool { func isLater(than date: Date) -> Bool {
return self.compare(date) == .orderedDescending return compare(date) == .orderedDescending
} }
/** /**
@ -81,7 +80,7 @@ public extension Date {
* - returns: Bool representing comparison result * - returns: Bool representing comparison result
*/ */
func isLaterThanOrEqual(to date: Date) -> Bool { func isLaterThanOrEqual(to date: Date) -> Bool {
return self.compare(date) == .orderedDescending || self.compare(date) == .orderedSame return compare(date) == .orderedDescending || compare(date) == .orderedSame
} }
/** /**
@ -93,7 +92,7 @@ public extension Date {
* - returns: Bool representing comparison result * - returns: Bool representing comparison result
*/ */
func isEarlier(than date: Date) -> Bool { func isEarlier(than date: Date) -> Bool {
return self.compare(date) == .orderedAscending return compare(date) == .orderedAscending
} }
/** /**
@ -105,7 +104,7 @@ public extension Date {
* - returns: Bool representing comparison result * - returns: Bool representing comparison result
*/ */
func isEarlierThanOrEqual(to date: Date) -> Bool { func isEarlierThanOrEqual(to date: Date) -> Bool {
return self.compare(date) == .orderedAscending || self.compare(date) == .orderedSame return compare(date) == .orderedAscending || compare(date) == .orderedSame
} }
/** /**
@ -213,7 +212,7 @@ public extension Date {
* - returns: The hours between receiver and provided date * - returns: The hours between receiver and provided date
*/ */
func hours(from date: Date) -> Int { func hours(from date: Date) -> Int {
return Int(self.timeIntervalSince(date)/Constants.SecondsInHour) return Int(timeIntervalSince(date) / Constants.SecondsInHour)
} }
/** /**
@ -227,7 +226,7 @@ public extension Date {
* - returns: The minutes between receiver and provided date * - returns: The minutes between receiver and provided date
*/ */
func minutes(from date: Date) -> Int { func minutes(from date: Date) -> Int {
return Int(self.timeIntervalSince(date)/Constants.SecondsInMinute) return Int(timeIntervalSince(date) / Constants.SecondsInMinute)
} }
/** /**
@ -259,7 +258,7 @@ public extension Date {
*/ */
func years(from date: Date, calendar: Calendar?) -> Int { func years(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar var calendarCopy = calendar
if (calendar == nil) { if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent calendarCopy = Calendar.autoupdatingCurrent
} }
@ -283,7 +282,7 @@ public extension Date {
*/ */
func months(from date: Date, calendar: Calendar?) -> Int { func months(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar var calendarCopy = calendar
if (calendar == nil) { if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent calendarCopy = Calendar.autoupdatingCurrent
} }
@ -307,7 +306,7 @@ public extension Date {
*/ */
func weeks(from date: Date, calendar: Calendar?) -> Int { func weeks(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar var calendarCopy = calendar
if (calendar == nil) { if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent calendarCopy = Calendar.autoupdatingCurrent
} }
@ -331,7 +330,7 @@ public extension Date {
*/ */
func days(from date: Date, calendar: Calendar?) -> Int { func days(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar var calendarCopy = calendar
if (calendar == nil) { if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent calendarCopy = Calendar.autoupdatingCurrent
} }

22
Clocker/Dependencies/Date Additions/Date+Components.swift

@ -14,7 +14,6 @@ import Foundation
* several computed Bools. * several computed Bools.
*/ */
public extension Date { public extension Date {
/** /**
* Convenient accessor of the date's `Calendar` components. * Convenient accessor of the date's `Calendar` components.
* *
@ -60,11 +59,10 @@ public extension Date {
var unitRange: Range<Int>? var unitRange: Range<Int>?
if larger.hashValue < smaller.hashValue { if larger.hashValue < smaller.hashValue {
for x in larger.hashValue ..< smaller.hashValue { for x in larger.hashValue ..< smaller.hashValue {
var stepLarger: Calendar.Component var stepLarger: Calendar.Component
var stepSmaller: Calendar.Component var stepSmaller: Calendar.Component
switch(x) { switch x {
case 0: case 0:
stepLarger = Calendar.Component.era stepLarger = Calendar.Component.era
stepSmaller = Calendar.Component.year stepSmaller = Calendar.Component.year
@ -79,10 +77,10 @@ public extension Date {
} }
case 2: case 2:
if larger.hashValue < 2 { if larger.hashValue < 2 {
if self.isInLeapYear { if isInLeapYear {
unitRange = Range.init(uncheckedBounds: (lower: 0, upper: 366)) unitRange = Range(uncheckedBounds: (lower: 0, upper: 366))
} else { } else {
unitRange = Range.init(uncheckedBounds: (lower: 0, upper: 365)) unitRange = Range(uncheckedBounds: (lower: 0, upper: 365))
} }
} else { } else {
stepLarger = Calendar.Component.month stepLarger = Calendar.Component.month
@ -229,42 +227,42 @@ public extension Date {
* Convenience setter for the date's `year` component * Convenience setter for the date's `year` component
*/ */
mutating func year(_ year: Int) { mutating func year(_ year: Int) {
self = Date.init(year: year, month: self.month, day: self.day, hour: self.hour, minute: self.minute, second: self.second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
/** /**
* Convenience setter for the date's `month` component * Convenience setter for the date's `month` component
*/ */
mutating func month(_ month: Int) { mutating func month(_ month: Int) {
self = Date.init(year: self.year, month: month, day: self.day, hour: self.hour, minute: self.minute, second: self.second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
/** /**
* Convenience setter for the date's `day` component * Convenience setter for the date's `day` component
*/ */
mutating func day(_ day: Int) { mutating func day(_ day: Int) {
self = Date.init(year: self.year, month: self.month, day: day, hour: self.hour, minute: self.minute, second: self.second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
/** /**
* Convenience setter for the date's `hour` component * Convenience setter for the date's `hour` component
*/ */
mutating func hour(_ hour: Int) { mutating func hour(_ hour: Int) {
self = Date.init(year: self.year, month: self.month, day: self.day, hour: hour, minute: self.minute, second: self.second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
/** /**
* Convenience setter for the date's `minute` component * Convenience setter for the date's `minute` component
*/ */
mutating func minute(_ minute: Int) { mutating func minute(_ minute: Int) {
self = Date.init(year: self.year, month: self.month, day: self.day, hour: self.hour, minute: minute, second: self.second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
/** /**
* Convenience setter for the date's `second` component * Convenience setter for the date's `second` component
*/ */
mutating func second(_ second: Int) { mutating func second(_ second: Int) {
self = Date.init(year: self.year, month: self.month, day: self.day, hour: self.hour, minute: self.minute, second: second) self = Date(year: year, month: month, day: day, hour: hour, minute: minute, second: second)
} }
// MARK: - Bools // MARK: - Bools

1
Clocker/Dependencies/Date Additions/Date+Format.swift

@ -12,7 +12,6 @@ import Foundation
* Extends the Date class by adding convenience methods for formatting dates. * Extends the Date class by adding convenience methods for formatting dates.
*/ */
public extension Date { public extension Date {
// MARK: - Formatted Date - Style // MARK: - Formatted Date - Style
/** /**

1
Clocker/Dependencies/Date Additions/Date+Inits.swift

@ -14,7 +14,6 @@ import Foundation
*/ */
public extension Date { public extension Date {
// MARK: - Initializers // MARK: - Initializers
/** /**

12
Clocker/Dependencies/Date Additions/Date+Manipulations.swift

@ -12,7 +12,6 @@ import Foundation
* Extends the Date class by adding manipulation methods for transforming dates * Extends the Date class by adding manipulation methods for transforming dates
*/ */
public extension Date { public extension Date {
// MARK: - StartOf // MARK: - StartOf
/** /**
@ -26,7 +25,7 @@ public extension Date {
func start(of component: Component) -> Date { func start(of component: Component) -> Date {
var newDate = self var newDate = self
if component == .second { if component == .second {
newDate.second(self.second) newDate.second(second)
} else if component == .minute { } else if component == .minute {
newDate.second(0) newDate.second(0)
} else if component == .hour { } else if component == .hour {
@ -99,10 +98,10 @@ public extension Date {
if month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 { if month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 {
// 31 day month // 31 day month
return 31 return 31
} else if month == 2 && date.isInLeapYear { } else if month == 2, date.isInLeapYear {
// February with leap year // February with leap year
return 29 return 29
} else if month == 2 && !date.isInLeapYear { } else if month == 2, !date.isInLeapYear {
// February without leap year // February without leap year
return 28 return 28
} else { } else {
@ -175,14 +174,13 @@ public extension Date {
* Operator overload for adding a `TimeInterval` to a date. * Operator overload for adding a `TimeInterval` to a date.
*/ */
static func + (leftAddend: Date, rightAddend: Int) -> Date { static func + (leftAddend: Date, rightAddend: Int) -> Date {
return leftAddend.addingTimeInterval((TimeInterval(rightAddend))) return leftAddend.addingTimeInterval(TimeInterval(rightAddend))
} }
/** /**
* Operator overload for subtracting a `TimeInterval` from a date. * Operator overload for subtracting a `TimeInterval` from a date.
*/ */
static func - (minuend: Date, subtrahend: Int) -> Date { static func - (minuend: Date, subtrahend: Int) -> Date {
return minuend.addingTimeInterval(-(TimeInterval(subtrahend))) return minuend.addingTimeInterval(-TimeInterval(subtrahend))
} }
} }

126
Clocker/Dependencies/Date Additions/Date+TimeAgo.swift

@ -13,7 +13,6 @@ import Foundation
* time in String format. * time in String format.
*/ */
public extension Date { public extension Date {
// MARK: - Time Ago // MARK: - Time Ago
/** /**
@ -47,7 +46,7 @@ public extension Date {
* - returns String - Formatted return string * - returns String - Formatted return string
*/ */
var timeAgoSinceNow: String { var timeAgoSinceNow: String {
return self.timeAgo(since:Date()) return timeAgo(since: Date())
} }
/** /**
@ -57,82 +56,76 @@ public extension Date {
* - returns String - Formatted return string * - returns String - Formatted return string
*/ */
var shortTimeAgoSinceNow: String { var shortTimeAgoSinceNow: String {
return self.shortTimeAgo(since:Date()) return shortTimeAgo(since: Date())
} }
func timeAgo(since date: Date, numericDates: Bool = false, numericTimes: Bool = false) -> String { func timeAgo(since date: Date, numericDates: Bool = false, numericTimes: Bool = false) -> String {
let calendar = NSCalendar.current let calendar = NSCalendar.current
let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year]) let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])
let earliest = self.earlierDate(date) let earliest = earlierDate(date)
let latest = (earliest == self) ? date : self // Should be triple equals, but not extended to Date at this time 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 components = calendar.dateComponents(unitFlags, from: earliest, to: latest)
let yesterday = date.subtract(1.days) let yesterday = date.subtract(1.days)
let isYesterday = yesterday.day == self.day let isYesterday = yesterday.day == day
// Not Yet Implemented/Optional // Not Yet Implemented/Optional
// The following strings are present in the translation files but lack logic as of 2014.04.05 // The following strings are present in the translation files but lack logic as of 2014.04.05
// @"Today", @"This week", @"This month", @"This year" // @"Today", @"This week", @"This month", @"This year"
// and @"This morning", @"This afternoon" // and @"This morning", @"This afternoon"
if (components.year! >= 2) { if components.year! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@years ago", value: components.year!) return logicalLocalizedStringFromFormat(format: "%%d %@years ago", value: components.year!)
} else if (components.year! >= 1) { } else if components.year! >= 1 {
if numericDates {
if (numericDates) {
return DateToolsLocalizedStrings("1 year ago") return DateToolsLocalizedStrings("1 year ago")
} }
return DateToolsLocalizedStrings("Last year") return DateToolsLocalizedStrings("Last year")
} else if (components.month! >= 2) { } else if components.month! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@months ago", value: components.month!) return logicalLocalizedStringFromFormat(format: "%%d %@months ago", value: components.month!)
} else if (components.month! >= 1) { } else if components.month! >= 1 {
if numericDates {
if (numericDates) {
return DateToolsLocalizedStrings("1 month ago") return DateToolsLocalizedStrings("1 month ago")
} }
return DateToolsLocalizedStrings("Last month") return DateToolsLocalizedStrings("Last month")
} else if (components.weekOfYear! >= 2) { } else if components.weekOfYear! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@weeks ago", value: components.weekOfYear!) return logicalLocalizedStringFromFormat(format: "%%d %@weeks ago", value: components.weekOfYear!)
} else if (components.weekOfYear! >= 1) { } else if components.weekOfYear! >= 1 {
if numericDates {
if (numericDates) {
return DateToolsLocalizedStrings("1 week ago") return DateToolsLocalizedStrings("1 week ago")
} }
return DateToolsLocalizedStrings("Last week") return DateToolsLocalizedStrings("Last week")
} else if (components.day! >= 2) { } else if components.day! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@days ago", value: components.day!) return logicalLocalizedStringFromFormat(format: "%%d %@days ago", value: components.day!)
} else if (isYesterday) { } else if isYesterday {
if (numericDates) { if numericDates {
return DateToolsLocalizedStrings("1 day ago") return DateToolsLocalizedStrings("1 day ago")
} }
return DateToolsLocalizedStrings("Yesterday") return DateToolsLocalizedStrings("Yesterday")
} else if (components.hour! >= 2) { } else if components.hour! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@hours ago", value: components.hour!) return logicalLocalizedStringFromFormat(format: "%%d %@hours ago", value: components.hour!)
} else if (components.hour! >= 1) { } else if components.hour! >= 1 {
if numericTimes {
if (numericTimes) {
return DateToolsLocalizedStrings("1 hour ago") return DateToolsLocalizedStrings("1 hour ago")
} }
return DateToolsLocalizedStrings("An hour ago") return DateToolsLocalizedStrings("An hour ago")
} else if (components.minute! >= 2) { } else if components.minute! >= 2 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@minutes ago", value: components.minute!) return logicalLocalizedStringFromFormat(format: "%%d %@minutes ago", value: components.minute!)
} else if (components.minute! >= 1) { } else if components.minute! >= 1 {
if numericTimes {
if (numericTimes) {
return DateToolsLocalizedStrings("1 minute ago") return DateToolsLocalizedStrings("1 minute ago")
} }
return DateToolsLocalizedStrings("A minute ago") return DateToolsLocalizedStrings("A minute ago")
} else if (components.second! >= 3) { } else if components.second! >= 3 {
return self.logicalLocalizedStringFromFormat(format: "%%d %@seconds ago", value: components.second!) return logicalLocalizedStringFromFormat(format: "%%d %@seconds ago", value: components.second!)
} else { } else {
if numericTimes {
if (numericTimes) {
return DateToolsLocalizedStrings("1 second ago") return DateToolsLocalizedStrings("1 second ago")
} }
@ -143,55 +136,55 @@ public extension Date {
func shortTimeAgo(since date: Date) -> String { func shortTimeAgo(since date: Date) -> String {
let calendar = NSCalendar.current let calendar = NSCalendar.current
let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year]) let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])
let earliest = self.earlierDate(date) let earliest = earlierDate(date)
let latest = (earliest == self) ? date : self // Should pbe triple equals, but not extended to Date at this time 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 components = calendar.dateComponents(unitFlags, from: earliest, to: latest)
let yesterday = date.subtract(1.days) let yesterday = date.subtract(1.days)
let isYesterday = yesterday.day == self.day let isYesterday = yesterday.day == day
if (components.year! >= 1) { if components.year! >= 1 {
return self.logicalLocalizedStringFromFormat(format: "%%d%@y", value: components.year!) return 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!) return 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!) return 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!) return logicalLocalizedStringFromFormat(format: "%%d%@d", value: components.day!)
} else if (isYesterday) { } else if isYesterday {
return self.logicalLocalizedStringFromFormat(format: "%%d%@d", value: 1) return 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!) return 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!) return 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!) return logicalLocalizedStringFromFormat(format: "%%d%@s", value: components.second!)
} else { } else {
return self.logicalLocalizedStringFromFormat(format: "%%d%@s", value: components.second!) return logicalLocalizedStringFromFormat(format: "%%d%@s", value: components.second!)
// return DateToolsLocalizedStrings(@"Now"); //string not yet translated 2014.04.05 // 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 {
let localeFormat = String.init(format: format, getLocaleFormatUnderscoresWithValue(Double(value))) let localeFormat = String(format: format, getLocaleFormatUnderscoresWithValue(Double(value)))
return String.init(format: DateToolsLocalizedStrings(localeFormat), value) return String(format: DateToolsLocalizedStrings(localeFormat), value)
} }
private func getLocaleFormatUnderscoresWithValue(_ value: Double) -> String { private func getLocaleFormatUnderscoresWithValue(_ value: Double) -> String {
let localCode = Bundle.main.preferredLocalizations[0] let localCode = Bundle.main.preferredLocalizations[0]
if (localCode == "ru" || localCode == "uk") { if localCode == "ru" || localCode == "uk" {
let XY = Int(floor(value).truncatingRemainder(dividingBy: 100)) let XY = Int(floor(value).truncatingRemainder(dividingBy: 100))
let Y = Int(floor(value).truncatingRemainder(dividingBy: 10)) let Y = Int(floor(value).truncatingRemainder(dividingBy: 10))
if(Y == 0 || Y > 4 || (XY > 10 && XY < 15)) { if Y == 0 || Y > 4 || (XY > 10 && XY < 15) {
return "" return ""
} }
if(Y > 1 && Y < 5 && (XY < 10 || XY > 20)) { if Y > 1, Y < 5, XY < 10 || XY > 20 {
return "_" return "_"
} }
if(Y == 1 && XY != 11) { if Y == 1, XY != 11 {
return "__" return "__"
} }
} }
@ -224,7 +217,7 @@ public extension Date {
* - returns: The date that is earlier * - returns: The date that is earlier
*/ */
func earlierDate(_ date: Date) -> Date { func earlierDate(_ date: Date) -> Date {
return (self.timeIntervalSince1970 <= date.timeIntervalSince1970) ? self : date return (timeIntervalSince1970 <= date.timeIntervalSince1970) ? self : date
} }
/** /**
@ -235,7 +228,6 @@ public extension Date {
* - returns: The date that is later * - returns: The date that is later
*/ */
func laterDate(_ date: Date) -> Date { func laterDate(_ date: Date) -> Date {
return (self.timeIntervalSince1970 >= date.timeIntervalSince1970) ? self : date return (timeIntervalSince1970 >= date.timeIntervalSince1970) ? self : date
} }
} }

1
Clocker/Dependencies/Date Additions/Integer+DateTools.swift

@ -9,7 +9,6 @@
import Foundation import Foundation
public extension Int { public extension Int {
// MARK: TimePeriod // MARK: TimePeriod
/** /**

86
Clocker/Dependencies/Date Additions/TimeChunk.swift

@ -20,7 +20,6 @@ import Foundation
* For more information about the utility of TimeChunks in relation to Dates, see the `Date+Manipulations` class. * For more information about the utility of TimeChunks in relation to Dates, see the `Date+Manipulations` class.
*/ */
public struct TimeChunk { public struct TimeChunk {
// MARK: - Variables // MARK: - Variables
public var seconds = 0 public var seconds = 0
@ -67,57 +66,57 @@ public struct TimeChunk {
* well defined unit of time without the context of a calendar. ! * well defined unit of time without the context of a calendar. !
*/ */
public func to(_ unit: TimeUnits) -> Int { public func to(_ unit: TimeUnits) -> Int {
if self.months != 0 { if months != 0 {
print("Months are not supported for conversion due to their uncertain number of days.") print("Months are not supported for conversion due to their uncertain number of days.")
return 0 return 0
} }
if (unit == .seconds) { if unit == .seconds {
var total = self.seconds var total = seconds
total += self.minutes * 60 total += minutes * 60
total += self.hours * 60 * 60 total += hours * 60 * 60
total += self.days * 24 * 60 * 60 total += days * 24 * 60 * 60
total += self.weeks * 7 * 24 * 60 * 60 total += weeks * 7 * 24 * 60 * 60
total += self.years * 365 * 24 * 60 * 60 total += years * 365 * 24 * 60 * 60
return total return total
} else if (unit == .minutes) { } else if unit == .minutes {
var total = self.minutes var total = minutes
total += self.seconds / 60 total += seconds / 60
total += self.hours * 60 total += hours * 60
total += self.days * 24 * 60 total += days * 24 * 60
total += self.weeks * 7 * 24 * 60 total += weeks * 7 * 24 * 60
total += self.years * 365 * 24 * 60 total += years * 365 * 24 * 60
return total return total
} else if (unit == .hours) { } else if unit == .hours {
var total = self.hours var total = hours
let secondsToMinutes = self.seconds / 60 let secondsToMinutes = seconds / 60
total += (self.minutes + secondsToMinutes) / 60 total += (minutes + secondsToMinutes) / 60
total += self.days * 24 total += days * 24
total += self.weeks * 7 * 24 total += weeks * 7 * 24
total += self.years * 365 * 24 total += years * 365 * 24
return total return total
} else if (unit == .days) { } else if unit == .days {
var total = self.days var total = days
let secondsToMinutes = self.seconds / 60 let secondsToMinutes = seconds / 60
let minutesToHours = (self.minutes + secondsToMinutes) / 60 let minutesToHours = (minutes + secondsToMinutes) / 60
total += (self.hours + minutesToHours) / 24 total += (hours + minutesToHours) / 24
total += self.weeks * 7 total += weeks * 7
total += self.years * 365 total += years * 365
return total return total
} else if (unit == .weeks) { } else if unit == .weeks {
var total = self.weeks var total = weeks
let secondsToMinutes = self.seconds / 60 let secondsToMinutes = seconds / 60
let minutesToHours = (self.minutes + secondsToMinutes) / 60 let minutesToHours = (minutes + secondsToMinutes) / 60
let hoursToDays = (self.hours + minutesToHours) / 24 let hoursToDays = (hours + minutesToHours) / 24
total += (self.days + hoursToDays) / 7 total += (days + hoursToDays) / 7
total += self.years * 52 total += years * 52
return total return total
} else if (unit == .years) { } else if unit == .years {
var total = self.years var total = years
let secondsToMinutes = self.seconds / 60 let secondsToMinutes = seconds / 60
let minutesToHours = (self.minutes + secondsToMinutes) / 60 let minutesToHours = (minutes + secondsToMinutes) / 60
let hoursToDays = (self.hours + minutesToHours) / 24 let hoursToDays = (hours + minutesToHours) / 24
let weeksToDays = weeks * 7 let weeksToDays = weeks * 7
total += (self.days + hoursToDays + weeksToDays) / 365 total += (days + hoursToDays + weeksToDays) / 365
return total return total
} }
return 0 return 0
@ -274,5 +273,4 @@ public struct TimeChunk {
invertedChunk.years = -chunk.years invertedChunk.years = -chunk.years
return invertedChunk return invertedChunk
} }
} }

175
Clocker/Dependencies/Date Additions/TimePeriod.swift

@ -16,7 +16,6 @@ import Foundation
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information. * [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information.
*/ */
public protocol TimePeriodProtocol { public protocol TimePeriodProtocol {
// MARK: - Variables // MARK: - Variables
/** /**
@ -31,14 +30,13 @@ public protocol TimePeriodProtocol {
} }
public extension TimePeriodProtocol { public extension TimePeriodProtocol {
// MARK: - Information // MARK: - Information
/** /**
* True if the `TimePeriod`'s duration is zero * True if the `TimePeriod`'s duration is zero
*/ */
var isMoment: Bool { var isMoment: Bool {
return self.beginning == self.end return beginning == end
} }
/** /**
@ -46,8 +44,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var years: Int { var years: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.yearsEarlier(than: self.end!) return beginning!.yearsEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -57,8 +55,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var weeks: Int { var weeks: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.weeksEarlier(than: self.end!) return beginning!.weeksEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -68,8 +66,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var days: Int { var days: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.daysEarlier(than: self.end!) return beginning!.daysEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -79,8 +77,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var hours: Int { var hours: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.hoursEarlier(than: self.end!) return beginning!.hoursEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -90,8 +88,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var minutes: Int { var minutes: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.minutesEarlier(than: self.end!) return beginning!.minutesEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -101,8 +99,8 @@ public extension TimePeriodProtocol {
* Returns the max int if beginning or end are nil. * Returns the max int if beginning or end are nil.
*/ */
var seconds: Int { var seconds: Int {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return self.beginning!.secondsEarlier(than: self.end!) return beginning!.secondsEarlier(than: end!)
} }
return Int.max return Int.max
} }
@ -112,7 +110,7 @@ public extension TimePeriodProtocol {
* Returns a time chunk with all zeroes if beginning or end are nil. * Returns a time chunk with all zeroes if beginning or end are nil.
*/ */
var chunk: TimeChunk { var chunk: TimeChunk {
if beginning != nil && end != nil { if beginning != nil, end != nil {
return beginning!.chunkBetween(date: end!) return beginning!.chunkBetween(date: end!)
} }
return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 0, weeks: 0, months: 0, years: 0) return TimeChunk(seconds: 0, minutes: 0, hours: 0, days: 0, weeks: 0, months: 0, years: 0)
@ -123,8 +121,8 @@ public extension TimePeriodProtocol {
* `TimePeriod` as a `TimeInterval`. * `TimePeriod` as a `TimeInterval`.
*/ */
var duration: TimeInterval { var duration: TimeInterval {
if self.beginning != nil && self.end != nil { if beginning != nil, end != nil {
return abs(self.beginning!.timeIntervalSince(self.end!)) return abs(beginning!.timeIntervalSince(end!))
} }
return TimeInterval(Double.greatestFiniteMagnitude) return TimeInterval(Double.greatestFiniteMagnitude)
@ -145,36 +143,35 @@ public extension TimePeriodProtocol {
*/ */
func relation(to period: TimePeriodProtocol) -> Relation { func relation(to period: TimePeriodProtocol) -> Relation {
// Make sure that all start and end points exist for comparison // Make sure that all start and end points exist for comparison
if (self.beginning != nil && self.end != nil && period.beginning != nil && period.end != nil) { if beginning != nil, end != nil, period.beginning != nil, period.end != nil {
// Make sure time periods are of positive durations // Make sure time periods are of positive durations
if (self.beginning!.isEarlier(than: self.end!) && period.beginning!.isEarlier(than: period.end!)) { if beginning!.isEarlier(than: end!), period.beginning!.isEarlier(than: period.end!) {
// Make comparisons // Make comparisons
if (period.end!.isEarlier(than: self.beginning!)) { if period.end!.isEarlier(than: beginning!) {
return .after return .after
} else if (period.end!.equals(self.beginning!)) { } else if period.end!.equals(beginning!) {
return .startTouching return .startTouching
} else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { } else if period.beginning!.isEarlier(than: beginning!), period.end!.isEarlier(than: end!) {
return .startInside return .startInside
} else if (period.beginning!.equals(self.beginning!) && period.end!.isLater(than: self.end!)) { } else if period.beginning!.equals(beginning!), period.end!.isLater(than: end!) {
return .insideStartTouching return .insideStartTouching
} else if (period.beginning!.equals(self.beginning!) && period.end!.isEarlier(than: self.end!)) { } else if period.beginning!.equals(beginning!), period.end!.isEarlier(than: end!) {
return .enclosingStartTouching return .enclosingStartTouching
} else if (period.beginning!.isLater(than: self.beginning!) && period.end!.isEarlier(than: self.end!)) { } else if period.beginning!.isLater(than: beginning!), period.end!.isEarlier(than: end!) {
return .enclosing return .enclosing
} else if (period.beginning!.isLater(than: self.beginning!) && period.end!.equals(self.end!)) { } else if period.beginning!.isLater(than: beginning!), period.end!.equals(end!) {
return .enclosingEndTouching return .enclosingEndTouching
} else if (period.beginning!.equals(self.beginning!) && period.end!.equals(self.end!)) { } else if period.beginning!.equals(beginning!), period.end!.equals(end!) {
return .exactMatch return .exactMatch
} else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isLater(than: self.end!)) { } else if period.beginning!.isEarlier(than: beginning!), period.end!.isLater(than: end!) {
return .inside return .inside
} else if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.equals(self.end!)) { } else if period.beginning!.isEarlier(than: beginning!), period.end!.equals(end!) {
return .insideEndTouching return .insideEndTouching
} else if (period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)) { } else if period.beginning!.isEarlier(than: end!), period.end!.isLater(than: end!) {
return .endInside return .endInside
} else if (period.beginning!.equals(self.end!) && period.end!.isLater(than: self.end!)) { } else if period.beginning!.equals(end!), period.end!.isLater(than: end!) {
return .endTouching return .endTouching
} else if (period.beginning!.isLater(than: self.end!)) { } else if period.beginning!.isLater(than: end!) {
return .before return .before
} }
} }
@ -192,7 +189,7 @@ public extension TimePeriodProtocol {
* - returns: True if the periods are the same * - returns: True if the periods are the same
*/ */
func equals(_ period: TimePeriodProtocol) -> Bool { func equals(_ period: TimePeriodProtocol) -> Bool {
return self.beginning == period.beginning && self.end == period.end return beginning == period.beginning && end == period.end
} }
/** /**
@ -204,7 +201,7 @@ public extension TimePeriodProtocol {
* - returns: True if self is inside of the given `TimePeriod` * - returns: True if self is inside of the given `TimePeriod`
*/ */
func isInside(of period: TimePeriodProtocol) -> Bool { func isInside(of period: TimePeriodProtocol) -> Bool {
return period.beginning!.isEarlierThanOrEqual(to: self.beginning!) && period.end!.isLaterThanOrEqual(to: self.end!) return period.beginning!.isEarlierThanOrEqual(to: beginning!) && period.end!.isLaterThanOrEqual(to: end!)
} }
/** /**
@ -216,10 +213,10 @@ public extension TimePeriodProtocol {
* - returns: True if the given `TimePeriod` is inside of self * - returns: True if the given `TimePeriod` is inside of self
*/ */
func contains(_ date: Date, interval: Interval) -> Bool { func contains(_ date: Date, interval: Interval) -> Bool {
if (interval == .open) { if interval == .open {
return self.beginning!.isEarlier(than: date) && self.end!.isLater(than: date) return beginning!.isEarlier(than: date) && end!.isLater(than: date)
} else if (interval == .closed) { } else if interval == .closed {
return (self.beginning!.isEarlierThanOrEqual(to: date) && self.end!.isLaterThanOrEqual(to: date)) return (beginning!.isEarlierThanOrEqual(to: date) && end!.isLaterThanOrEqual(to: date))
} }
return false return false
@ -234,7 +231,7 @@ public extension TimePeriodProtocol {
* - returns: True if the given `TimePeriod` is inside of self * - returns: True if the given `TimePeriod` is inside of self
*/ */
func contains(_ period: TimePeriodProtocol) -> Bool { func contains(_ period: TimePeriodProtocol) -> Bool {
return self.beginning!.isEarlierThanOrEqual(to: period.beginning!) && self.end!.isLaterThanOrEqual(to: period.end!) return beginning!.isEarlierThanOrEqual(to: period.beginning!) && end!.isLaterThanOrEqual(to: period.end!)
} }
/** /**
@ -246,15 +243,15 @@ public extension TimePeriodProtocol {
*/ */
func overlaps(with period: TimePeriodProtocol) -> Bool { func overlaps(with period: TimePeriodProtocol) -> Bool {
// Outside -> Inside // Outside -> Inside
if (period.beginning!.isEarlier(than: self.beginning!) && period.end!.isLater(than: self.beginning!)) { if period.beginning!.isEarlier(than: beginning!), period.end!.isLater(than: beginning!) {
return true return true
} }
// Enclosing // Enclosing
else if (period.beginning!.isLaterThanOrEqual(to: self.beginning!) && period.end!.isEarlierThanOrEqual(to: self.end!)) { else if period.beginning!.isLaterThanOrEqual(to: beginning!), period.end!.isEarlierThanOrEqual(to: end!) {
return true return true
} }
// Inside -> Out // Inside -> Out
else if(period.beginning!.isEarlier(than: self.end!) && period.end!.isLater(than: self.end!)) { else if period.beginning!.isEarlier(than: end!), period.end!.isLater(than: end!) {
return true return true
} }
return false return false
@ -268,7 +265,7 @@ public extension TimePeriodProtocol {
* - returns: True if there is a period of time or moment that is shared by both `TimePeriod`s * - returns: True if there is a period of time or moment that is shared by both `TimePeriod`s
*/ */
func intersects(with period: TimePeriodProtocol) -> Bool { func intersects(with period: TimePeriodProtocol) -> Bool {
return self.relation(to: period) != .after && self.relation(to: period) != .before return relation(to: period) != .after && relation(to: period) != .before
} }
/** /**
@ -279,7 +276,7 @@ public extension TimePeriodProtocol {
* - returns: True if there is a period of time between self and the given `TimePeriod` not contained by either period * - returns: True if there is a period of time between self and the given `TimePeriod` not contained by either period
*/ */
func hasGap(between period: TimePeriodProtocol) -> Bool { func hasGap(between period: TimePeriodProtocol) -> Bool {
return self.isBefore(period: period) || self.isAfter(period: period) return isBefore(period: period) || isAfter(period: period)
} }
/** /**
@ -290,10 +287,10 @@ public extension TimePeriodProtocol {
* - returns: The gap between the periods. Zero if there is no gap. * - returns: The gap between the periods. Zero if there is no gap.
*/ */
func gap(between period: TimePeriodProtocol) -> TimeInterval { func gap(between period: TimePeriodProtocol) -> TimeInterval {
if (self.end!.isEarlier(than: period.beginning!)) { if end!.isEarlier(than: period.beginning!) {
return abs(self.end!.timeIntervalSince(period.beginning!)) return abs(end!.timeIntervalSince(period.beginning!))
} else if (period.end!.isEarlier(than: self.beginning!)) { } else if period.end!.isEarlier(than: beginning!) {
return abs(period.end!.timeIntervalSince(self.beginning!)) return abs(period.end!.timeIntervalSince(beginning!))
} }
return 0 return 0
} }
@ -307,8 +304,8 @@ public extension TimePeriodProtocol {
* - returns: The gap between the periods, zero if there is no gap * - returns: The gap between the periods, zero if there is no gap
*/ */
func gap(between period: TimePeriodProtocol) -> TimeChunk? { func gap(between period: TimePeriodProtocol) -> TimeChunk? {
if self.end != nil && period.beginning != nil { if end != nil, period.beginning != nil {
return (self.end?.chunkBetween(date: period.beginning!))! return (end?.chunkBetween(date: period.beginning!))!
} }
return nil return nil
} }
@ -321,7 +318,7 @@ public extension TimePeriodProtocol {
* - returns: True if self is after the given `TimePeriod` * - returns: True if self is after the given `TimePeriod`
*/ */
func isAfter(period: TimePeriodProtocol) -> Bool { func isAfter(period: TimePeriodProtocol) -> Bool {
return self.relation(to: period) == .after return relation(to: period) == .after
} }
/** /**
@ -332,7 +329,7 @@ public extension TimePeriodProtocol {
* - returns: True if self is after the given `TimePeriod` * - returns: True if self is after the given `TimePeriod`
*/ */
func isBefore(period: TimePeriodProtocol) -> Bool { func isBefore(period: TimePeriodProtocol) -> Bool {
return self.relation(to: period) == .before return relation(to: period) == .before
} }
// MARK: - Shifts // MARK: - Shifts
@ -345,8 +342,8 @@ public extension TimePeriodProtocol {
* - parameter timeInterval: The time interval to shift the period by * - parameter timeInterval: The time interval to shift the period by
*/ */
mutating func shift(by timeInterval: TimeInterval) { mutating func shift(by timeInterval: TimeInterval) {
self.beginning?.addTimeInterval(timeInterval) beginning?.addTimeInterval(timeInterval)
self.end?.addTimeInterval(timeInterval) end?.addTimeInterval(timeInterval)
} }
/** /**
@ -355,8 +352,8 @@ public extension TimePeriodProtocol {
* - parameter chunk: The time chunk to shift the period by * - parameter chunk: The time chunk to shift the period by
*/ */
mutating func shift(by chunk: TimeChunk) { mutating func shift(by chunk: TimeChunk) {
self.beginning = self.beginning?.add(chunk) beginning = beginning?.add(chunk)
self.end = self.end?.add(chunk) end = end?.add(chunk)
} }
// MARK: - Lengthen / Shorten // MARK: - Lengthen / Shorten
@ -372,12 +369,12 @@ public extension TimePeriodProtocol {
mutating func lengthen(by timeInterval: TimeInterval, at anchor: Anchor) { mutating func lengthen(by timeInterval: TimeInterval, at anchor: Anchor) {
switch anchor { switch anchor {
case .beginning: case .beginning:
self.end = self.end?.addingTimeInterval(timeInterval) end = end?.addingTimeInterval(timeInterval)
case .center: case .center:
self.beginning = self.beginning?.addingTimeInterval(-timeInterval/2.0) beginning = beginning?.addingTimeInterval(-timeInterval / 2.0)
self.end = self.end?.addingTimeInterval(timeInterval/2.0) end = end?.addingTimeInterval(timeInterval / 2.0)
case .end: case .end:
self.beginning = self.beginning?.addingTimeInterval(-timeInterval) beginning = beginning?.addingTimeInterval(-timeInterval)
} }
} }
@ -390,12 +387,12 @@ public extension TimePeriodProtocol {
mutating func lengthen(by chunk: TimeChunk, at anchor: Anchor) { mutating func lengthen(by chunk: TimeChunk, at anchor: Anchor) {
switch anchor { switch anchor {
case .beginning: case .beginning:
self.end = self.end?.add(chunk) end = end?.add(chunk)
case .center: case .center:
// Do not lengthen by TimeChunk at center // Do not lengthen by TimeChunk at center
print("Mutation via chunk from center anchor is not supported.") print("Mutation via chunk from center anchor is not supported.")
case .end: case .end:
self.beginning = self.beginning?.subtract(chunk) beginning = beginning?.subtract(chunk)
} }
} }
@ -408,12 +405,12 @@ public extension TimePeriodProtocol {
mutating func shorten(by timeInterval: TimeInterval, at anchor: Anchor) { mutating func shorten(by timeInterval: TimeInterval, at anchor: Anchor) {
switch anchor { switch anchor {
case .beginning: case .beginning:
self.end = self.end?.addingTimeInterval(-timeInterval) end = end?.addingTimeInterval(-timeInterval)
case .center: case .center:
self.beginning = self.beginning?.addingTimeInterval(timeInterval/2.0) beginning = beginning?.addingTimeInterval(timeInterval / 2.0)
self.end = self.end?.addingTimeInterval(-timeInterval/2.0) end = end?.addingTimeInterval(-timeInterval / 2.0)
case .end: case .end:
self.beginning = self.beginning?.addingTimeInterval(timeInterval) beginning = beginning?.addingTimeInterval(timeInterval)
} }
} }
@ -426,12 +423,12 @@ public extension TimePeriodProtocol {
mutating func shorten(by chunk: TimeChunk, at anchor: Anchor) { mutating func shorten(by chunk: TimeChunk, at anchor: Anchor) {
switch anchor { switch anchor {
case .beginning: case .beginning:
self.end = self.end?.subtract(chunk) end = end?.subtract(chunk)
case .center: case .center:
// Do not shorten by TimeChunk at center // Do not shorten by TimeChunk at center
print("Mutation via chunk from center anchor is not supported.") print("Mutation via chunk from center anchor is not supported.")
case .end: case .end:
self.beginning = self.beginning?.add(chunk) beginning = beginning?.add(chunk)
} }
} }
} }
@ -444,8 +441,8 @@ public extension TimePeriodProtocol {
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information. * [Visit our github page](https://github.com/MatthewYork/DateTools#time-periods) for more information.
*/ */
open class TimePeriod: TimePeriodProtocol { open class TimePeriod: TimePeriodProtocol {
// MARK: - Variables // MARK: - Variables
/** /**
* The start date for a TimePeriod representing the starting boundary of the time period * The start date for a TimePeriod representing the starting boundary of the time period
*/ */
@ -458,9 +455,7 @@ open class TimePeriod: TimePeriodProtocol {
// MARK: - Initializers // MARK: - Initializers
init() { init() {}
}
init(beginning: Date?, end: Date?) { init(beginning: Date?, end: Date?) {
self.beginning = beginning self.beginning = beginning
@ -469,27 +464,27 @@ open class TimePeriod: TimePeriodProtocol {
init(beginning: Date, duration: TimeInterval) { init(beginning: Date, duration: TimeInterval) {
self.beginning = beginning self.beginning = beginning
self.end = beginning + duration end = beginning + duration
} }
init(end: Date, duration: TimeInterval) { init(end: Date, duration: TimeInterval) {
self.end = end self.end = end
self.beginning = end.addingTimeInterval(-duration) beginning = end.addingTimeInterval(-duration)
} }
init(beginning: Date, chunk: TimeChunk) { init(beginning: Date, chunk: TimeChunk) {
self.beginning = beginning self.beginning = beginning
self.end = beginning + chunk end = beginning + chunk
} }
init(end: Date, chunk: TimeChunk) { init(end: Date, chunk: TimeChunk) {
self.end = end self.end = end
self.beginning = end - chunk beginning = end - chunk
} }
init(chunk: TimeChunk) { init(chunk: TimeChunk) {
self.beginning = Date() beginning = Date()
self.end = self.beginning?.add(chunk) end = beginning?.add(chunk)
} }
// MARK: - Shifted // MARK: - Shifted
@ -503,8 +498,8 @@ open class TimePeriod: TimePeriodProtocol {
*/ */
func shifted(by timeInterval: TimeInterval) -> TimePeriod { func shifted(by timeInterval: TimeInterval) -> TimePeriod {
let timePeriod = TimePeriod() let timePeriod = TimePeriod()
timePeriod.beginning = self.beginning?.addingTimeInterval(timeInterval) timePeriod.beginning = beginning?.addingTimeInterval(timeInterval)
timePeriod.end = self.end?.addingTimeInterval(timeInterval) timePeriod.end = end?.addingTimeInterval(timeInterval)
return timePeriod return timePeriod
} }
@ -517,8 +512,8 @@ open class TimePeriod: TimePeriodProtocol {
*/ */
func shifted(by chunk: TimeChunk) -> TimePeriod { func shifted(by chunk: TimeChunk) -> TimePeriod {
let timePeriod = TimePeriod() let timePeriod = TimePeriod()
timePeriod.beginning = self.beginning?.add(chunk) timePeriod.beginning = beginning?.add(chunk)
timePeriod.end = self.end?.add(chunk) timePeriod.end = end?.add(chunk)
return timePeriod return timePeriod
} }
@ -538,14 +533,14 @@ open class TimePeriod: TimePeriodProtocol {
let timePeriod = TimePeriod() let timePeriod = TimePeriod()
switch anchor { switch anchor {
case .beginning: case .beginning:
timePeriod.beginning = self.beginning timePeriod.beginning = beginning
timePeriod.end = self.end?.addingTimeInterval(timeInterval) timePeriod.end = end?.addingTimeInterval(timeInterval)
case .center: case .center:
timePeriod.beginning = self.beginning?.addingTimeInterval(-timeInterval) timePeriod.beginning = beginning?.addingTimeInterval(-timeInterval)
timePeriod.end = self.end?.addingTimeInterval(timeInterval) timePeriod.end = end?.addingTimeInterval(timeInterval)
case .end: case .end:
timePeriod.beginning = self.beginning?.addingTimeInterval(-timeInterval) timePeriod.beginning = beginning?.addingTimeInterval(-timeInterval)
timePeriod.end = self.end timePeriod.end = end
} }
return timePeriod return timePeriod

21
Clocker/Dependencies/Date Additions/TimePeriodChain.swift

@ -18,7 +18,6 @@ import Foundation
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-chains) for more information. * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-chains) for more information.
*/ */
open class TimePeriodChain: TimePeriodGroup { open class TimePeriodChain: TimePeriodGroup {
// MARK: - Chain Existence Manipulation // MARK: - Chain Existence Manipulation
/** /**
@ -28,10 +27,10 @@ open class TimePeriodChain: TimePeriodGroup {
* - parameter period: TimePeriodProtocol to add to the collection * - parameter period: TimePeriodProtocol to add to the collection
*/ */
public func append(_ period: TimePeriodProtocol) { public func append(_ period: TimePeriodProtocol) {
let beginning = (self.periods.isEmpty == false) ? self.periods.last!.end! : period.beginning let beginning = (periods.isEmpty == false) ? periods.last!.end! : period.beginning
let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration) let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration)
self.periods.append(newPeriod) periods.append(newPeriod)
// Update updateExtremes // Update updateExtremes
if periods.count == 1 { if periods.count == 1 {
@ -50,10 +49,10 @@ open class TimePeriodChain: TimePeriodGroup {
*/ */
public func append<G: TimePeriodGroup>(contentsOf group: G) { public func append<G: TimePeriodGroup>(contentsOf group: G) {
for period in group.periods { for period in group.periods {
let beginning = (self.periods.isEmpty == false) ? self.periods.last!.end! : period.beginning let beginning = (periods.isEmpty == false) ? periods.last!.end! : period.beginning
let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration) let newPeriod = TimePeriod(beginning: beginning!, duration: period.duration)
self.periods.append(newPeriod) periods.append(newPeriod)
// Update updateExtremes // Update updateExtremes
if periods.count == 1 { if periods.count == 1 {
@ -73,10 +72,10 @@ open class TimePeriodChain: TimePeriodGroup {
*/ */
public func insert(_ period: TimePeriodProtocol, at index: Int) { public func insert(_ period: TimePeriodProtocol, at index: Int) {
// Check for special zero case which takes the beginning date // Check for special zero case which takes the beginning date
if index == 0 && period.beginning != nil && period.end != nil { if index == 0, period.beginning != nil, period.end != nil {
// Insert new period // Insert new period
periods.insert(period, at: index) periods.insert(period, at: index)
} else if period.beginning != nil && period.end != nil { } else if period.beginning != nil, period.end != nil {
// Insert new period // Insert new period
periods.insert(period, at: index) periods.insert(period, at: index)
} else { } else {
@ -86,7 +85,7 @@ open class TimePeriodChain: TimePeriodGroup {
// Shift all periods after inserted period // Shift all periods after inserted period
for i in 0 ..< periods.count { for i in 0 ..< periods.count {
if i > index && i > 0 { if i > index, i > 0 {
let currentPeriod = TimePeriod(beginning: period.beginning, end: period.end) let currentPeriod = TimePeriod(beginning: period.beginning, end: period.end)
periods[i].beginning = periods[i - 1].end periods[i].beginning = periods[i - 1].end
periods[i].end = periods[i].beginning!.addingTimeInterval(currentPeriod.duration) periods[i].end = periods[i].beginning!.addingTimeInterval(currentPeriod.duration)
@ -119,7 +118,7 @@ open class TimePeriodChain: TimePeriodGroup {
* Remove all periods from period array. * Remove all periods from period array.
*/ */
public func removeAll() { public func removeAll() {
self.periods.removeAll() periods.removeAll()
updateExtremes() updateExtremes()
} }
@ -131,7 +130,7 @@ open class TimePeriodChain: TimePeriodGroup {
* - parameter duration: The time interval to shift the period by * - parameter duration: The time interval to shift the period by
*/ */
public func shift(by duration: TimeInterval) { public func shift(by duration: TimeInterval) {
for var period in self.periods { for var period in periods {
period.shift(by: duration) period.shift(by: duration)
} }
@ -156,7 +155,7 @@ open class TimePeriodChain: TimePeriodGroup {
* *
*/ */
public func pop() -> TimePeriodProtocol? { public func pop() -> TimePeriodProtocol? {
let period = self.periods.popLast() let period = periods.popLast()
updateExtremes() updateExtremes()
return period return period

42
Clocker/Dependencies/Date Additions/TimePeriodCollection.swift

@ -17,7 +17,6 @@ import Foundation
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-collections) for more information. * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-collections) for more information.
*/ */
open class TimePeriodCollection: TimePeriodGroup { open class TimePeriodCollection: TimePeriodGroup {
// MARK: - Collection Manipulation // MARK: - Collection Manipulation
/** /**
@ -93,12 +92,12 @@ open class TimePeriodCollection: TimePeriodGroup {
* Sort periods array in place by beginning * Sort periods array in place by beginning
*/ */
public func sortByBeginning() { public func sortByBeginning() {
self.sort { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in sort { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in
if period1.beginning == nil && period2.beginning == nil { if period1.beginning == nil, period2.beginning == nil {
return false return false
} else if (period1.beginning == nil) { } else if period1.beginning == nil {
return true return true
} else if (period2.beginning == nil) { } else if period2.beginning == nil {
return false return false
} else { } else {
return period2.beginning! < period1.beginning! return period2.beginning! < period1.beginning!
@ -110,7 +109,7 @@ open class TimePeriodCollection: TimePeriodGroup {
* Sort periods array in place * Sort periods array in place
*/ */
public func sort(by areInIncreasingOrder: (TimePeriodProtocol, TimePeriodProtocol) -> Bool) { public func sort(by areInIncreasingOrder: (TimePeriodProtocol, TimePeriodProtocol) -> Bool) {
self.periods.sort(by: areInIncreasingOrder) periods.sort(by: areInIncreasingOrder)
} }
// New collection // New collection
@ -120,12 +119,12 @@ open class TimePeriodCollection: TimePeriodGroup {
* - returns: Collection with sorted periods * - returns: Collection with sorted periods
*/ */
public func sortedByBeginning() -> TimePeriodCollection { public func sortedByBeginning() -> TimePeriodCollection {
let array = self.periods.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in let array = periods.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in
if period1.beginning == nil && period2.beginning == nil { if period1.beginning == nil, period2.beginning == nil {
return false return false
} else if (period1.beginning == nil) { } else if period1.beginning == nil {
return true return true
} else if (period2.beginning == nil) { } else if period2.beginning == nil {
return false return false
} else { } else {
return period2.beginning! < period1.beginning! return period2.beginning! < period1.beginning!
@ -143,7 +142,7 @@ open class TimePeriodCollection: TimePeriodGroup {
*/ */
public func sorted(by areInIncreasingOrder: (TimePeriodProtocol, TimePeriodProtocol) -> Bool) -> TimePeriodCollection { public func sorted(by areInIncreasingOrder: (TimePeriodProtocol, TimePeriodProtocol) -> Bool) -> TimePeriodCollection {
let collection = TimePeriodCollection() let collection = TimePeriodCollection()
collection.append(self.periods.sorted(by: areInIncreasingOrder)) collection.append(periods.sorted(by: areInIncreasingOrder))
return collection return collection
} }
@ -161,9 +160,9 @@ open class TimePeriodCollection: TimePeriodGroup {
public func allInside(in period: TimePeriodProtocol) -> TimePeriodCollection { public func allInside(in period: TimePeriodProtocol) -> TimePeriodCollection {
let collection = TimePeriodCollection() let collection = TimePeriodCollection()
// Filter by period // Filter by period
collection.periods = self.periods.filter({ (timePeriod: TimePeriodProtocol) -> Bool in collection.periods = periods.filter { (timePeriod: TimePeriodProtocol) -> Bool in
return timePeriod.isInside(of: period) timePeriod.isInside(of: period)
}) }
return collection return collection
} }
@ -178,9 +177,9 @@ open class TimePeriodCollection: TimePeriodGroup {
public func periodsIntersected(by date: Date) -> TimePeriodCollection { public func periodsIntersected(by date: Date) -> TimePeriodCollection {
let collection = TimePeriodCollection() let collection = TimePeriodCollection()
// Filter by period // Filter by period
collection.periods = self.periods.filter({ (timePeriod: TimePeriodProtocol) -> Bool in collection.periods = periods.filter { (timePeriod: TimePeriodProtocol) -> Bool in
return timePeriod.contains(date, interval: .closed) timePeriod.contains(date, interval: .closed)
}) }
return collection return collection
} }
@ -195,9 +194,9 @@ open class TimePeriodCollection: TimePeriodGroup {
public func periodsIntersected(by period: TimePeriodProtocol) -> TimePeriodCollection { public func periodsIntersected(by period: TimePeriodProtocol) -> TimePeriodCollection {
let collection = TimePeriodCollection() let collection = TimePeriodCollection()
// Filter by periop // Filter by periop
collection.periods = self.periods.filter({ (timePeriod: TimePeriodProtocol) -> Bool in collection.periods = periods.filter { (timePeriod: TimePeriodProtocol) -> Bool in
return timePeriod.intersects(with: period) timePeriod.intersects(with: period)
}) }
return collection return collection
} }
@ -227,14 +226,13 @@ open class TimePeriodCollection: TimePeriodGroup {
internal func updateExtremes(period: TimePeriodProtocol) { internal func updateExtremes(period: TimePeriodProtocol) {
// Check incoming period against previous beginning and end date // Check incoming period against previous beginning and end date
if self.count == 1 { if count == 1 {
_beginning = period.beginning _beginning = period.beginning
_end = period.end _end = period.end
} else { } else {
_beginning = nilOrEarlier(date1: _beginning, date2: period.beginning) _beginning = nilOrEarlier(date1: _beginning, date2: period.beginning)
_end = nilOrLater(date1: _end, date2: period.end) _end = nilOrLater(date1: _end, date2: period.end)
} }
} }
internal func updateExtremes() { internal func updateExtremes() {

25
Clocker/Dependencies/Date Additions/TimePeriodGroup.swift

@ -16,7 +16,6 @@ import Foundation
* [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-groups) for more information. * [Visit our github page](https://github.com/MatthewYork/DateTools#time-period-groups) for more information.
*/ */
open class TimePeriodGroup: Sequence { open class TimePeriodGroup: Sequence {
// MARK: - Variables // MARK: - Variables
/** /**
@ -57,7 +56,7 @@ open class TimePeriodGroup: Sequence {
* periods array. Nil if any beginning or end date in any contained period is nil. * periods array. Nil if any beginning or end date in any contained period is nil.
*/ */
public var duration: TimeInterval? { public var duration: TimeInterval? {
if beginning != nil && end != nil { if beginning != nil, end != nil {
return end!.timeIntervalSince(beginning!) return end!.timeIntervalSince(beginning!)
} }
return nil return nil
@ -65,9 +64,7 @@ open class TimePeriodGroup: Sequence {
// MARK: - Initializers // MARK: - Initializers
public init() { public init() {}
}
// MARK: - Comparisons // MARK: - Comparisons
@ -79,12 +76,12 @@ open class TimePeriodGroup: Sequence {
* - returns: True if the periods arrays are the same * - returns: True if the periods arrays are the same
*/ */
public func equals(_ group: TimePeriodGroup) -> Bool { public func equals(_ group: TimePeriodGroup) -> Bool {
return containSameElements(array1: self.periods, group.periods) return containSameElements(array1: periods, group.periods)
} }
// MARK: - Sequence Protocol // MARK: - Sequence Protocol
public func makeIterator() -> IndexingIterator<Array<TimePeriodProtocol>> { public func makeIterator() -> IndexingIterator<[TimePeriodProtocol]> {
return periods.makeIterator() return periods.makeIterator()
} }
@ -105,10 +102,8 @@ open class TimePeriodGroup: Sequence {
} }
subscript(index: Int) -> TimePeriodProtocol { subscript(index: Int) -> TimePeriodProtocol {
get {
return periods[index] return periods[index]
} }
}
internal func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result { internal func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result {
return try periods.reduce(initialResult, nextPartialResult) return try periods.reduce(initialResult, nextPartialResult)
@ -120,22 +115,22 @@ open class TimePeriodGroup: Sequence {
} }
var compArray1: [TimePeriodProtocol] = array1.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in var compArray1: [TimePeriodProtocol] = array1.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in
if period1.beginning == nil && period2.beginning == nil { if period1.beginning == nil, period2.beginning == nil {
return false return false
} else if (period1.beginning == nil) { } else if period1.beginning == nil {
return true return true
} else if (period2.beginning == nil) { } else if period2.beginning == nil {
return false return false
} else { } else {
return period2.beginning! < period1.beginning! return period2.beginning! < period1.beginning!
} }
} }
var compArray2: [TimePeriodProtocol] = array2.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in var compArray2: [TimePeriodProtocol] = array2.sorted { (period1: TimePeriodProtocol, period2: TimePeriodProtocol) -> Bool in
if period1.beginning == nil && period2.beginning == nil { if period1.beginning == nil, period2.beginning == nil {
return false return false
} else if (period1.beginning == nil) { } else if period1.beginning == nil {
return true return true
} else if (period2.beginning == nil) { } else if period2.beginning == nil {
return false return false
} else { } else {
return period2.beginning! < period1.beginning! return period2.beginning! < period1.beginning!

34
Clocker/Dependencies/Solar.swift

@ -4,21 +4,20 @@ import Cocoa
import CoreLocation import CoreLocation
public struct Solar { public struct Solar {
/// The coordinate that is used for the calculation /// The coordinate that is used for the calculation
public let coordinate: CLLocationCoordinate2D public let coordinate: CLLocationCoordinate2D
/// The date to generate sunrise / sunset times for /// The date to generate sunrise / sunset times for
public fileprivate(set) var date: Date public private(set) var date: Date
public fileprivate(set) var sunrise: Date? public private(set) var sunrise: Date?
public fileprivate(set) var sunset: Date? public private(set) var sunset: Date?
public fileprivate(set) var civilSunrise: Date? public private(set) var civilSunrise: Date?
public fileprivate(set) var civilSunset: Date? public private(set) var civilSunset: Date?
public fileprivate(set) var nauticalSunrise: Date? public private(set) var nauticalSunrise: Date?
public fileprivate(set) var nauticalSunset: Date? public private(set) var nauticalSunset: Date?
public fileprivate(set) var astronomicalSunrise: Date? public private(set) var astronomicalSunrise: Date?
public fileprivate(set) var astronomicalSunset: Date? public private(set) var astronomicalSunset: Date?
// MARK: Init // MARK: Init
@ -52,20 +51,20 @@ public struct Solar {
// MARK: - Private functions // MARK: - Private functions
fileprivate enum SunriseSunset { private enum SunriseSunset {
case sunrise case sunrise
case sunset case sunset
} }
/// Used for generating several of the possible sunrise / sunset times /// Used for generating several of the possible sunrise / sunset times
fileprivate enum Zenith: Double { private enum Zenith: Double {
case official = 90.83 case official = 90.83
case civil = 96 case civil = 96
case nautical = 102 case nautical = 102
case astronimical = 108 case astronimical = 108
} }
fileprivate func calculate(_ sunriseSunset: SunriseSunset, for date: Date, and zenith: Zenith) -> Date? { private func calculate(_ sunriseSunset: SunriseSunset, for date: Date, and zenith: Zenith) -> Date? {
guard let utcTimezone = TimeZone(identifier: "UTC") else { return nil } guard let utcTimezone = TimeZone(identifier: "UTC") else { return nil }
// Get the day of the year // Get the day of the year
@ -147,7 +146,7 @@ public struct Solar {
if shouldBeYesterday { if shouldBeYesterday {
setDate = Date(timeInterval: -(60 * 60 * 24), since: date) setDate = Date(timeInterval: -(60 * 60 * 24), since: date)
} else if shouldBeTomorrow { } else if shouldBeTomorrow {
setDate = Date(timeInterval: (60 * 60 * 24), since: date) setDate = Date(timeInterval: 60 * 60 * 24, since: date)
} else { } else {
setDate = date setDate = date
} }
@ -162,7 +161,7 @@ public struct Solar {
} }
/// Normalises a value between 0 and `maximum`, by adding or subtracting `maximum` /// Normalises a value between 0 and `maximum`, by adding or subtracting `maximum`
fileprivate func normalise(_ value: Double, withMaximum maximum: Double) -> Double { private func normalise(_ value: Double, withMaximum maximum: Double) -> Double {
var value = value var value = value
if value < 0 { if value < 0 {
@ -175,11 +174,9 @@ public struct Solar {
return value return value
} }
} }
extension Solar { extension Solar {
/// Whether the location specified by the `latitude` and `longitude` is in daytime on `date` /// Whether the location specified by the `latitude` and `longitude` is in daytime on `date`
/// - Complexity: O(1) /// - Complexity: O(1)
public var isDaytime: Bool { public var isDaytime: Bool {
@ -192,7 +189,7 @@ extension Solar {
let beginningOfDay = sunrise.timeIntervalSince1970 let beginningOfDay = sunrise.timeIntervalSince1970
let endOfDay = sunset.timeIntervalSince1970 let endOfDay = sunset.timeIntervalSince1970
let currentTime = self.date.timeIntervalSince1970 let currentTime = date.timeIntervalSince1970
let isSunriseOrLater = currentTime >= beginningOfDay let isSunriseOrLater = currentTime >= beginningOfDay
let isBeforeSunset = currentTime < endOfDay let isBeforeSunset = currentTime < endOfDay
@ -205,7 +202,6 @@ extension Solar {
public var isNighttime: Bool { public var isNighttime: Bool {
return !isDaytime return !isDaytime
} }
} }
// MARK: - Helper extensions // MARK: - Helper extensions

15
Clocker/Events and Reminders/CalendarHandler.swift

@ -94,17 +94,17 @@ extension EventCenter {
let relevantEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date())] ?? [] let relevantEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date())] ?? []
let filteredEvent = relevantEvents.filter({ let filteredEvent = relevantEvents.filter {
$0.event.isAllDay == false && $0.event.startDate.timeIntervalSinceNow > 0 $0.event.isAllDay == false && $0.event.startDate.timeIntervalSinceNow > 0
}).first }.first
if let firstEvent = filteredEvent { if let firstEvent = filteredEvent {
return firstEvent.event return firstEvent.event
} }
let filteredAllDayEvent = relevantEvents.filter({ let filteredAllDayEvent = relevantEvents.filter {
$0.isAllDay $0.isAllDay
}).first }.first
return filteredAllDayEvent?.event return filteredAllDayEvent?.event
} }
@ -113,7 +113,7 @@ extension EventCenter {
store.requestAccess(to: entity) { [weak self] granted, _ in store.requestAccess(to: entity) { [weak self] granted, _ in
// On successful granting of calendar permission, we default to showing events from all calendars // On successful granting of calendar permission, we default to showing events from all calendars
if let `self` = self, entity == .event, granted { if let self = self, entity == .event, granted {
self.saveDefaultIdentifiersList() self.saveDefaultIdentifiersList()
} }
@ -149,7 +149,7 @@ extension EventCenter {
func saveDefaultIdentifiersList() { func saveDefaultIdentifiersList() {
OperationQueue.main.addOperation { [weak self] in OperationQueue.main.addOperation { [weak self] in
guard let `self` = self else { return } guard let self = self else { return }
let allCalendars = self.retrieveAllCalendarIdentifiers() let allCalendars = self.retrieveAllCalendarIdentifiers()
if !allCalendars.isEmpty { if !allCalendars.isEmpty {
@ -162,7 +162,7 @@ extension EventCenter {
func retrieveAllCalendarIdentifiers() -> [String] { func retrieveAllCalendarIdentifiers() -> [String] {
return store.calendars(for: .event).map { (calendar) -> String in return store.calendars(for: .event).map { (calendar) -> String in
return calendar.calendarIdentifier calendar.calendarIdentifier
} }
} }
@ -211,7 +211,6 @@ extension EventCenter {
// We map eachDate to array of events happening on that day // We map eachDate to array of events happening on that day
for event in events where shouldSkipEvent(event) == false { for event in events where shouldSkipEvent(event) == false {
// Iterate through the days this event spans. We only care about // Iterate through the days this event spans. We only care about
// days for this event that are between startDate and endDate // days for this event that are between startDate and endDate
let eventStartDate = event.startDate as NSDate let eventStartDate = event.startDate as NSDate

2
Clocker/Events and Reminders/EventCenter.swift

@ -29,7 +29,7 @@ class EventCenter: NSObject {
object: nil) object: nil)
} }
@objc func eventStoreDidChange(_ sender: Any) { @objc func eventStoreDidChange(_: Any) {
refetchAll() refetchAll()
} }

3
Clocker/Events and Reminders/RemindersHandler.swift

@ -3,7 +3,6 @@
import EventKit import EventKit
extension EventCenter { extension EventCenter {
// MARK: Private helper methods // MARK: Private helper methods
private func retrieveCalendar() -> EKCalendar? { private func retrieveCalendar() -> EKCalendar? {
@ -11,7 +10,7 @@ extension EventCenter {
let calendars = store.calendars(for: .reminder) let calendars = store.calendars(for: .reminder)
let calendarTitle = "Clocker Reminders" let calendarTitle = "Clocker Reminders"
let predicate = NSPredicate(format: "title matches %@", calendarTitle) let predicate = NSPredicate(format: "title matches %@", calendarTitle)
let filtered = calendars.filter({ predicate.evaluate(with: $0) }) let filtered = calendars.filter { predicate.evaluate(with: $0) }
if !filtered.isEmpty { if !filtered.isEmpty {
calendar = filtered.first calendar = filtered.first

12
Clocker/Menu Bar/MenubarHandler.swift

@ -5,7 +5,6 @@ import EventKit
class MenubarHandler: NSObject { class MenubarHandler: NSObject {
@objc func titleForMenubar() -> String? { @objc func titleForMenubar() -> String? {
if let nextEvent = checkForUpcomingEvents() { if let nextEvent = checkForUpcomingEvents() {
return nextEvent return nextEvent
} }
@ -20,11 +19,11 @@ class MenubarHandler: NSObject {
} }
if menubarTitles.isEmpty == false { if menubarTitles.isEmpty == false {
let titles = menubarTitles.map({ (data) -> String? in let titles = menubarTitles.map { (data) -> String? in
let timezone = TimezoneData.customObject(from: data) let timezone = TimezoneData.customObject(from: data)
let operationsObject = TimezoneDataOperations(with: timezone!) let operationsObject = TimezoneDataOperations(with: timezone!)
return "\(operationsObject.menuTitle().trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines))" return "\(operationsObject.menuTitle().trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines))"
}) }
let titlesStringified = titles.compactMap { $0 } let titlesStringified = titles.compactMap { $0 }
return titlesStringified.joined(separator: " ") return titlesStringified.joined(separator: " ")
@ -34,9 +33,7 @@ class MenubarHandler: NSObject {
} }
private func checkForUpcomingEvents() -> String? { private func checkForUpcomingEvents() -> String? {
if DataStore.shared().shouldDisplay(.showMeetingInMenubar) { if DataStore.shared().shouldDisplay(.showMeetingInMenubar) {
let filteredDates = EventCenter.sharedCenter().eventsForDate let filteredDates = EventCenter.sharedCenter().eventsForDate
let autoupdatingCal = EventCenter.sharedCenter().autoupdatingCalendar let autoupdatingCal = EventCenter.sharedCenter().autoupdatingCalendar
guard let events = filteredDates[autoupdatingCal.startOfDay(for: Date())] else { guard let events = filteredDates[autoupdatingCal.startOfDay(for: Date())] else {
@ -44,13 +41,10 @@ class MenubarHandler: NSObject {
} }
for event in events { for event in events {
if event.event.startDate.timeIntervalSinceNow > 0, !event.isAllDay {
if event.event.startDate.timeIntervalSinceNow > 0 && !event.isAllDay {
let timeForEventToStart = event.event.startDate.timeIntervalSinceNow / 60 let timeForEventToStart = event.event.startDate.timeIntervalSinceNow / 60
if timeForEventToStart > 30 { if timeForEventToStart > 30 {
print("Our next event: \(event.event.title ?? "Error") starts in \(timeForEventToStart) mins") print("Our next event: \(event.event.title ?? "Error") starts in \(timeForEventToStart) mins")
continue continue

8
Clocker/Menu Bar/StatusContainerView.swift

@ -21,7 +21,6 @@ func bufferCalculatedWidth() -> Int {
} }
func compactWidth(for timezone: TimezoneData) -> Int { func compactWidth(for timezone: TimezoneData) -> Int {
var totalWidth = 55 var totalWidth = 55
let timeFormat = timezone.timezoneFormat() let timeFormat = timezone.timezoneFormat()
@ -47,7 +46,6 @@ func compactWidth(for timezone: TimezoneData) -> Int {
let bufferWidth: CGFloat = 9.5 let bufferWidth: CGFloat = 9.5
class StatusContainerView: NSView { class StatusContainerView: NSView {
private var previousX: Int = 0 private var previousX: Int = 0
override func awakeFromNib() { override func awakeFromNib() {
@ -57,7 +55,6 @@ class StatusContainerView: NSView {
} }
init(with timezones: [Data]) { init(with timezones: [Data]) {
func addSubviews() { func addSubviews() {
timezones.forEach { timezones.forEach {
if let timezoneObject = TimezoneData.customObject(from: $0) { if let timezoneObject = TimezoneData.customObject(from: $0) {
@ -91,12 +88,11 @@ class StatusContainerView: NSView {
addSubviews() addSubviews()
} }
required init?(coder decoder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func addTimezone(_ timezone: TimezoneData) { func addTimezone(_ timezone: TimezoneData) {
let calculatedWidth = bestWidth(for: timezone) let calculatedWidth = bestWidth(for: timezone)
let frame = NSRect(x: previousX, y: 0, width: calculatedWidth, height: 30) let frame = NSRect(x: previousX, y: 0, width: calculatedWidth, height: 30)
@ -118,7 +114,6 @@ class StatusContainerView: NSView {
} }
func updateTime() { func updateTime() {
if subviews.isEmpty { if subviews.isEmpty {
assertionFailure("Subviews count should > 0") assertionFailure("Subviews count should > 0")
} }
@ -129,5 +124,4 @@ class StatusContainerView: NSView {
} }
} }
} }
} }

5
Clocker/Menu Bar/StatusItemHandler.swift

@ -9,7 +9,6 @@ private enum MenubarState {
} }
class StatusItemHandler: NSObject { class StatusItemHandler: NSObject {
var hasActiveIcon: Bool = false var hasActiveIcon: Bool = false
var menubarTimer: Timer? var menubarTimer: Timer?
@ -219,7 +218,7 @@ class StatusItemHandler: NSObject {
let shouldDisplaySeconds = shouldDisplaySecondsInMenubar() let shouldDisplaySeconds = shouldDisplaySecondsInMenubar()
let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites)
if !units.contains(.second) && shouldDisplaySeconds { if !units.contains(.second), shouldDisplaySeconds {
units.insert(.second) units.insert(.second)
} }
@ -281,7 +280,7 @@ class StatusItemHandler: NSObject {
let menubarFavourites = (DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data]) ?? [] let menubarFavourites = (DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data]) ?? []
if menubarFavourites.isEmpty && DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false { if menubarFavourites.isEmpty, DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false {
print("Invalidating menubar timer!") print("Invalidating menubar timer!")
invalidation() invalidation()

15
Clocker/Menu Bar/StatusItemView.swift

@ -14,26 +14,26 @@ var compactModeTimeFont: NSFont {
} }
var timeAttributes: [NSAttributedString.Key: AnyObject] { var timeAttributes: [NSAttributedString.Key: AnyObject] {
let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black
let attributes = [ let attributes = [
NSAttributedString.Key.font: compactModeTimeFont, NSAttributedString.Key.font: compactModeTimeFont,
NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.backgroundColor: NSColor.clear, NSAttributedString.Key.backgroundColor: NSColor.clear,
NSAttributedString.Key.paragraphStyle: defaultParagraphStyle NSAttributedString.Key.paragraphStyle: defaultParagraphStyle,
] ]
return attributes return attributes
} }
class StatusItemView: NSView { class StatusItemView: NSView {
// MARK: Private variables // MARK: Private variables
private let locationView: NSTextField = NSTextField(labelWithString: "Hello") private let locationView: NSTextField = NSTextField(labelWithString: "Hello")
private let timeView: NSTextField = NSTextField(labelWithString: "Mon 19:14 PM") private let timeView: NSTextField = NSTextField(labelWithString: "Mon 19:14 PM")
private var operationsObject: TimezoneDataOperations { private var operationsObject: TimezoneDataOperations {
return TimezoneDataOperations(with: dataObject) return TimezoneDataOperations(with: dataObject)
} }
private var textFontAttributes: [NSAttributedString.Key: Any] { private var textFontAttributes: [NSAttributedString.Key: Any] {
let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black let textColor = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? NSColor.white : NSColor.black
@ -41,12 +41,13 @@ class StatusItemView: NSView {
NSAttributedString.Key.font: NSFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.font: NSFont.boldSystemFont(ofSize: 10),
NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.backgroundColor: NSColor.clear, NSAttributedString.Key.backgroundColor: NSColor.clear,
NSAttributedString.Key.paragraphStyle: defaultParagraphStyle NSAttributedString.Key.paragraphStyle: defaultParagraphStyle,
] ]
return textFontAttributes return textFontAttributes
} }
// MARK: Public // MARK: Public
var dataObject: TimezoneData! { var dataObject: TimezoneData! {
didSet { didSet {
initialSetup() initialSetup()
@ -69,14 +70,14 @@ class StatusItemView: NSView {
locationView.leadingAnchor.constraint(equalTo: leadingAnchor), locationView.leadingAnchor.constraint(equalTo: leadingAnchor),
locationView.trailingAnchor.constraint(equalTo: trailingAnchor), locationView.trailingAnchor.constraint(equalTo: trailingAnchor),
locationView.topAnchor.constraint(equalTo: topAnchor, constant: 7), locationView.topAnchor.constraint(equalTo: topAnchor, constant: 7),
locationView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.35) locationView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.35),
]) ])
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
timeView.leadingAnchor.constraint(equalTo: leadingAnchor), timeView.leadingAnchor.constraint(equalTo: leadingAnchor),
timeView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0), timeView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0),
timeView.topAnchor.constraint(equalTo: locationView.bottomAnchor), timeView.topAnchor.constraint(equalTo: locationView.bottomAnchor),
timeView.bottomAnchor.constraint(equalTo: bottomAnchor) timeView.bottomAnchor.constraint(equalTo: bottomAnchor),
]) ])
} }
@ -89,7 +90,7 @@ class StatusItemView: NSView {
timeView.attributedStringValue = NSAttributedString(string: operationsObject.compactMenuHeader(), attributes: timeAttributes) timeView.attributedStringValue = NSAttributedString(string: operationsObject.compactMenuHeader(), attributes: timeAttributes)
} }
required init?(coder decoder: NSCoder) { required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }

6
Clocker/Onboarding/OnboardingParentViewController.swift

@ -58,7 +58,6 @@ class OnboardingParentViewController: NSViewController {
} }
private func setupUI() { private func setupUI() {
setIdentifiersForTests() setIdentifiersForTests()
positiveButton.title = "Get Started" positiveButton.title = "Get Started"
@ -178,11 +177,10 @@ class OnboardingParentViewController: NSViewController {
self.positiveButton.tag = OnboardingType.final.rawValue self.positiveButton.tag = OnboardingType.final.rawValue
self.positiveButton.title = "Launch Clocker" self.positiveButton.title = "Launch Clocker"
} }
} }
private func performFinalStepsBeforeFinishing() { private func performFinalStepsBeforeFinishing() {
self.positiveButton.tag = OnboardingType.complete.rawValue positiveButton.tag = OnboardingType.complete.rawValue
// Install the menubar option! // Install the menubar option!
let appDelegate = NSApplication.shared.delegate as? AppDelegate let appDelegate = NSApplication.shared.delegate as? AppDelegate
@ -279,7 +277,6 @@ class OnboardingParentViewController: NSViewController {
} }
private func shouldStartAtLogin(_ shouldStart: Bool) { private func shouldStartAtLogin(_ shouldStart: Bool) {
// If tests are going on, we don't want to enable/disable launch at login! // If tests are going on, we don't want to enable/disable launch at login!
if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) { if ProcessInfo.processInfo.arguments.contains(CLOnboaringTestsLaunchArgument) {
return return
@ -307,7 +304,6 @@ class OnboardingParentViewController: NSViewController {
} }
private func currentController() -> [String: String] { private func currentController() -> [String: String] {
switch positiveButton.tag { switch positiveButton.tag {
case 0: case 0:
return ["Onboarding Process Interrupted": "Welcome View"] return ["Onboarding Process Interrupted": "Welcome View"]

3
Clocker/Onboarding/OnboardingPermissionsViewController.swift

@ -91,7 +91,7 @@ class OnboardingPermissionsViewController: NSViewController {
eventCenter.requestAccess(to: .event, completionHandler: { [weak self] granted in eventCenter.requestAccess(to: .event, completionHandler: { [weak self] granted in
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
guard let `self` = self else { return } guard let self = self else { return }
self.calendarActivityIndicator.stopAnimation(nil) self.calendarActivityIndicator.stopAnimation(nil)
@ -130,7 +130,6 @@ class OnboardingPermissionsViewController: NSViewController {
if granted { if granted {
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
self.view.window?.orderBack(nil) self.view.window?.orderBack(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)

30
Clocker/Onboarding/OnboardingSearchController.swift

@ -14,7 +14,7 @@ class OnboardingSearchController: NSViewController {
@IBOutlet private var searchBar: ClockerSearchField! @IBOutlet private var searchBar: ClockerSearchField!
@IBOutlet private var resultsTableView: NSTableView! @IBOutlet private var resultsTableView: NSTableView!
@IBOutlet private var accessoryLabel: NSTextField! @IBOutlet private var accessoryLabel: NSTextField!
@IBOutlet weak var undoButton: NSButton! @IBOutlet var undoButton: NSButton!
private var results: [TimezoneData] = [] private var results: [TimezoneData] = []
private var dataTask: URLSessionDataTask? = .none private var dataTask: URLSessionDataTask? = .none
@ -57,7 +57,7 @@ class OnboardingSearchController: NSViewController {
@objc func doubleClickAction(_: NSTableView?) { @objc func doubleClickAction(_: NSTableView?) {
[accessoryLabel].forEach { $0?.isHidden = false } [accessoryLabel].forEach { $0?.isHidden = false }
if resultsTableView.selectedRow >= 0 && resultsTableView.selectedRow < results.count { if resultsTableView.selectedRow >= 0, resultsTableView.selectedRow < results.count {
let selectedTimezone = results[resultsTableView.selectedRow] let selectedTimezone = results[resultsTableView.selectedRow]
addTimezoneToDefaults(selectedTimezone) addTimezoneToDefaults(selectedTimezone)
@ -65,7 +65,6 @@ class OnboardingSearchController: NSViewController {
} }
private func addTimezoneToDefaults(_ timezone: TimezoneData) { private func addTimezoneToDefaults(_ timezone: TimezoneData) {
func setupLabelHidingTimer() { func setupLabelHidingTimer() {
Timer.scheduledTimer(withTimeInterval: 5, Timer.scheduledTimer(withTimeInterval: 5,
repeats: false) { _ in repeats: false) { _ in
@ -104,7 +103,6 @@ class OnboardingSearchController: NSViewController {
/// Returns true if there's an error. /// Returns true if there's an error.
private func handleEdgeCase(for response: Data?) -> Bool { private func handleEdgeCase(for response: Data?) -> Bool {
func setErrorPlaceholders() { func setErrorPlaceholders() {
setInfoLabel("No timezone found! Try entering an exact name.") setInfoLabel("No timezone found! Try entering an exact name.")
searchBar.placeholderString = placeholders.randomElement() searchBar.placeholderString = placeholders.randomElement()
@ -150,7 +148,7 @@ class OnboardingSearchController: NSViewController {
NetworkManager.task(with: urlString) { [weak self] response, error in NetworkManager.task(with: urlString) { [weak self] response, error in
guard let `self` = self else { return } guard let self = self else { return }
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
if self.handleEdgeCase(for: response) == true { if self.handleEdgeCase(for: response) == true {
@ -158,7 +156,7 @@ class OnboardingSearchController: NSViewController {
} }
if error == nil, let json = response, let response = self.decodeTimezone(from: json) { if error == nil, let json = response, let response = self.decodeTimezone(from: json) {
if self.resultsTableView.selectedRow >= 0 && self.resultsTableView.selectedRow < self.results.count { if self.resultsTableView.selectedRow >= 0, self.resultsTableView.selectedRow < self.results.count {
var filteredAddress = "Error" var filteredAddress = "Error"
if let address = dataObject.formattedAddress { if let address = dataObject.formattedAddress {
@ -172,7 +170,7 @@ class OnboardingSearchController: NSViewController {
"latitude": latitude, "latitude": latitude,
"longitude": longitude, "longitude": longitude,
"nextUpdate": CLEmptyString, "nextUpdate": CLEmptyString,
CLCustomLabel: filteredAddress CLCustomLabel: filteredAddress,
] as [String: Any] ] as [String: Any]
DataStore.shared().addTimezone(TimezoneData(with: newTimeZone)) DataStore.shared().addTimezone(TimezoneData(with: newTimeZone))
@ -222,7 +220,6 @@ class OnboardingSearchController: NSViewController {
} }
@IBAction func search(_ sender: NSSearchField) { @IBAction func search(_ sender: NSSearchField) {
resultsTableView.deselectAll(nil) resultsTableView.deselectAll(nil)
let searchString = sender.stringValue let searchString = sender.stringValue
@ -249,9 +246,8 @@ class OnboardingSearchController: NSViewController {
} }
@objc func actualSearch() { @objc func actualSearch() {
func setupForError() { func setupForError() {
self.resultsTableView.isHidden = true resultsTableView.isHidden = true
} }
let userPreferredLanguage = Locale.preferredLanguages.first ?? "en-US" let userPreferredLanguage = Locale.preferredLanguages.first ?? "en-US"
@ -267,10 +263,9 @@ class OnboardingSearchController: NSViewController {
dataTask = NetworkManager.task(with: urlString, dataTask = NetworkManager.task(with: urlString,
completionHandler: { [weak self] response, error in completionHandler: { [weak self] response, error in
guard let `self` = self else { return } guard let self = self else { return }
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
print("Search string was: \(searchString)") print("Search string was: \(searchString)")
let currentSearchBarValue = self.searchBar.stringValue let currentSearchBarValue = self.searchBar.stringValue
@ -314,9 +309,9 @@ class OnboardingSearchController: NSViewController {
private func presentErrorMessage(_ errorMessage: String) { private func presentErrorMessage(_ errorMessage: String) {
if errorMessage == PreferencesConstants.offlineErrorMessage { if errorMessage == PreferencesConstants.offlineErrorMessage {
self.setInfoLabel(PreferencesConstants.noInternetConnectivityError) setInfoLabel(PreferencesConstants.noInternetConnectivityError)
} else { } else {
self.setInfoLabel(PreferencesConstants.tryAgainMessage) setInfoLabel(PreferencesConstants.tryAgainMessage)
} }
} }
@ -333,7 +328,7 @@ class OnboardingSearchController: NSViewController {
CLTimezoneName: formattedAddress, CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress, CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString, CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: $0.placeId CLPlaceIdentifier: $0.placeId,
] as [String: Any] ] as [String: Any]
self.results.append(TimezoneData(with: totalPackage)) self.results.append(TimezoneData(with: totalPackage))
@ -359,11 +354,10 @@ class OnboardingSearchController: NSViewController {
searchBar.placeholderString = placeholders.randomElement() searchBar.placeholderString = placeholders.randomElement()
} }
@IBAction func undoAction(_ sender: Any) { @IBAction func undoAction(_: Any) {
DataStore.shared().removeLastTimezone() DataStore.shared().removeLastTimezone()
setInfoLabel("Removed.") setInfoLabel("Removed.")
} }
} }
extension OnboardingSearchController: NSTableViewDataSource { extension OnboardingSearchController: NSTableViewDataSource {
@ -385,7 +379,7 @@ extension OnboardingSearchController: NSTableViewDataSource {
extension OnboardingSearchController: NSTableViewDelegate { extension OnboardingSearchController: NSTableViewDelegate {
func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat { func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat {
if row == 0 && results.isEmpty { if row == 0, results.isEmpty {
return 30 return 30
} }

9
Clocker/Overall App/AppDefaults.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
class AppDefaults { class AppDefaults {
class func initialize() { class func initialize() {
initializeDefaults() initializeDefaults()
} }
@ -19,7 +18,6 @@ class AppDefaults {
} }
private class func initializeDefaults() { private class func initializeDefaults() {
let userDefaults = UserDefaults.standard let userDefaults = UserDefaults.standard
let menubarFavourites = userDefaults.object(forKey: CLMenubarFavorites) let menubarFavourites = userDefaults.object(forKey: CLMenubarFavorites)
@ -43,7 +41,6 @@ class AppDefaults {
// If we already have timezones to display in menubar, do nothing. // If we already have timezones to display in menubar, do nothing.
// Else, we switch the menubar mode default to compact mode for new users // Else, we switch the menubar mode default to compact mode for new users
if userDefaults.bool(forKey: CLDefaultMenubarMode) == false { if userDefaults.bool(forKey: CLDefaultMenubarMode) == false {
if let menubarFavourites = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data], menubarFavourites.isEmpty == false { if let menubarFavourites = userDefaults.object(forKey: CLDefaultPreferenceKey) as? [Data], menubarFavourites.isEmpty == false {
userDefaults.set(1, forKey: CLMenubarCompactMode) userDefaults.set(1, forKey: CLMenubarCompactMode)
} else { } else {
@ -54,11 +51,9 @@ class AppDefaults {
} }
if userDefaults.bool(forKey: CLSwitchToCompactModeAlert) == false { if userDefaults.bool(forKey: CLSwitchToCompactModeAlert) == false {
userDefaults.set(true, forKey: CLSwitchToCompactModeAlert) userDefaults.set(true, forKey: CLSwitchToCompactModeAlert)
if let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data], menubarFavourites.count > 1 { if let menubarFavourites = DataStore.shared().retrieve(key: CLMenubarFavorites) as? [Data], menubarFavourites.count > 1 {
// If the user is already using the compact mode, abort. // If the user is already using the compact mode, abort.
if DataStore.shared().shouldDisplay(.menubarCompactMode) { if DataStore.shared().shouldDisplay(.menubarCompactMode) {
return return
@ -78,7 +73,6 @@ class AppDefaults {
// Set default to System theme for Mojave // Set default to System theme for Mojave
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
if defaults.bool(forKey: CLDefaultThemeOnMojave) == false { if defaults.bool(forKey: CLDefaultThemeOnMojave) == false {
if isDarkModeOn() { if isDarkModeOn() {
Themer.shared().set(theme: 2) Themer.shared().set(theme: 2)
@ -139,16 +133,13 @@ class AppDefaults {
CLAppDislayOptions: 0, CLAppDislayOptions: 0,
CLMenubarCompactMode: 1] CLMenubarCompactMode: 1]
} }
} }
extension UserDefaults { extension UserDefaults {
func wipeIfNeccesary() { func wipeIfNeccesary() {
if let bundleID = Bundle.main.bundleIdentifier, object(forKey: "PreferencesHaveBeenWiped") == nil { if let bundleID = Bundle.main.bundleIdentifier, object(forKey: "PreferencesHaveBeenWiped") == nil {
removePersistentDomain(forName: bundleID) removePersistentDomain(forName: bundleID)
set(true, forKey: "PreferencesHaveBeenWiped") set(true, forKey: "PreferencesHaveBeenWiped")
} }
} }
} }

6
Clocker/Overall App/AppKit + Additions.swift

@ -1,7 +1,6 @@
// Copyright © 2015 Abhishek Banthia // Copyright © 2015 Abhishek Banthia
extension NSTextField { extension NSTextField {
func applyDefaultStyle() { func applyDefaultStyle() {
backgroundColor = NSColor.clear backgroundColor = NSColor.clear
isEditable = false isEditable = false
@ -19,24 +18,21 @@ extension NSTextField {
cell?.wraps = false cell?.wraps = false
cell?.isScrollable = true cell?.isScrollable = true
} }
} }
extension NSFont { extension NSFont {
func size(_ string: String, _ width: Double, attributes: [NSAttributedString.Key: AnyObject]) -> CGSize { func size(_ string: String, _ width: Double, attributes: [NSAttributedString.Key: AnyObject]) -> CGSize {
let size = CGSize(width: width, let size = CGSize(width: width,
height: Double.greatestFiniteMagnitude) height: Double.greatestFiniteMagnitude)
var otherAttributes: [NSAttributedString.Key: AnyObject] = [NSAttributedString.Key.font: self] var otherAttributes: [NSAttributedString.Key: AnyObject] = [NSAttributedString.Key.font: self]
attributes.forEach { (arg) in let (key, value) = arg; otherAttributes[key] = value } attributes.forEach { arg in let (key, value) = arg; otherAttributes[key] = value }
return NSString(string: string).boundingRect(with: size, return NSString(string: string).boundingRect(with: size,
options: NSString.DrawingOptions.usesLineFragmentOrigin, options: NSString.DrawingOptions.usesLineFragmentOrigin,
attributes: attributes).size attributes: attributes).size
} }
} }
class ClockerSearchField: NSSearchField { class ClockerSearchField: NSSearchField {

2
Clocker/Overall App/DateFormatterManager.swift

@ -46,7 +46,7 @@ class DateFormatterManager: NSObject {
return specializedFormatter return specializedFormatter
} }
@objc class func localizedFormatter(with format: String, for timezoneIdentifier: String, locale: Locale = Locale.autoupdatingCurrent) -> DateFormatter { @objc class func localizedFormatter(with format: String, for timezoneIdentifier: String, locale _: Locale = Locale.autoupdatingCurrent) -> DateFormatter {
dateFormatter.dateStyle = .none dateFormatter.dateStyle = .none
dateFormatter.timeStyle = .none dateFormatter.timeStyle = .none
dateFormatter.locale = Locale.autoupdatingCurrent dateFormatter.locale = Locale.autoupdatingCurrent

2
Clocker/Overall App/Reach.swift

@ -76,7 +76,7 @@ extension ReachabilityStatus {
let connectionRequired = flags.contains(.connectionRequired) let connectionRequired = flags.contains(.connectionRequired)
let isReachable = flags.contains(.reachable) let isReachable = flags.contains(.reachable)
if !connectionRequired && isReachable { if !connectionRequired, isReachable {
self = .online(.wiFi) self = .online(.wiFi)
} else { } else {
self = .offline self = .offline

5
Clocker/Overall App/Themer.swift

@ -76,7 +76,6 @@ extension Themer {
} }
setAppAppearance() setAppAppearance()
} }
@objc func respondToInterfaceStyle() { @objc func respondToInterfaceStyle() {
@ -315,7 +314,6 @@ extension Themer {
} }
func currentLocationImage() -> NSImage { func currentLocationImage() -> NSImage {
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
switch themeIndex { switch themeIndex {
case .light: case .light:
@ -365,7 +363,6 @@ extension Themer {
} }
func privacyTabImage() -> NSImage { func privacyTabImage() -> NSImage {
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
switch themeIndex { switch themeIndex {
case .light: case .light:
@ -381,7 +378,6 @@ extension Themer {
} }
func appearanceTabImage() -> NSImage { func appearanceTabImage() -> NSImage {
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
switch themeIndex { switch themeIndex {
case .light: case .light:
@ -397,7 +393,6 @@ extension Themer {
} }
func calendarTabImage() -> NSImage { func calendarTabImage() -> NSImage {
if #available(macOS 10.14, *) { if #available(macOS 10.14, *) {
switch themeIndex { switch themeIndex {
case .light: case .light:

4
Clocker/Overall App/Timer.swift

@ -126,7 +126,7 @@ open class Repeater: Equatable {
/// Current state of the timer /// Current state of the timer
public private(set) var state: State = .paused { public private(set) var state: State = .paused {
didSet { didSet {
self.onStateChanged?(self, state) onStateChanged?(self, state)
} }
} }
@ -334,7 +334,7 @@ open class Repeater: Equatable {
/// Pause a running timer. If timer is paused it does nothing. /// Pause a running timer. If timer is paused it does nothing.
@discardableResult @discardableResult
public func pause() -> Bool { public func pause() -> Bool {
guard state != .paused && state != .finished else { guard state != .paused, state != .finished else {
return false return false
} }

1
Clocker/Overall App/UserDefaults + KVOExtensions.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
extension UserDefaults { extension UserDefaults {
@objc dynamic var displayFutureSlider: Int { @objc dynamic var displayFutureSlider: Int {
return integer(forKey: CLDisplayFutureSliderKey) return integer(forKey: CLDisplayFutureSliderKey)
} }

7
Clocker/Panel/Data Layer/TimezoneData.swift

@ -12,7 +12,6 @@ struct DateFormat {
// Non-class type cannot conform to NSCoding! // Non-class type cannot conform to NSCoding!
class TimezoneData: NSObject, NSCoding { class TimezoneData: NSObject, NSCoding {
enum SelectionType: Int { enum SelectionType: Int {
case city case city
case timezone case timezone
@ -202,7 +201,7 @@ class TimezoneData: NSObject, NSCoding {
let feedbackInfo = [ let feedbackInfo = [
AppFeedbackConstants.CLOperatingSystemVersion: osVersion, AppFeedbackConstants.CLOperatingSystemVersion: osVersion,
AppFeedbackConstants.CLClockerVersion: versionInfo AppFeedbackConstants.CLClockerVersion: versionInfo,
] ]
Logger.log(object: feedbackInfo, for: "CLTimezoneData is still being used!") Logger.log(object: feedbackInfo, for: "CLTimezoneData is still being used!")
@ -256,7 +255,7 @@ class TimezoneData: NSObject, NSCoding {
// Do the serialization // Do the serialization
let serializedModels = newModels.map { (place) -> Data in let serializedModels = newModels.map { (place) -> Data in
return NSKeyedArchiver.archivedData(withRootObject: place) NSKeyedArchiver.archivedData(withRootObject: place)
} }
return serializedModels return serializedModels
@ -361,7 +360,7 @@ class TimezoneData: NSObject, NSCoding {
let errorDictionary = [ let errorDictionary = [
"Formatted Address": name, "Formatted Address": name,
"Place Identifier": placeIdentifier, "Place Identifier": placeIdentifier,
"TimezoneID": timezoneIdentifier "TimezoneID": timezoneIdentifier,
] ]
Logger.log(object: errorDictionary, for: "Error fetching timezone() in TimezoneData") Logger.log(object: errorDictionary, for: "Error fetching timezone() in TimezoneData")

5
Clocker/Panel/Data Layer/TimezoneDataOperations.swift

@ -33,7 +33,6 @@ extension TimezoneDataOperations {
} }
func compactMenuHeader() -> String { func compactMenuHeader() -> String {
var subtitle = CLEmptyString var subtitle = CLEmptyString
let shouldDayBeShown = DataStore.shared().shouldShowDateInMenubar() let shouldDayBeShown = DataStore.shared().shouldShowDateInMenubar()
@ -136,10 +135,8 @@ extension TimezoneDataOperations {
} }
if displayType == CLDateDisplayType.panelDisplay { if displayType == CLDateDisplayType.panelDisplay {
// Yesterday, tomorrow, etc // Yesterday, tomorrow, etc
if relativeDayPreference.intValue == 0 { if relativeDayPreference.intValue == 0 {
let localFormatter = DateFormatterManager.localizedSimpleFormatter("EEEE") let localFormatter = DateFormatterManager.localizedSimpleFormatter("EEEE")
let local = localFormatter.date(from: localeDate(with: "EEEE")) let local = localFormatter.date(from: localeDate(with: "EEEE"))
@ -207,7 +204,7 @@ extension TimezoneDataOperations {
let unableToConvertDateParameters = [ let unableToConvertDateParameters = [
"New Date": newDate, "New Date": newDate,
"Timezone": dataObject.timezone(), "Timezone": dataObject.timezone(),
"Locale": dateFormatter.locale.identifier "Locale": dateFormatter.locale.identifier,
] as [String: Any] ] as [String: Any]
Logger.log(object: unableToConvertDateParameters, for: "Date conversion failure - New Date is nil") Logger.log(object: unableToConvertDateParameters, for: "Date conversion failure - New Date is nil")
return CLEmptyString return CLEmptyString

2
Clocker/Panel/FloatingWindowController.swift

@ -55,7 +55,7 @@ class FloatingWindowController: ParentPanelController {
target.image = Themer.shared().extraOptionsHighlightedImage() target.image = Themer.shared().extraOptionsHighlightedImage()
if popover.isShown && row == previousPopoverRow { if popover.isShown, row == previousPopoverRow {
popover.close() popover.close()
target.image = Themer.shared().extraOptionsImage() target.image = Themer.shared().extraOptionsImage()
previousPopoverRow = -1 previousPopoverRow = -1

8
Clocker/Panel/Notes Popover/NotesPopover.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
class NotesPopover: NSViewController { class NotesPopover: NSViewController {
private enum OverrideType { private enum OverrideType {
case timezoneFormat case timezoneFormat
case seconds case seconds
@ -37,7 +36,7 @@ class NotesPopover: NSViewController {
@IBOutlet var timeFormatControl: NSSegmentedControl! @IBOutlet var timeFormatControl: NSSegmentedControl!
@IBOutlet weak var secondsFormatControl: NSSegmentedControl! @IBOutlet var secondsFormatControl: NSSegmentedControl!
@IBOutlet var notesTextView: TextViewWithPlaceholder! @IBOutlet var notesTextView: TextViewWithPlaceholder!
@ -62,7 +61,7 @@ class NotesPopover: NSViewController {
"1 hour before", "1 hour before",
"2 hour before", "2 hour before",
"1 day before", "1 day before",
"2 days before" "2 days before",
] ]
alertPopupButton.removeAllItems() alertPopupButton.removeAllItems()
@ -200,7 +199,7 @@ class NotesPopover: NSViewController {
let attributesDictionary = [ let attributesDictionary = [
NSAttributedString.Key.font: font, NSAttributedString.Key.font: font,
NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(), NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(),
NSAttributedString.Key.paragraphStyle: style NSAttributedString.Key.paragraphStyle: style,
] ]
button.attributedTitle = NSAttributedString(string: title, button.attributedTitle = NSAttributedString(string: title,
@ -448,7 +447,6 @@ class NotesPopover: NSViewController {
handler.setupStatusItem() handler.setupStatusItem()
} }
} }
} }
@objc extension NotesPopover { @objc extension NotesPopover {

4
Clocker/Panel/Notes Popover/TextViewWithPlaceholder.swift

@ -18,7 +18,7 @@ class TextViewWithPlaceholder: NSTextView {
if let placeHolderFont = NSFont(name: "Avenir", size: 14) { if let placeHolderFont = NSFont(name: "Avenir", size: 14) {
let textDict = [ let textDict = [
NSAttributedString.Key.foregroundColor: NSColor.gray, NSAttributedString.Key.foregroundColor: NSColor.gray,
NSAttributedString.Key.font: placeHolderFont NSAttributedString.Key.font: placeHolderFont,
] ]
return NSAttributedString(string: " Add your notes here.", attributes: textDict) return NSAttributedString(string: " Add your notes here.", attributes: textDict)
} }
@ -32,7 +32,7 @@ class TextViewWithPlaceholder: NSTextView {
override func draw(_ dirtyRect: NSRect) { override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect) super.draw(dirtyRect)
if string == CLEmptyString && self != window?.firstResponder { if string == CLEmptyString, self != window?.firstResponder {
placeholder.draw(at: NSPoint(x: 0, y: 0)) placeholder.draw(at: NSPoint(x: 0, y: 0))
} }
} }

11
Clocker/Panel/PanelController.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
class PanelController: ParentPanelController { class PanelController: ParentPanelController {
@objc dynamic var hasActivePanel: Bool = false @objc dynamic var hasActivePanel: Bool = false
static var sharedWindow = PanelController(windowNibName: .panel) static var sharedWindow = PanelController(windowNibName: .panel)
@ -76,7 +75,7 @@ class PanelController: ParentPanelController {
setTimezoneDatasourceSlider(sliderValue: 0) setTimezoneDatasourceSlider(sliderValue: 0)
reviewView.isHidden = !(RateController.canPrompt()) reviewView.isHidden = !RateController.canPrompt()
reviewView.layer?.backgroundColor = NSColor.clear.cgColor reviewView.layer?.backgroundColor = NSColor.clear.cgColor
@ -100,7 +99,6 @@ class PanelController: ParentPanelController {
// New way to set the panel's frame. // New way to set the panel's frame.
// This takes into account the screen's dimensions. // This takes into account the screen's dimensions.
private func setPanelFrame() { private func setPanelFrame() {
guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else {
return return
} }
@ -176,14 +174,13 @@ class PanelController: ParentPanelController {
"Show Upcoming Event View": showUpcomingEventView == "YES" ? "Yes" : "No", "Show Upcoming Event View": showUpcomingEventView == "YES" ? "Yes" : "No",
"Country": country, "Country": country,
"Calendar Access Provided": EventCenter.sharedCenter().calendarAccessGranted() ? "Yes" : "No", "Calendar Access Provided": EventCenter.sharedCenter().calendarAccessGranted() ? "Yes" : "No",
"Number of Timezones": preferences.count "Number of Timezones": preferences.count,
] ]
Logger.log(object: panelEvent, for: "openedPanel") Logger.log(object: panelEvent, for: "openedPanel")
} }
private func startWindowTimer() { private func startWindowTimer() {
stopMenubarTimerIfNeccesary() stopMenubarTimerIfNeccesary()
if let timer = parentTimer, timer.state == .paused { if let timer = parentTimer, timer.state == .paused {
@ -195,7 +192,6 @@ class PanelController: ParentPanelController {
} }
private func startTimer() { private func startTimer() {
print("Start timer called") print("Start timer called")
parentTimer = Repeater(interval: .seconds(1), mode: .infinite) { _ in parentTimer = Repeater(interval: .seconds(1), mode: .infinite) { _ in
@ -204,7 +200,6 @@ class PanelController: ParentPanelController {
} }
} }
parentTimer!.start() parentTimer!.start()
} }
private func stopMenubarTimerIfNeccesary() { private func stopMenubarTimerIfNeccesary() {
@ -278,7 +273,7 @@ class PanelController: ParentPanelController {
target.image = Themer.shared().extraOptionsHighlightedImage() target.image = Themer.shared().extraOptionsHighlightedImage()
if popover.isShown && row == previousPopoverRow { if popover.isShown, row == previousPopoverRow {
popover.close() popover.close()
target.image = Themer.shared().extraOptionsImage() target.image = Themer.shared().extraOptionsImage()
previousPopoverRow = -1 previousPopoverRow = -1

38
Clocker/Panel/ParentPanelController.swift

@ -12,7 +12,6 @@ struct PanelConstants {
} }
class ParentPanelController: NSWindowController { class ParentPanelController: NSWindowController {
private var futureSliderObserver: NSKeyValueObservation? private var futureSliderObserver: NSKeyValueObservation?
private var userFontSizeSelectionObserver: NSKeyValueObservation? private var userFontSizeSelectionObserver: NSKeyValueObservation?
private var futureSliderRangeObserver: NSKeyValueObservation? private var futureSliderRangeObserver: NSKeyValueObservation?
@ -98,13 +97,13 @@ class ParentPanelController: NSWindowController {
} }
private func setupObservers() { private func setupObservers() {
futureSliderObserver = UserDefaults.standard.observe(\.displayFutureSlider, options: [.new]) { (_, change) in futureSliderObserver = UserDefaults.standard.observe(\.displayFutureSlider, options: [.new]) { _, change in
if let changedValue = change.newValue { if let changedValue = change.newValue {
self.futureSliderView.isHidden = changedValue == 1 self.futureSliderView.isHidden = changedValue == 1
} }
} }
userFontSizeSelectionObserver = UserDefaults.standard.observe(\.userFontSize, options: [.new]) { (_, change) in userFontSizeSelectionObserver = UserDefaults.standard.observe(\.userFontSize, options: [.new]) { _, change in
if let newFontSize = change.newValue { if let newFontSize = change.newValue {
Logger.log(object: ["FontSize": newFontSize], for: "User Font Size Preference") Logger.log(object: ["FontSize": newFontSize], for: "User Font Size Preference")
self.mainTableView.reloadData() self.mainTableView.reloadData()
@ -112,7 +111,7 @@ class ParentPanelController: NSWindowController {
} }
} }
futureSliderRangeObserver = UserDefaults.standard.observe(\.sliderDayRange, options: [.new]) { (_, change) in futureSliderRangeObserver = UserDefaults.standard.observe(\.sliderDayRange, options: [.new]) { _, change in
if change.newValue != nil { if change.newValue != nil {
self.adjustFutureSliderBasedOnPreferences() self.adjustFutureSliderBasedOnPreferences()
} }
@ -153,7 +152,7 @@ class ParentPanelController: NSWindowController {
themeChanged() themeChanged()
futureSliderView.isHidden = !(DataStore.shared().shouldDisplay(.futureSlider)) futureSliderView.isHidden = !DataStore.shared().shouldDisplay(.futureSlider)
sharingButton.sendAction(on: .leftMouseDown) sharingButton.sendAction(on: .leftMouseDown)
@ -168,14 +167,13 @@ class ParentPanelController: NSWindowController {
} }
private func showDebugVersionViewIfNeccesary() { private func showDebugVersionViewIfNeccesary() {
if debugVersionView != nil { if debugVersionView != nil {
debugVersionView.wantsLayer = true debugVersionView.wantsLayer = true
debugVersionView.layer?.backgroundColor = NSColor.systemRed.cgColor debugVersionView.layer?.backgroundColor = NSColor.systemRed.cgColor
} }
#if RELEASE #if RELEASE
if debugVersionView != nil && stackView.arrangedSubviews.contains(debugVersionView) { if debugVersionView != nil, stackView.arrangedSubviews.contains(debugVersionView) {
stackView.removeView(debugVersionView) stackView.removeView(debugVersionView)
} }
#endif #endif
@ -277,7 +275,7 @@ class ParentPanelController: NSWindowController {
let styleAttributes = [ let styleAttributes = [
NSAttributedString.Key.paragraphStyle: paragraphStyle, 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) let leftButtonAttributedTitle = NSAttributedString(string: leftButton.title, attributes: styleAttributes)
@ -830,7 +828,7 @@ class ParentPanelController: NSWindowController {
let styleAttributes = [ let styleAttributes = [
NSAttributedString.Key.paragraphStyle: paragraphStyle, 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) leftButton.attributedTitle = NSAttributedString(string: "Not Really", attributes: styleAttributes)
rightButton.attributedTitle = NSAttributedString(string: "Yes!", attributes: styleAttributes) rightButton.attributedTitle = NSAttributedString(string: "Yes!", attributes: styleAttributes)
@ -841,7 +839,7 @@ class ParentPanelController: NSWindowController {
return return
} }
NSAnimationContext.runAnimationGroup({ (context) in NSAnimationContext.runAnimationGroup({ context in
context.duration = 1 context.duration = 1
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
leftButton.animator().alphaValue = 0.0 leftButton.animator().alphaValue = 0.0
@ -849,7 +847,7 @@ class ParentPanelController: NSWindowController {
}, completionHandler: { }, completionHandler: {
field.stringValue = title field.stringValue = title
NSAnimationContext.runAnimationGroup({ (context) in NSAnimationContext.runAnimationGroup({ context in
context.duration = 1 context.duration = 1
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
self.runAnimationCompletionBlock(leftTitle, rightTitle) self.runAnimationCompletionBlock(leftTitle, rightTitle)
@ -858,27 +856,27 @@ class ParentPanelController: NSWindowController {
} }
private func runAnimationCompletionBlock(_ leftButtonTitle: String, _ rightButtonTitle: String) { private func runAnimationCompletionBlock(_ leftButtonTitle: String, _ rightButtonTitle: String) {
self.leftButton.animator().alphaValue = 1.0 leftButton.animator().alphaValue = 1.0
self.rightButton.animator().alphaValue = 1.0 rightButton.animator().alphaValue = 1.0
let paragraphStyle = NSMutableParagraphStyle() let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center paragraphStyle.alignment = .center
let styleAttributes = [ let styleAttributes = [
NSAttributedString.Key.paragraphStyle: paragraphStyle, 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" { if leftButton.attributedTitle.string == "Not Really" {
self.leftButton.animator().attributedTitle = NSAttributedString(string: PanelConstants.noThanksTitle, attributes: styleAttributes) leftButton.animator().attributedTitle = NSAttributedString(string: PanelConstants.noThanksTitle, attributes: styleAttributes)
} }
if self.rightButton.attributedTitle.string == PanelConstants.yesWithExclamation { if rightButton.attributedTitle.string == PanelConstants.yesWithExclamation {
self.rightButton.animator().attributedTitle = NSAttributedString(string: "Yes, sure", attributes: styleAttributes) rightButton.animator().attributedTitle = NSAttributedString(string: "Yes, sure", attributes: styleAttributes)
} }
self.leftButton.animator().attributedTitle = NSAttributedString(string: leftButtonTitle, attributes: styleAttributes) leftButton.animator().attributedTitle = NSAttributedString(string: leftButtonTitle, attributes: styleAttributes)
self.rightButton.animator().attributedTitle = NSAttributedString(string: rightButtonTitle, attributes: styleAttributes) rightButton.animator().attributedTitle = NSAttributedString(string: rightButtonTitle, attributes: styleAttributes)
} }
// MARK: Date Picker + Slider // MARK: Date Picker + Slider

2
Clocker/Panel/Rate Controller/RateController.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
final class RateController { final class RateController {
private static var storage = UserDefaults.standard private static var storage = UserDefaults.standard
private static let version: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "N/A" private static let version: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "N/A"
private static var debugging = false private static var debugging = false
@ -66,5 +65,4 @@ final class RateController {
NSWorkspace.shared.open(ratingsURL) NSWorkspace.shared.open(ratingsURL)
prompted() prompted()
} }
} }

4
Clocker/Panel/Rate Controller/ReviewView.swift

@ -7,7 +7,7 @@ class ReviewView: NSView {
override func mouseEntered(with event: NSEvent) { override func mouseEntered(with event: NSEvent) {
super.mouseEntered(with: event) super.mouseEntered(with: event)
let dismissalButton = subviews.filter({ $0.tag == 55 }).first let dismissalButton = subviews.filter { $0.tag == 55 }.first
if let firstMatch = dismissalButton, firstMatch.isHidden { if let firstMatch = dismissalButton, firstMatch.isHidden {
firstMatch.isHidden = false firstMatch.isHidden = false
} }
@ -15,7 +15,7 @@ class ReviewView: NSView {
override func mouseExited(with event: NSEvent) { override func mouseExited(with event: NSEvent) {
super.mouseExited(with: event) super.mouseExited(with: event)
let dismissalButton = subviews.filter({ $0.tag == 55 }).first let dismissalButton = subviews.filter { $0.tag == 55 }.first
if let firstMatch = dismissalButton, !firstMatch.isHidden { if let firstMatch = dismissalButton, !firstMatch.isHidden {
firstMatch.isHidden = true firstMatch.isHidden = true
} }

2
Clocker/Panel/UI/PanelTableView.swift

@ -31,7 +31,7 @@ class PanelTableView: NSTableView {
let options: NSTrackingArea.Options = [ let options: NSTrackingArea.Options = [
.mouseMoved, .mouseMoved,
.mouseEnteredAndExited, .mouseEnteredAndExited,
.activeAlways .activeAlways,
] ]
let clipRect = enclosingScrollView?.contentView.bounds ?? .zero let clipRect = enclosingScrollView?.contentView.bounds ?? .zero

2
Clocker/Panel/UI/TimezoneCellView.swift

@ -116,7 +116,7 @@ class TimezoneCellView: NSTableCellView {
var searchView = superview var searchView = superview
while searchView != nil && searchView is PanelTableView == false { while searchView != nil, searchView is PanelTableView == false {
searchView = searchView?.superview searchView = searchView?.superview
} }

4
Clocker/Panel/UI/TimezoneDataSource.swift

@ -60,7 +60,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
cellView.time.stringValue = operation.time(with: sliderValue) cellView.time.stringValue = operation.time(with: sliderValue)
cellView.noteLabel.stringValue = currentModel.note ?? CLEmptyString cellView.noteLabel.stringValue = currentModel.note ?? CLEmptyString
cellView.noteLabel.toolTip = currentModel.note ?? CLEmptyString cellView.noteLabel.toolTip = currentModel.note ?? CLEmptyString
cellView.currentLocationIndicator.isHidden = !(currentModel.isSystemTimezone) cellView.currentLocationIndicator.isHidden = !currentModel.isSystemTimezone
cellView.time.setAccessibilityIdentifier("ActualTime") cellView.time.setAccessibilityIdentifier("ActualTime")
cellView.layout(with: currentModel) cellView.layout(with: currentModel)
@ -177,7 +177,7 @@ extension TimezoneCellView {
sunriseImage.isHidden = !shouldDisplay sunriseImage.isHidden = !shouldDisplay
// If it's a timezone and not a place, we can't determine the sunrise/sunset time; hide the sunrise image // If it's a timezone and not a place, we can't determine the sunrise/sunset time; hide the sunrise image
if model.selectionType == .timezone && (model.latitude == nil && model.longitude == nil) { if model.selectionType == .timezone, model.latitude == nil, model.longitude == nil {
sunriseImage.isHidden = true sunriseImage.isHidden = true
} }

11
Clocker/Preferences/About/AboutViewController.swift

@ -33,7 +33,7 @@ class AboutViewController: ParentViewController {
setup() setup()
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
self.setup() self.setup()
} }
} }
@ -48,14 +48,12 @@ class AboutViewController: ParentViewController {
let rangesInOrder = [NSRange(location: 3, length: 8), let rangesInOrder = [NSRange(location: 3, length: 8),
NSRange(location: 7, length: privateFeedback.attributedTitle.length - 7), NSRange(location: 7, length: privateFeedback.attributedTitle.length - 7),
NSRange(location: 27, length: 33), NSRange(location: 27, length: 33),
NSRange(location: 32, length: 30) NSRange(location: 32, length: 30)]
]
let buttonsInOrder = [quickCommentAction, let buttonsInOrder = [quickCommentAction,
privateFeedback, privateFeedback,
supportClocker, supportClocker,
openSourceButton openSourceButton]
]
let localizedKeys = ["1. @n0shake on Twitter for quick comments", let localizedKeys = ["1. @n0shake on Twitter for quick comments",
"2. For Private Feedback", "2. For Private Feedback",
@ -126,7 +124,6 @@ class AboutViewController: ParentViewController {
let custom: [String: Any] = ["Country": countryCode] let custom: [String: Any] = ["Country": countryCode]
Logger.log(object: custom, for: "Report Issue Opened") Logger.log(object: custom, for: "Report Issue Opened")
} }
} }
@IBAction func openGitHub(_: Any) { @IBAction func openGitHub(_: Any) {
@ -139,7 +136,7 @@ class AboutViewController: ParentViewController {
Logger.log(object: custom, for: "Opened GitHub") Logger.log(object: custom, for: "Opened GitHub")
} }
@IBOutlet weak var feedbackLabel: NSTextField! @IBOutlet var feedbackLabel: NSTextField!
private func setup() { private func setup() {
feedbackLabel.stringValue = "Feedback is always welcome:" feedbackLabel.stringValue = "Feedback is always welcome:"

2
Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift

@ -155,7 +155,7 @@ class AppFeedbackWindowController: NSWindowController {
AppFeedbackConstants.CLAppFeedbackEmailProperty: email, AppFeedbackConstants.CLAppFeedbackEmailProperty: email,
AppFeedbackConstants.CLAppFeedbackFeedbackProperty: appFeedbackProperty, AppFeedbackConstants.CLAppFeedbackFeedbackProperty: appFeedbackProperty,
AppFeedbackConstants.CLOperatingSystemVersion: osVersion, AppFeedbackConstants.CLOperatingSystemVersion: osVersion,
AppFeedbackConstants.CLClockerVersion: versionInfo AppFeedbackConstants.CLClockerVersion: versionInfo,
] ]
return feedbackInfo return feedbackInfo

57
Clocker/Preferences/Appearance/AppearanceViewController.swift

@ -8,10 +8,10 @@ class AppearanceViewController: ParentViewController {
@IBOutlet var informationLabel: NSTextField! @IBOutlet var informationLabel: NSTextField!
@IBOutlet var sliderDayRangePopup: NSPopUpButton! @IBOutlet var sliderDayRangePopup: NSPopUpButton!
@IBOutlet var visualEffectView: NSVisualEffectView! @IBOutlet var visualEffectView: NSVisualEffectView!
@IBOutlet weak var menubarMode: NSSegmentedControl! @IBOutlet var menubarMode: NSSegmentedControl!
@IBOutlet weak var includeDayInMenubarControl: NSSegmentedControl! @IBOutlet var includeDayInMenubarControl: NSSegmentedControl!
@IBOutlet weak var includeDateInMenubarControl: NSSegmentedControl! @IBOutlet var includeDateInMenubarControl: NSSegmentedControl!
@IBOutlet weak var includePlaceNameControl: NSSegmentedControl! @IBOutlet var includePlaceNameControl: NSSegmentedControl!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
@ -31,12 +31,12 @@ class AppearanceViewController: ParentViewController {
"4 days", "4 days",
"5 days", "5 days",
"6 days", "6 days",
"7 days" "7 days",
]) ])
setup() setup()
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
self.setup() self.setup()
self.animateBackgroundColorChange() self.animateBackgroundColorChange()
self.view.needsDisplay = true // Let's make the color change permanent. self.view.needsDisplay = true // Let's make the color change permanent.
@ -54,7 +54,7 @@ class AppearanceViewController: ParentViewController {
colorAnimation.duration = 0.25 colorAnimation.duration = 0.25
colorAnimation.fromValue = previousBackgroundColor.cgColor colorAnimation.fromValue = previousBackgroundColor.cgColor
colorAnimation.toValue = Themer.shared().mainBackgroundColor().cgColor colorAnimation.toValue = Themer.shared().mainBackgroundColor().cgColor
self.view.layer?.add(colorAnimation, forKey: "backgroundColor") view.layer?.add(colorAnimation, forKey: "backgroundColor")
} }
override func viewWillAppear() { override func viewWillAppear() {
@ -80,21 +80,21 @@ class AppearanceViewController: ParentViewController {
updateMenubarControls(!shouldDisplayCompact) updateMenubarControls(!shouldDisplayCompact)
} }
@IBOutlet weak var headerLabel: NSTextField! @IBOutlet var headerLabel: NSTextField!
@IBOutlet weak var timeFormatLabel: NSTextField! @IBOutlet var timeFormatLabel: NSTextField!
@IBOutlet weak var panelTheme: NSTextField! @IBOutlet var panelTheme: NSTextField!
@IBOutlet weak var dayDisplayOptionsLabel: NSTextField! @IBOutlet var dayDisplayOptionsLabel: NSTextField!
@IBOutlet weak var showSliderLabel: NSTextField! @IBOutlet var showSliderLabel: NSTextField!
@IBOutlet weak var showSunriseLabel: NSTextField! @IBOutlet var showSunriseLabel: NSTextField!
@IBOutlet weak var showSecondsLabel: NSTextField! @IBOutlet var showSecondsLabel: NSTextField!
@IBOutlet weak var largerTextLabel: NSTextField! @IBOutlet var largerTextLabel: NSTextField!
@IBOutlet weak var futureSliderRangeLabel: NSTextField! @IBOutlet var futureSliderRangeLabel: NSTextField!
@IBOutlet weak var includeDateLabel: NSTextField! @IBOutlet var includeDateLabel: NSTextField!
@IBOutlet weak var includeDayLabel: NSTextField! @IBOutlet var includeDayLabel: NSTextField!
@IBOutlet weak var includePlaceLabel: NSTextField! @IBOutlet var includePlaceLabel: NSTextField!
@IBOutlet weak var menubarDisplayOptionsLabel: NSTextField! @IBOutlet var menubarDisplayOptionsLabel: NSTextField!
@IBOutlet weak var appDisplayLabel: NSTextField! @IBOutlet var appDisplayLabel: NSTextField!
@IBOutlet weak var menubarModeLabel: NSTextField! @IBOutlet var menubarModeLabel: NSTextField!
private func setup() { private func setup() {
headerLabel.stringValue = "Main Panel Options" headerLabel.stringValue = "Main Panel Options"
@ -136,7 +136,6 @@ class AppearanceViewController: ParentViewController {
private var previousBackgroundColor: NSColor = NSColor.white private var previousBackgroundColor: NSColor = NSColor.white
@IBAction func themeChanged(_ sender: NSSegmentedControl) { @IBAction func themeChanged(_ sender: NSSegmentedControl) {
previousBackgroundColor = Themer.shared().mainBackgroundColor() previousBackgroundColor = Themer.shared().mainBackgroundColor()
Themer.shared().set(theme: sender.selectedSegment) Themer.shared().set(theme: sender.selectedSegment)
@ -208,7 +207,6 @@ class AppearanceViewController: ParentViewController {
} }
@IBAction func changeAppDisplayOptions(_ sender: NSSegmentedControl) { @IBAction func changeAppDisplayOptions(_ sender: NSSegmentedControl) {
if sender.selectedSegment == 0 { if sender.selectedSegment == 0 {
Logger.log(object: ["Selection": "Menubar"], for: "Dock Mode") Logger.log(object: ["Selection": "Menubar"], for: "Dock Mode")
NSApp.setActivationPolicy(.accessory) NSApp.setActivationPolicy(.accessory)
@ -220,7 +218,7 @@ class AppearanceViewController: ParentViewController {
private func refresh(panel: Bool, floating: Bool) { private func refresh(panel: Bool, floating: Bool) {
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
if panel && DataStore.shared().shouldDisplay(ViewType.showAppInForeground) == false { if panel, DataStore.shared().shouldDisplay(ViewType.showAppInForeground) == false {
guard let panelController = PanelController.panel() else { return } guard let panelController = PanelController.panel() else { return }
let futureSliderBounds = panelController.futureSlider.bounds let futureSliderBounds = panelController.futureSlider.bounds
@ -232,7 +230,7 @@ class AppearanceViewController: ParentViewController {
panelController.setupMenubarTimer() panelController.setupMenubarTimer()
} }
if floating && DataStore.shared().shouldDisplay(ViewType.showAppInForeground) { if floating, DataStore.shared().shouldDisplay(ViewType.showAppInForeground) {
if DataStore.shared().shouldDisplay(ViewType.showAppInForeground) { if DataStore.shared().shouldDisplay(ViewType.showAppInForeground) {
let floatingWindow = FloatingWindowController.shared() let floatingWindow = FloatingWindowController.shared()
floatingWindow.updateTableContent() floatingWindow.updateTableContent()
@ -246,16 +244,16 @@ class AppearanceViewController: ParentViewController {
} }
} }
@IBAction func displayDayInMenubarAction(_ sender: Any) { @IBAction func displayDayInMenubarAction(_: Any) {
DataStore.shared().updateDayPreference() DataStore.shared().updateDayPreference()
updateStatusItem() updateStatusItem()
} }
@IBAction func displayDateInMenubarAction(_ sender: Any) { @IBAction func displayDateInMenubarAction(_: Any) {
updateStatusItem() updateStatusItem()
} }
@IBAction func displayPlaceInMenubarAction(_ sender: Any) { @IBAction func displayPlaceInMenubarAction(_: Any) {
updateStatusItem() updateStatusItem()
} }
@ -285,7 +283,6 @@ class AppearanceViewController: ParentViewController {
} else { } else {
Logger.log(object: ["Context": "In Appearance View"], for: "Switched to Standard Mode") 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. // We don't support showing day or date in the menubar for compact mode yet.

54
Clocker/Preferences/Calendar/CalendarViewController.swift

@ -4,7 +4,6 @@ import Cocoa
import EventKit import EventKit
class ClockerTextBackgroundView: NSView { class ClockerTextBackgroundView: NSView {
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
override func awakeFromNib() { override func awakeFromNib() {
@ -13,7 +12,7 @@ class ClockerTextBackgroundView: NSView {
layer?.masksToBounds = false layer?.masksToBounds = false
layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
self.layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor self.layer?.backgroundColor = Themer.shared().textBackgroundColor().cgColor
} }
} }
@ -31,18 +30,17 @@ class ClockerTextBackgroundView: NSView {
} }
class CalendarViewController: ParentViewController { class CalendarViewController: ParentViewController {
@IBOutlet var showSegmentedControl: NSSegmentedControl! @IBOutlet var showSegmentedControl: NSSegmentedControl!
@IBOutlet var allDaysSegmentedControl: NSSegmentedControl! @IBOutlet var allDaysSegmentedControl: NSSegmentedControl!
@IBOutlet var truncateTextField: NSTextField! @IBOutlet var truncateTextField: NSTextField!
@IBOutlet var noAccessView: NSVisualEffectView! @IBOutlet var noAccessView: NSVisualEffectView!
@IBOutlet var informationField: NSTextField! @IBOutlet var informationField: NSTextField!
@IBOutlet var grantAccessButton: NSButton! @IBOutlet var grantAccessButton: NSButton!
@IBOutlet weak var calendarsTableView: NSTableView! @IBOutlet var calendarsTableView: NSTableView!
@IBOutlet weak var showNextMeetingInMenubarControl: NSSegmentedControl! @IBOutlet var showNextMeetingInMenubarControl: NSSegmentedControl!
@IBOutlet weak var backgroundView: NSView! @IBOutlet var backgroundView: NSView!
@IBOutlet weak var nextMeetingBackgroundView: NSView! @IBOutlet var nextMeetingBackgroundView: NSView!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
private lazy var calendars: [Any] = EventCenter.sharedCenter().fetchSourcesAndCalendars() private lazy var calendars: [Any] = EventCenter.sharedCenter().fetchSourcesAndCalendars()
@ -57,7 +55,7 @@ class CalendarViewController: ParentViewController {
name: .calendarAccessGranted, name: .calendarAccessGranted,
object: nil) object: nil)
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
self.setup() self.setup()
} }
@ -91,7 +89,7 @@ class CalendarViewController: ParentViewController {
} }
// If the menubar mode is compact, we can't show meetings in the menubar. So disable toggling that option. // 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)) showNextMeetingInMenubarControl.isEnabled = !DataStore.shared().shouldDisplay(.menubarCompactMode)
} }
private func verifyCalendarAccess() { private func verifyCalendarAccess() {
@ -121,7 +119,7 @@ class CalendarViewController: ParentViewController {
let attributesDictionary: [NSAttributedString.Key: Any] = [ let attributesDictionary: [NSAttributedString.Key: Any] = [
NSAttributedString.Key.paragraphStyle: style, NSAttributedString.Key.paragraphStyle: style,
NSAttributedString.Key.font: boldFont, NSAttributedString.Key.font: boldFont,
NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor() NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(),
] ]
let attributedString = NSAttributedString(string: title, let attributedString = NSAttributedString(string: title,
attributes: attributesDictionary) attributes: attributesDictionary)
@ -146,7 +144,6 @@ class CalendarViewController: ParentViewController {
} }
@IBAction func showNextMeetingAction(_ sender: NSSegmentedControl) { @IBAction func showNextMeetingAction(_ sender: NSSegmentedControl) {
// We need to start the menubar timer if it hasn't been started already // We need to start the menubar timer if it hasn't been started already
guard let delegate = NSApplication.shared.delegate as? AppDelegate else { guard let delegate = NSApplication.shared.delegate as? AppDelegate else {
assertionFailure() assertionFailure()
@ -156,7 +153,6 @@ class CalendarViewController: ParentViewController {
let statusItemHandler = delegate.statusItemForPanel() let statusItemHandler = delegate.statusItemForPanel()
if sender.selectedSegment == 0 { if sender.selectedSegment == 0 {
if let isValid = statusItemHandler.menubarTimer?.isValid, isValid == true { if let isValid = statusItemHandler.menubarTimer?.isValid, isValid == true {
print("Timer is already in progress") print("Timer is already in progress")
updateStatusItem() updateStatusItem()
@ -164,10 +160,8 @@ class CalendarViewController: ParentViewController {
} }
} else { } else {
statusItemHandler.invalidateTimer(showIcon: true, isSyncing: false) statusItemHandler.invalidateTimer(showIcon: true, isSyncing: false)
} }
} }
@IBAction func showUpcomingEventView(_ sender: NSSegmentedControl) { @IBAction func showUpcomingEventView(_ sender: NSSegmentedControl) {
@ -203,15 +197,15 @@ class CalendarViewController: ParentViewController {
statusItem.performTimerWork() statusItem.performTimerWork()
} }
@IBOutlet weak var headerLabel: NSTextField! @IBOutlet var headerLabel: NSTextField!
@IBOutlet weak var upcomingEventView: NSTextField! @IBOutlet var upcomingEventView: NSTextField!
@IBOutlet weak var allDayMeetingsLabel: NSTextField! @IBOutlet var allDayMeetingsLabel: NSTextField!
@IBOutlet weak var showNextMeetingLabel: NSTextField! @IBOutlet var showNextMeetingLabel: NSTextField!
@IBOutlet weak var nextMeetingAccessoryLabel: NSTextField! @IBOutlet var nextMeetingAccessoryLabel: NSTextField!
@IBOutlet weak var truncateTextLabel: NSTextField! @IBOutlet var truncateTextLabel: NSTextField!
@IBOutlet weak var showEventsFromLabel: NSTextField! @IBOutlet var showEventsFromLabel: NSTextField!
@IBOutlet weak var charactersField: NSTextField! @IBOutlet var charactersField: NSTextField!
@IBOutlet weak var truncateAccessoryLabel: NSTextField! @IBOutlet var truncateAccessoryLabel: NSTextField!
private func setup() { private func setup() {
// Grant access button's text color is taken care above. // Grant access button's text color is taken care above.
@ -231,21 +225,18 @@ class CalendarViewController: ParentViewController {
} }
extension CalendarViewController: NSTableViewDataSource { extension CalendarViewController: NSTableViewDataSource {
func numberOfRows(in _: NSTableView) -> Int {
func numberOfRows(in tableView: NSTableView) -> Int {
let hasCalendarAccess = EventCenter.sharedCenter().calendarAccessGranted() let hasCalendarAccess = EventCenter.sharedCenter().calendarAccessGranted()
return hasCalendarAccess ? calendars.count : 0 return hasCalendarAccess ? calendars.count : 0
} }
} }
extension CalendarViewController: NSTableViewDelegate { extension CalendarViewController: NSTableViewDelegate {
func tableView(_: NSTableView, shouldSelectRow _: Int) -> Bool {
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
return false return false
} }
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat {
guard let currentSource = calendars[row] as? String, !currentSource.isEmpty else { guard let currentSource = calendars[row] as? String, !currentSource.isEmpty else {
return 30.0 return 30.0
} }
@ -253,8 +244,7 @@ extension CalendarViewController: NSTableViewDelegate {
return 24.0 return 24.0
} }
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
if let currentSource = calendars[row] as? String, if let currentSource = calendars[row] as? String,
let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell { let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell {
message.sourceName.stringValue = currentSource message.sourceName.stringValue = currentSource
@ -273,7 +263,6 @@ extension CalendarViewController: NSTableViewDelegate {
} }
return nil return nil
} }
@objc func calendarSelected(_ checkbox: NSButton) { @objc func calendarSelected(_ checkbox: NSButton) {
@ -290,7 +279,6 @@ extension CalendarViewController: NSTableViewDelegate {
} }
private func updateSelectedCalendars(_ selection: [Any]) { private func updateSelectedCalendars(_ selection: [Any]) {
var selectedCalendars: [String] = [] var selectedCalendars: [String] = []
for obj in selection { for obj in selection {

48
Clocker/Preferences/General/PreferencesViewController.swift

@ -70,7 +70,7 @@ class PreferencesViewController: ParentViewController {
@IBOutlet private var headerView: NSView! @IBOutlet private var headerView: NSView!
@IBOutlet private var tableview: NSView! @IBOutlet private var tableview: NSView!
@IBOutlet private var additionalSortOptions: NSView! @IBOutlet private var additionalSortOptions: NSView!
@IBOutlet weak var startAtLoginLabel: NSTextField! @IBOutlet var startAtLoginLabel: NSTextField!
@IBOutlet var startupCheckbox: NSButton! @IBOutlet var startupCheckbox: NSButton!
@IBOutlet var headerLabel: NSTextField! @IBOutlet var headerLabel: NSTextField!
@ -198,7 +198,7 @@ class PreferencesViewController: ParentViewController {
} }
let archivedObjects = menubarTimes.map { (timezone) -> Data in let archivedObjects = menubarTimes.map { (timezone) -> Data in
return NSKeyedArchiver.archivedData(withRootObject: timezone) NSKeyedArchiver.archivedData(withRootObject: timezone)
} }
UserDefaults.standard.set(archivedObjects, forKey: CLMenubarFavorites) UserDefaults.standard.set(archivedObjects, forKey: CLMenubarFavorites)
@ -244,7 +244,7 @@ class PreferencesViewController: ParentViewController {
[timezoneNameSortButton, labelSortButton, timezoneSortButton].forEach { [timezoneNameSortButton, labelSortButton, timezoneSortButton].forEach {
$0?.attributedTitle = NSAttributedString(string: $0?.title ?? CLEmptyString, attributes: [ $0?.attributedTitle = NSAttributedString(string: $0?.title ?? CLEmptyString, attributes: [
NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(), NSAttributedString.Key.foregroundColor: Themer.shared().mainTextColor(),
NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)! NSAttributedString.Key.font: NSFont(name: "Avenir-Light", size: 13)!,
]) ])
} }
@ -362,7 +362,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
return nil return nil
} }
private func handleTimezoneNameIdentifier(for row: Int, _ selectedDataSource: TimezoneData?) -> Any? { private func handleTimezoneNameIdentifier(for _: Int, _ selectedDataSource: TimezoneData?) -> Any? {
guard let model = selectedDataSource else { guard let model = selectedDataSource else {
return nil return nil
} }
@ -382,7 +382,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
return dataSource?.formattedAddress return dataSource?.formattedAddress
} }
} else { } else {
if searchField.stringValue.isEmpty == false && row < timezoneFilteredArray.count { if searchField.stringValue.isEmpty == false, row < timezoneFilteredArray.count {
return timezoneFilteredArray[row] return timezoneFilteredArray[row]
} }
return timezoneArray[row] return timezoneArray[row]
@ -391,7 +391,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
} }
private func handleAbbreviationColumn(for row: Int) -> Any? { private func handleAbbreviationColumn(for row: Int) -> Any? {
if searchField.stringValue.isEmpty == false && (row < timezoneFilteredArray.count) { if searchField.stringValue.isEmpty == false, row < timezoneFilteredArray.count {
let currentSelection = timezoneFilteredArray[row] let currentSelection = timezoneFilteredArray[row]
if currentSelection == "UTC" { if currentSelection == "UTC" {
return "UTC" return "UTC"
@ -497,7 +497,7 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
if selectedTimeZones.count > row { if selectedTimeZones.count > row {
Logger.log(object: [ Logger.log(object: [
"Old Label": dataObject.customLabel ?? "Error", "Old Label": dataObject.customLabel ?? "Error",
"New Label": formattedValue "New Label": formattedValue,
], ],
for: "Custom Label Changed") for: "Custom Label Changed")
@ -510,25 +510,24 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
Logger.log(object: [ Logger.log(object: [
"MethodName": "SetObjectValue", "MethodName": "SetObjectValue",
"Selected Timezone Count": selectedTimeZones.count, "Selected Timezone Count": selectedTimeZones.count,
"Current Row": row "Current Row": row,
], ],
for: "Error in selected row count") for: "Error in selected row count")
} }
} }
private func showAlertIfMoreThanOneTimezoneHasBeenAddedToTheMenubar() { private func showAlertIfMoreThanOneTimezoneHasBeenAddedToTheMenubar() {
let isUITestRunning = ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) let isUITestRunning = ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument)
// If we have seen displayed the message before, abort! // If we have seen displayed the message before, abort!
let haveWeSeenThisMessageBefore = UserDefaults.standard.bool(forKey: CLLongStatusBarWarningMessage) let haveWeSeenThisMessageBefore = UserDefaults.standard.bool(forKey: CLLongStatusBarWarningMessage)
if haveWeSeenThisMessageBefore && !isUITestRunning { if haveWeSeenThisMessageBefore, !isUITestRunning {
return return
} }
// If the user is already using the compact mode, abort. // If the user is already using the compact mode, abort.
if DataStore.shared().shouldDisplay(.menubarCompactMode) && !isUITestRunning { if DataStore.shared().shouldDisplay(.menubarCompactMode), !isUITestRunning {
return return
} }
@ -692,7 +691,7 @@ extension PreferencesViewController {
self.dataTask = NetworkManager.task(with: self.generateSearchURL(), self.dataTask = NetworkManager.task(with: self.generateSearchURL(),
completionHandler: { [weak self] response, error in completionHandler: { [weak self] response, error in
guard let `self` = self else { return } guard let self = self else { return }
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
if let errorPresent = error { if let errorPresent = error {
@ -734,12 +733,12 @@ extension PreferencesViewController {
private func presentError(_ errorMessage: String) { private func presentError(_ errorMessage: String) {
if errorMessage == PreferencesConstants.offlineErrorMessage { if errorMessage == PreferencesConstants.offlineErrorMessage {
self.placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError placeholderLabel.placeholderString = PreferencesConstants.noInternetConnectivityError
} else { } else {
self.placeholderLabel.placeholderString = PreferencesConstants.tryAgainMessage placeholderLabel.placeholderString = PreferencesConstants.tryAgainMessage
} }
self.isActivityInProgress = false isActivityInProgress = false
} }
private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) { private func appendResultsToFilteredArray(_ results: [SearchResult.Result]) {
@ -755,7 +754,7 @@ extension PreferencesViewController {
CLTimezoneName: formattedAddress, CLTimezoneName: formattedAddress,
CLCustomLabel: formattedAddress, CLCustomLabel: formattedAddress,
CLTimezoneID: CLEmptyString, CLTimezoneID: CLEmptyString,
CLPlaceIdentifier: $0.placeId CLPlaceIdentifier: $0.placeId,
] as [String: Any] ] as [String: Any]
self.filteredArray.append(TimezoneData(with: totalPackage)) self.filteredArray.append(TimezoneData(with: totalPackage))
@ -763,9 +762,9 @@ extension PreferencesViewController {
} }
private func prepareUIForPresentingResults() { private func prepareUIForPresentingResults() {
self.placeholderLabel.placeholderString = CLEmptyString placeholderLabel.placeholderString = CLEmptyString
self.isActivityInProgress = false isActivityInProgress = false
self.availableTimezoneTableView.reloadData() availableTimezoneTableView.reloadData()
} }
// Extracting this out for tests // Extracting this out for tests
@ -821,7 +820,7 @@ extension PreferencesViewController {
NetworkManager.task(with: urlString) { [weak self] response, error in NetworkManager.task(with: urlString) { [weak self] response, error in
guard let `self` = self else { return } guard let self = self else { return }
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
if self.handleEdgeCase(for: response) == true { if self.handleEdgeCase(for: response) == true {
@ -829,7 +828,7 @@ extension PreferencesViewController {
} }
if error == nil, let json = response, let timezone = self.decodeTimezone(from: json) { if error == nil, let json = response, let timezone = self.decodeTimezone(from: json) {
if self.availableTimezoneTableView.selectedRow >= 0 && self.availableTimezoneTableView.selectedRow < self.filteredArray.count { if self.availableTimezoneTableView.selectedRow >= 0, self.availableTimezoneTableView.selectedRow < self.filteredArray.count {
self.installTimezone(timezone) self.installTimezone(timezone)
} }
self.updateViewState() self.updateViewState()
@ -867,7 +866,7 @@ extension PreferencesViewController {
"latitude": dataObject.latitude!, "latitude": dataObject.latitude!,
"longitude": dataObject.longitude!, "longitude": dataObject.longitude!,
"nextUpdate": CLEmptyString, "nextUpdate": CLEmptyString,
CLCustomLabel: filteredAddress CLCustomLabel: filteredAddress,
] as [String: Any] ] as [String: Any]
let timezoneObject = TimezoneData(with: newTimeZone) let timezoneObject = TimezoneData(with: newTimeZone)
@ -1118,7 +1117,7 @@ extension PreferencesViewController {
var newDefaults = selectedTimeZones var newDefaults = selectedTimeZones
let objectsToRemove = timezoneTableView.selectedRowIndexes.map { (index) -> Data in let objectsToRemove = timezoneTableView.selectedRowIndexes.map { (index) -> Data in
return selectedTimeZones[index] selectedTimeZones[index]
} }
newDefaults = newDefaults.filter { !objectsToRemove.contains($0) } newDefaults = newDefaults.filter { !objectsToRemove.contains($0) }
@ -1331,8 +1330,7 @@ extension PreferencesViewController {
} }
} }
extension PreferencesViewController: SRRecorderControlDelegate { extension PreferencesViewController: SRRecorderControlDelegate {}
}
// Helpers // Helpers
extension PreferencesViewController { extension PreferencesViewController {

20
Clocker/Preferences/OneWindowController.swift

@ -3,15 +3,14 @@
import Cocoa import Cocoa
class CenteredTabViewController: NSTabViewController { class CenteredTabViewController: NSTabViewController {
override func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { override func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
super.toolbarDefaultItemIdentifiers(toolbar) super.toolbarDefaultItemIdentifiers(toolbar)
var toolbarItems: [NSToolbarItem.Identifier] = [NSToolbarItem.Identifier.flexibleSpace] var toolbarItems: [NSToolbarItem.Identifier] = [NSToolbarItem.Identifier.flexibleSpace]
tabViewItems.forEach { (item) in tabViewItems.forEach { item in
if let identifier = item.identifier as? String { if let identifier = item.identifier as? String {
toolbarItems.append(NSToolbarItem.Identifier.init(identifier)) toolbarItems.append(NSToolbarItem.Identifier(identifier))
} }
} }
@ -19,11 +18,9 @@ class CenteredTabViewController: NSTabViewController {
return toolbarItems return toolbarItems
} }
} }
class OneWindowController: NSWindowController { class OneWindowController: NSWindowController {
private static var sharedWindow: OneWindowController! private static var sharedWindow: OneWindowController!
private var themeDidChangeNotification: NSObjectProtocol? private var themeDidChangeNotification: NSObjectProtocol?
@ -31,13 +28,13 @@ class OneWindowController: NSWindowController {
super.windowDidLoad() super.windowDidLoad()
setup() setup()
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { (_) in themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
NSAnimationContext.runAnimationGroup({ (context) in NSAnimationContext.runAnimationGroup { context in
context.duration = 1 context.duration = 1
context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) context.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
self.window?.animator().backgroundColor = Themer.shared().mainBackgroundColor() self.window?.animator().backgroundColor = Themer.shared().mainBackgroundColor()
}) }
self.setupToolbarImages() self.setupToolbarImages()
} }
@ -61,7 +58,7 @@ class OneWindowController: NSWindowController {
class func shared() -> OneWindowController { class func shared() -> OneWindowController {
if sharedWindow == nil { if sharedWindow == nil {
let prefStoryboard = NSStoryboard.init(name: "Preferences", bundle: nil) let prefStoryboard = NSStoryboard(name: "Preferences", bundle: nil)
sharedWindow = prefStoryboard.instantiateInitialController() as? OneWindowController sharedWindow = prefStoryboard.instantiateInitialController() as? OneWindowController
} }
return sharedWindow return sharedWindow
@ -72,7 +69,7 @@ class OneWindowController: NSWindowController {
return return
} }
if !(window.isMainWindow) || !(window.isVisible) { if !window.isMainWindow || !window.isVisible {
showWindow(nil) showWindow(nil)
} }
@ -93,12 +90,11 @@ class OneWindowController: NSWindowController {
"Calendar": themer.calendarTabImage(), "Calendar": themer.calendarTabImage(),
"Permissions": themer.privacyTabImage()] "Permissions": themer.privacyTabImage()]
tabViewController.tabViewItems.forEach { (tabViewItem) in tabViewController.tabViewItems.forEach { tabViewItem in
let identity = (tabViewItem.identifier as? String) ?? "" let identity = (tabViewItem.identifier as? String) ?? ""
if identifierTOImageMapping[identity] != nil { if identifierTOImageMapping[identity] != nil {
tabViewItem.image = identifierTOImageMapping[identity] tabViewItem.image = identifierTOImageMapping[identity]
} }
} }
} }
} }

3
Clocker/Preferences/Permissions/PermissionsViewController.swift

@ -3,7 +3,6 @@
import Cocoa import Cocoa
class PermissionsViewController: ParentViewController { class PermissionsViewController: ParentViewController {
@IBOutlet var calendarContainerView: NSView! @IBOutlet var calendarContainerView: NSView!
@IBOutlet var remindersContainerView: NSView! @IBOutlet var remindersContainerView: NSView!
@ -129,7 +128,6 @@ class PermissionsViewController: ParentViewController {
if granted { if granted {
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
self.view.window?.orderBack(nil) self.view.window?.orderBack(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
@ -163,7 +161,6 @@ class PermissionsViewController: ParentViewController {
if granted { if granted {
OperationQueue.main.addOperation { OperationQueue.main.addOperation {
self.view.window?.orderBack(nil) self.view.window?.orderBack(nil)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)

Loading…
Cancel
Save