From b0c3ac57c43bc1ce79a306cc747bc396d8a7a837 Mon Sep 17 00:00:00 2001 From: Kristopher Johnson Date: Thu, 17 Oct 2019 19:17:35 -0400 Subject: [PATCH 1/3] Update for Swift 5 Make necessary changes so all source files compile on Xcode 11. Note that while the app builds now, it crashes at startup. --- MenubarCountdown.xcodeproj/project.pbxproj | 47 ++++++++-- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ .../xcschemes/MenubarCountdown.xcscheme | 24 ++--- MenubarCountdown/AppDelegate.swift | 83 +++++++++-------- ...erDefaults.swift => AppUserDefaults.swift} | 8 +- MenubarCountdown/CALayerExtensions.swift | 34 +++---- .../CATransactionExtensions.swift | 2 +- MenubarCountdown/Log.swift | 8 +- .../StartTimerDialogController.swift | 16 ++-- MenubarCountdown/StatusItemView.swift | 93 ++++++++++--------- MenubarCountdown/Stopwatch.swift | 4 +- MenubarCountdown/TextField.swift | 34 +++---- .../TimerExpiredAlertController.swift | 2 +- MenubarCountdown/ViewController.swift | 6 +- 14 files changed, 205 insertions(+), 164 deletions(-) create mode 100644 MenubarCountdown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename MenubarCountdown/{UserDefaults.swift => AppUserDefaults.swift} (89%) diff --git a/MenubarCountdown.xcodeproj/project.pbxproj b/MenubarCountdown.xcodeproj/project.pbxproj index 3f1f9ce..d65a8d3 100644 --- a/MenubarCountdown.xcodeproj/project.pbxproj +++ b/MenubarCountdown.xcodeproj/project.pbxproj @@ -29,7 +29,7 @@ 4EB204351BED89F900D83EF3 /* MenubarCountdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB204341BED89F900D83EF3 /* MenubarCountdownTests.swift */; }; 4EB204401BED89F900D83EF3 /* MenubarCountdownUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2043F1BED89F900D83EF3 /* MenubarCountdownUITests.swift */; }; 4EB2044E1BED908300D83EF3 /* Stopwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2044D1BED908300D83EF3 /* Stopwatch.swift */; }; - 4EB204501BEE29D700D83EF3 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2044F1BEE29D700D83EF3 /* UserDefaults.swift */; }; + 4EB204501BEE29D700D83EF3 /* AppUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB2044F1BEE29D700D83EF3 /* AppUserDefaults.swift */; }; 4EB204521BEE2E8B00D83EF3 /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB204511BEE2E8B00D83EF3 /* StatusItemView.swift */; }; 4EB204541BEE307900D83EF3 /* CALayerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB204531BEE307900D83EF3 /* CALayerExtensions.swift */; }; /* End PBXBuildFile section */ @@ -82,7 +82,7 @@ 4EB2043F1BED89F900D83EF3 /* MenubarCountdownUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenubarCountdownUITests.swift; sourceTree = ""; }; 4EB204411BED89F900D83EF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4EB2044D1BED908300D83EF3 /* Stopwatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stopwatch.swift; sourceTree = ""; }; - 4EB2044F1BEE29D700D83EF3 /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = ""; }; + 4EB2044F1BEE29D700D83EF3 /* AppUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUserDefaults.swift; sourceTree = ""; }; 4EB204511BEE2E8B00D83EF3 /* StatusItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemView.swift; sourceTree = ""; }; 4EB204531BEE307900D83EF3 /* CALayerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayerExtensions.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -191,7 +191,7 @@ 4E4865991BEE79EA00C159BF /* TextField.swift */, 4E4865B21BEFC63700C159BF /* TimerExpiredAlert.xib */, 4E48659B1BEE81C200C159BF /* TimerExpiredAlertController.swift */, - 4EB2044F1BEE29D700D83EF3 /* UserDefaults.swift */, + 4EB2044F1BEE29D700D83EF3 /* AppUserDefaults.swift */, 4EB204241BED89F900D83EF3 /* ViewController.swift */, ); path = MenubarCountdown; @@ -278,7 +278,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0710; + LastUpgradeCheck = 1110; ORGANIZATIONNAME = "Kristopher Johnson"; TargetAttributes = { 4EB2041E1BED89F900D83EF3 = { @@ -297,7 +297,7 @@ }; buildConfigurationList = 4EB2041A1BED89F900D83EF3 /* Build configuration list for PBXProject "MenubarCountdown" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -357,7 +357,7 @@ 4E4865981BEE749E00C159BF /* StartTimerDialogController.swift in Sources */, 4EB204541BEE307900D83EF3 /* CALayerExtensions.swift in Sources */, 4E48659A1BEE79EA00C159BF /* TextField.swift in Sources */, - 4EB204501BEE29D700D83EF3 /* UserDefaults.swift in Sources */, + 4EB204501BEE29D700D83EF3 /* AppUserDefaults.swift in Sources */, 4EB204251BED89F900D83EF3 /* ViewController.swift in Sources */, 4E4865A11BEE880A00C159BF /* DTrace.m in Sources */, 4E4865961BEE721C00C159BF /* CATransactionExtensions.swift in Sources */, @@ -406,17 +406,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -438,12 +449,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "-DDEBUG"; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -451,17 +463,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -477,9 +500,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -487,8 +512,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = MenubarCountdown/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kristopherjohnson.MenubarCountdown; @@ -501,8 +527,9 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Apple Development"; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = MenubarCountdown/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = net.kristopherjohnson.MenubarCountdown; diff --git a/MenubarCountdown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MenubarCountdown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MenubarCountdown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MenubarCountdown.xcodeproj/xcshareddata/xcschemes/MenubarCountdown.xcscheme b/MenubarCountdown.xcodeproj/xcshareddata/xcschemes/MenubarCountdown.xcscheme index 315ebe0..418a136 100644 --- a/MenubarCountdown.xcodeproj/xcshareddata/xcschemes/MenubarCountdown.xcscheme +++ b/MenubarCountdown.xcodeproj/xcshareddata/xcschemes/MenubarCountdown.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -49,17 +58,6 @@ - - - - - - - - String { - var result = NSUserDefaults.standardUserDefaults().stringForKey(UserDefaults.AnnouncementTextKey) + var result = UserDefaults.standard.string(forKey: AppUserDefaults.AnnouncementTextKey) if (result == nil) || result!.isEmpty { result = NSLocalizedString("The Menubar Countdown timer has reached zero.", comment: "Default announcement text") @@ -211,10 +214,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { func showTimerExpiredAlert() { Log.debug("show timer-expired alert") - NSApp.activateIgnoringOtherApps(true) + NSApp.activate(ignoringOtherApps: true) if timerExpiredAlertController == nil { - NSBundle.mainBundle().loadNibNamed("TimerExpiredAlert", + Bundle.main.loadNibNamed("TimerExpiredAlert", owner: self, topLevelObjects: nil) assert(timerExpiredAlertController != nil, "timerExpiredAlertController outlet must be set") @@ -227,10 +230,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBAction func showStartTimerDialog(sender: AnyObject) { Log.debug("show start timer dialog") - dismissTimerExpiredAlert(sender) + dismissTimerExpiredAlert(sender: sender) if startTimerDialogController == nil { - NSBundle.mainBundle().loadNibNamed("StartTimerDialog", + Bundle.main.loadNibNamed("StartTimerDialog", owner: self, topLevelObjects: nil) assert(startTimerDialogController != nil, "startTimerDialogController must be set") @@ -241,11 +244,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBAction func startTimerDialogStartButtonWasClicked(sender: AnyObject) { Log.debug("start button was clicked") - dismissTimerExpiredAlert(sender) + dismissTimerExpiredAlert(sender: sender) - startTimerDialogController.dismissDialog(sender) + startTimerDialogController.dismissDialog(sender: sender) - NSUserDefaults.standardUserDefaults().synchronize() + UserDefaults.standard.synchronize() timerSettingSeconds = Int(startTimerDialogController.timerInterval) DTraceStartTimer(Int32(timerSettingSeconds)) @@ -255,7 +258,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { canResume = true stopwatch.reset() - updateStatusItemTitle(timerSettingSeconds) + updateStatusItemTitle(timeRemaining: timerSettingSeconds) statusItemView.showTitle() waitForNextSecond() @@ -294,7 +297,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { stopwatch.reset() - updateStatusItemTitle(timerSettingSeconds) + updateStatusItemTitle(timeRemaining: timerSettingSeconds) statusItemView.showTitle() waitForNextSecond() @@ -305,18 +308,18 @@ class AppDelegate: NSObject, NSApplicationDelegate { if timerExpiredAlertController != nil { timerExpiredAlertController.close() } - stopTimer(sender) + stopTimer(sender: sender) } @IBAction func restartCountdownWasClicked(sender: AnyObject) { Log.debug("restart countdown was clicked") - dismissTimerExpiredAlert(sender) - showStartTimerDialog(sender) + dismissTimerExpiredAlert(sender: sender) + showStartTimerDialog(sender: sender) } @IBAction func showAboutPanel(sender: AnyObject) { Log.debug("show About panel") - NSApp.activateIgnoringOtherApps(true) + NSApp.activate(ignoringOtherApps: true) NSApp.orderFrontStandardAboutPanel(sender) } } diff --git a/MenubarCountdown/UserDefaults.swift b/MenubarCountdown/AppUserDefaults.swift similarity index 89% rename from MenubarCountdown/UserDefaults.swift rename to MenubarCountdown/AppUserDefaults.swift index 35fc15a..486e555 100644 --- a/MenubarCountdown/UserDefaults.swift +++ b/MenubarCountdown/AppUserDefaults.swift @@ -22,7 +22,7 @@ import Foundation -struct UserDefaults { +struct AppUserDefaults { static let TimerHoursKey = "TimerHours" static let TimerMinutesKey = "TimerMinutes" static let TimerSecondsKey = "TimerSeconds" @@ -37,9 +37,9 @@ struct UserDefaults { static let ShowSeconds = "ShowSeconds" static func registerUserDefaults() { - if let plistPath = NSBundle.mainBundle().pathForResource("UserDefaults", ofType: "plist") { - if let dict = NSDictionary(contentsOfFile: plistPath) as? [String : AnyObject] { - NSUserDefaults.standardUserDefaults().registerDefaults(dict) + if let plistPath = Bundle.main.path(forResource: "UserDefaults", ofType: "plist") { + if let dict = NSDictionary(contentsOfFile: plistPath) as? [String : Any] { + UserDefaults.standard.register(defaults: dict) } else { Log.error("unable to load UserDefaults.plist") diff --git a/MenubarCountdown/CALayerExtensions.swift b/MenubarCountdown/CALayerExtensions.swift index 2bed0e3..2176ec5 100644 --- a/MenubarCountdown/CALayerExtensions.swift +++ b/MenubarCountdown/CALayerExtensions.swift @@ -24,12 +24,12 @@ import Cocoa // MARK: Create layer from image resource extension CALayer { - static func createImageNamed(name: String) -> CGImageRef? { - var image: CGImageRef? = nil + static func createImageNamed(name: String) -> CGImage? { + var image: CGImage? = nil - if let path = NSBundle.mainBundle().pathForResource(name, ofType: nil) { - let url = NSURL.fileURLWithPath(path) - if let imageSource = CGImageSourceCreateWithURL(url, nil) { + if let path = Bundle.main.path(forResource: name, ofType: nil) { + let url = NSURL.fileURL(withPath: path) + if let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) { image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) } else { @@ -45,16 +45,16 @@ extension CALayer { static func newLayerWithContentsFromFileNamed(name: String) -> CALayer { let newLayer = CALayer() - newLayer.setContentsFromFileNamed(name) + newLayer.setContentsFromFileNamed(name: name) return newLayer } func setContentsFromFileNamed(name: String) { - if let image = CALayer.createImageNamed(name) { - let imageWidth = CGImageGetWidth(image) - let imageHeight = CGImageGetHeight(image) + if let image = CALayer.createImageNamed(name: name) { + let imageWidth = CGFloat(image.width) + let imageHeight = CGFloat(image.height) - self.bounds = CGRectMake(0.0, 0.0, CGFloat(imageWidth), CGFloat(imageHeight)) + self.bounds = CGRect(x: 0.0, y: 0.0, width: imageWidth, height: imageHeight) self.contents = image } else { @@ -66,9 +66,9 @@ extension CALayer { // MARK: Layer coordinate system extension CALayer { func orientBottomLeft() { - self.anchorPoint = CGPointZero - self.position = CGPointZero - self.contentsGravity = kCAGravityBottomLeft + self.anchorPoint = CGPoint.zero + self.position = CGPoint.zero + self.contentsGravity = CALayerContentsGravity.bottomLeft } } @@ -77,7 +77,7 @@ extension CALayer { @nonobjc static let BlinkAnimationKey = "CALayer_Additions_BlinkAnimation" func addBlinkAnimation() { - if let _ = animationForKey(CALayer.BlinkAnimationKey) { + if let _ = animation(forKey: CALayer.BlinkAnimationKey) { return } @@ -91,12 +91,12 @@ extension CALayer { // Cycle between 0 and full opacity animation.fromValue = 0.0 animation.toValue = 1.0 - animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - addAnimation(animation, forKey: CALayer.BlinkAnimationKey) + add(animation, forKey: CALayer.BlinkAnimationKey) } func removeBlinkAnimation() { - removeAnimationForKey(CALayer.BlinkAnimationKey) + removeAnimation(forKey: CALayer.BlinkAnimationKey) } } diff --git a/MenubarCountdown/CATransactionExtensions.swift b/MenubarCountdown/CATransactionExtensions.swift index ca50dba..e733aa5 100644 --- a/MenubarCountdown/CATransactionExtensions.swift +++ b/MenubarCountdown/CATransactionExtensions.swift @@ -31,7 +31,7 @@ extension CATransaction { extension CATransaction { func setDuration(duration: Float) { - CATransaction.setValue(NSNumber(float: duration), + CATransaction.setValue(NSNumber(value: duration), forKey: kCATransactionAnimationDuration) } } diff --git a/MenubarCountdown/Log.swift b/MenubarCountdown/Log.swift index 56600ee..d724858 100644 --- a/MenubarCountdown/Log.swift +++ b/MenubarCountdown/Log.swift @@ -25,8 +25,8 @@ import Foundation struct Log { /// Log an error message - static func error(message: String, - function: String = __FUNCTION__, file: String = __FILE__, line: Int32 = __LINE__) + static func error(_ message: String, + function: String = #function, file: String = #file, line: Int32 = #line) { let filename = file.lastPathComponent @@ -37,8 +37,8 @@ struct Log { } /// Log a debug-level message - static func debug(message: String, - function: String = __FUNCTION__, file: String = __FILE__, line: Int32 = __LINE__) + static func debug(_ message: String, + function: String = #function, file: String = #file, line: Int32 = #line) { #if DEBUG let filename = file.lastPathComponent diff --git a/MenubarCountdown/StartTimerDialogController.swift b/MenubarCountdown/StartTimerDialogController.swift index 438f256..b755605 100644 --- a/MenubarCountdown/StartTimerDialogController.swift +++ b/MenubarCountdown/StartTimerDialogController.swift @@ -25,17 +25,17 @@ import Cocoa class StartTimerDialogController: NSWindowController { @IBOutlet var startTimerDialog: NSWindow! - var timerInterval: NSTimeInterval { - let defaults = NSUserDefaults.standardUserDefaults() - let hours = defaults.integerForKey(UserDefaults.TimerHoursKey); - let minutes = defaults.integerForKey(UserDefaults.TimerMinutesKey); - let seconds = defaults.integerForKey(UserDefaults.TimerSecondsKey); - return NSTimeInterval((hours * 3600) + (minutes * 60) + seconds); + var timerInterval: TimeInterval { + let defaults = UserDefaults.standard + let hours = defaults.integer(forKey: AppUserDefaults.TimerHoursKey); + let minutes = defaults.integer(forKey: AppUserDefaults.TimerMinutesKey); + let seconds = defaults.integer(forKey: AppUserDefaults.TimerSecondsKey); + return TimeInterval((hours * 3600) + (minutes * 60) + seconds); } func showDialog() { - NSApp.activateIgnoringOtherApps(true) - if !startTimerDialog.visible { + NSApp.activate(ignoringOtherApps: true) + if !startTimerDialog.isVisible { startTimerDialog.center() startTimerDialog.makeFirstResponder(nil) } diff --git a/MenubarCountdown/StatusItemView.swift b/MenubarCountdown/StatusItemView.swift index b1f378d..497c0bc 100644 --- a/MenubarCountdown/StatusItemView.swift +++ b/MenubarCountdown/StatusItemView.swift @@ -42,7 +42,7 @@ import Cocoa /// /// The application can cause the title to blink by setting the `isTitleBlinking` property. /// -class StatusItemView: NSView, NSMenuDelegate { +class StatusItemView: NSView, NSMenuDelegate, CALayerDelegate { static let IconPaddingWidth = CGFloat(3) static let TitlePaddingWidth = CGFloat(6) static let TitlePaddingHeight = CGFloat(3) @@ -63,8 +63,13 @@ class StatusItemView: NSView, NSMenuDelegate { var isTitleBlinking = false { didSet { if titleLayer != nil { - if isTitleBlinking { titleLayer.addBlinkAnimation() } - else { titleLayer.removeBlinkAnimation() } + if isTitleBlinking { + titleLayer.addBlinkAnimation() + + } + else { + titleLayer.removeBlinkAnimation() + } titleLayer.setNeedsDisplay() } @@ -92,15 +97,15 @@ class StatusItemView: NSView, NSMenuDelegate { backgroundLayer = self.layer - iconLayer = CALayer.newLayerWithContentsFromFileNamed("MenubarIcon.png") + iconLayer = CALayer.newLayerWithContentsFromFileNamed(name: "MenubarIcon.png") iconLayer.orientBottomLeft() - iconLayer.position = CGPointMake(StatusItemView.IconPaddingWidth, 0.0) + iconLayer.position = CGPoint(x: StatusItemView.IconPaddingWidth, y: 0.0) backgroundLayer.addSublayer(iconLayer) - highlightIconLayer = CALayer.newLayerWithContentsFromFileNamed("MenubarIconInverse.png") + highlightIconLayer = CALayer.newLayerWithContentsFromFileNamed(name: "MenubarIconInverse.png") highlightIconLayer.orientBottomLeft() highlightIconLayer.position = iconLayer.position - highlightIconLayer.hidden = true + highlightIconLayer.isHidden = true backgroundLayer.addSublayer(highlightIconLayer) titleLayer = makeTitleLayer() @@ -112,10 +117,10 @@ class StatusItemView: NSView, NSMenuDelegate { func showIcon() { if isTitleVisible { isTitleVisible = false - titleLayer.hidden = true + titleLayer.isHidden = true - iconLayer.hidden = isMenuVisible - highlightIconLayer.hidden = !isMenuVisible + iconLayer.isHidden = isMenuVisible + highlightIconLayer.isHidden = !isMenuVisible updateStatusItemSize() } @@ -125,12 +130,12 @@ class StatusItemView: NSView, NSMenuDelegate { if (!isTitleVisible) { isTitleVisible = true - iconLayer.hidden = true - highlightIconLayer.hidden = true + iconLayer.isHidden = true + highlightIconLayer.isHidden = true updateStatusItemSize() - titleLayer.hidden = false + titleLayer.isHidden = false } } @@ -155,7 +160,7 @@ class StatusItemView: NSView, NSMenuDelegate { let titleBounds = titleBoundingRect() let desiredWidth = titleBounds.size.width + 2 * StatusItemView.TitlePaddingWidth let desiredHeight = self.bounds.size.height - newLayer.bounds = CGRectMake(0.0, 0.0, desiredWidth, desiredHeight) + newLayer.bounds = CGRect(x: 0.0, y: 0.0, width: desiredWidth, height: desiredHeight) // drawLayer:inContext: will set the layer's contents newLayer.delegate = self @@ -168,41 +173,43 @@ class StatusItemView: NSView, NSMenuDelegate { let newTitleLayer = makeTitleLayer() if !isTitleVisible { - newTitleLayer.hidden = true + newTitleLayer.isHidden = true } - let oldTitleLayer = titleLayer - titleLayer = newTitleLayer - backgroundLayer.replaceSublayer(oldTitleLayer, with: newTitleLayer) + // #KJ TODO: what do we do if titleLayer is nil? + if let oldTitleLayer = titleLayer { + titleLayer = newTitleLayer + backgroundLayer.replaceSublayer(oldTitleLayer, with: newTitleLayer) + } } - func titleAttributes() -> [String : AnyObject] { + func titleAttributes() -> [NSAttributedString.Key : Any] { return [ - NSFontAttributeName: NSFont.menuBarFontOfSize(0), - NSForegroundColorAttributeName: titleForegroundColor() + NSAttributedString.Key.font: NSFont.menuBarFont(ofSize: 0), + NSAttributedString.Key.foregroundColor: titleForegroundColor() ] } func titleForegroundColor() -> NSColor { - if isMenuVisible { return NSColor.whiteColor() } - if isTitleBlinking { return NSColor.redColor() } - else { return NSColor.blackColor() } + if isMenuVisible { return NSColor.white } + if isTitleBlinking { return NSColor.red } + else { return NSColor.black } } func titleBoundingRect() -> NSRect { // TODO: boundingRectWithSize(options:attributes:) is deprecated; replace appropriately let infiniteSize = NSMakeSize(CGFloat.infinity, CGFloat.infinity) - return (title as NSString).boundingRectWithSize(infiniteSize, - options: NSStringDrawingOptions(rawValue: 0), - attributes: titleAttributes()) + return (title as NSString).boundingRect(with: infiniteSize, + options: NSString.DrawingOptions(rawValue: 0), + attributes: titleAttributes()) } - override func drawLayer(layer: CALayer, inContext ctx: CGContext) { + func draw(_ layer: CALayer, in ctx: CGContext) { // Set up graphics context so that we can use Application Kit drawing functions - let nsGraphicsContext = NSGraphicsContext(CGContext: ctx, flipped: false) + let nsGraphicsContext = NSGraphicsContext(cgContext: ctx, flipped: false) NSGraphicsContext.saveGraphicsState() - NSGraphicsContext.setCurrentContext(nsGraphicsContext) + NSGraphicsContext.current = nsGraphicsContext if layer == backgroundLayer { drawBackground() @@ -216,7 +223,8 @@ class StatusItemView: NSView, NSMenuDelegate { func drawBackground() { if let statusItem = statusItem { - statusItem.drawStatusBarBackgroundInRect(self.bounds, withHighlight: isMenuVisible) + // #KJ TODO: Eliminate use of this deprecated method + statusItem.drawStatusBarBackground(in: self.bounds, withHighlight: isMenuVisible) } } @@ -224,16 +232,17 @@ class StatusItemView: NSView, NSMenuDelegate { let position = NSMakePoint( StatusItemView.TitlePaddingWidth, StatusItemView.TitlePaddingHeight) - (title as NSString).drawAtPoint(position, withAttributes: titleAttributes()) + (title as NSString).draw(at: position, withAttributes: titleAttributes()) } // MARK: Mouse and menu handling - override func mouseDown(theEvent: NSEvent) { + override func mouseDown(with event: NSEvent) { if let menu = menu { menu.delegate = self if let statusItem = statusItem { - statusItem.popUpStatusItemMenu(menu) + // #KJ TODO: popUpMenu is deprecated + statusItem.popUpMenu(menu) } else { Log.error("statusItem property not set") @@ -244,12 +253,12 @@ class StatusItemView: NSView, NSMenuDelegate { } } - override func rightMouseDown(theEvent: NSEvent) { + override func rightMouseDown(with event: NSEvent) { // Treat right-click just like left-click - mouseDown(theEvent) + mouseDown(with: event) } - func menuWillOpen(menu: NSMenu) { + func menuWillOpen(_ menu: NSMenu) { isMenuVisible = true // Disable animation for the following changes @@ -265,14 +274,14 @@ class StatusItemView: NSView, NSMenuDelegate { } else { // Hide the normal icon and show the highlighted icon - iconLayer.hidden = true - highlightIconLayer.hidden = false + iconLayer.isHidden = true + highlightIconLayer.isHidden = false } CATransaction.commit() } - func menuDidClose(menu: NSMenu) { + func menuDidClose(_ menu: NSMenu) { isMenuVisible = false menu.delegate = nil @@ -290,8 +299,8 @@ class StatusItemView: NSView, NSMenuDelegate { } else { // Show the normal icon and hide the highlighted icon - iconLayer.hidden = false - highlightIconLayer.hidden = true + iconLayer.isHidden = false + highlightIconLayer.isHidden = true } CATransaction.commit() diff --git a/MenubarCountdown/Stopwatch.swift b/MenubarCountdown/Stopwatch.swift index c39ab7c..59dab14 100644 --- a/MenubarCountdown/Stopwatch.swift +++ b/MenubarCountdown/Stopwatch.swift @@ -28,7 +28,7 @@ import Cocoa /// the current time and the last call to `reset` (or `init`). @objc class Stopwatch: NSObject { - private var startTime: NSTimeInterval + private var startTime: TimeInterval /// Initialize with current time as start point override init() { @@ -41,7 +41,7 @@ import Cocoa } /// Calculate elapsed time since initialization or last call to reset() - func elapsedTimeInterval() -> NSTimeInterval { + func elapsedTimeInterval() -> TimeInterval { return CACurrentMediaTime() - startTime } } diff --git a/MenubarCountdown/TextField.swift b/MenubarCountdown/TextField.swift index 522dd89..1f0d42b 100644 --- a/MenubarCountdown/TextField.swift +++ b/MenubarCountdown/TextField.swift @@ -33,46 +33,46 @@ import Cocoa /// http://stackoverflow.com/questions/970707/cocoa-keyboard-shortcuts-in-dialog-without-an-edit-menu class TextField: NSTextField { - override func performKeyEquivalent(event: NSEvent) -> Bool { + override func performKeyEquivalent(with key: NSEvent) -> Bool { // Map Command-X to Cut // Command-C to Copy // Command-V to Paste // Command-A to Select All // Command-Z to Undo // Command-Shift-Z to Redo - if event.type == NSEventType.KeyDown { - let commandKeyMask = NSEventModifierFlags.CommandKeyMask.rawValue - let commandShiftKeyMask = commandKeyMask | NSEventModifierFlags.ShiftKeyMask.rawValue + if key.type == .keyDown { + let commandKeyMask = NSEvent.ModifierFlags.command.rawValue + let commandShiftKeyMask = commandKeyMask | NSEvent.ModifierFlags.shift.rawValue - let modifierFlagsMask = event.modifierFlags.rawValue - & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue + let modifierFlagsMask = key.modifierFlags.rawValue + & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue if modifierFlagsMask == commandKeyMask { - if let chars = event.charactersIgnoringModifiers { + if let chars = key.charactersIgnoringModifiers { switch chars { - case "x": return sendFirstResponderAction(Selector("cut:")) - case "c": return sendFirstResponderAction(Selector("copy")) - case "v": return sendFirstResponderAction(Selector("paste:")) - case "a": return sendFirstResponderAction(Selector("selectAll:")) - case "z": return sendFirstResponderAction(Selector("undo:")) + case "x": return sendFirstResponderAction(#selector(NSText.cut(_:))) + case "c": return sendFirstResponderAction(#selector(NSObject.copy as () -> Any)) + case "v": return sendFirstResponderAction(#selector(NSText.paste(_:))) + case "a": return sendFirstResponderAction(#selector(NSStandardKeyBindingResponding.selectAll(_:))) + case "z": return sendFirstResponderAction(#selector(UndoManager.undo)) default: break } } } else if modifierFlagsMask == commandShiftKeyMask { - if let chars = event.charactersIgnoringModifiers { + if let chars = key.charactersIgnoringModifiers { switch chars { - case "Z": return sendFirstResponderAction(Selector("redo:")) + case "Z": return sendFirstResponderAction(#selector(UndoManager.redo)) default: break } } } } - return super.performKeyEquivalent(event) + return super.performKeyEquivalent(with: key) } - func sendFirstResponderAction(action: Selector) -> Bool { + func sendFirstResponderAction(_ action: Selector) -> Bool { return NSApp.sendAction(action, to: self.window?.firstResponder, from: self) } -} \ No newline at end of file +} diff --git a/MenubarCountdown/TimerExpiredAlertController.swift b/MenubarCountdown/TimerExpiredAlertController.swift index 691ccb3..d4399c0 100644 --- a/MenubarCountdown/TimerExpiredAlertController.swift +++ b/MenubarCountdown/TimerExpiredAlertController.swift @@ -28,7 +28,7 @@ class TimerExpiredAlertController: NSWindowController { func showAlert() { if let w = self.window { w.makeFirstResponder(nil) - w.level = Int(CGWindowLevelForKey(.FloatingWindowLevelKey)) + w.level = .floating w.center() w.makeKeyAndOrderFront(self) } diff --git a/MenubarCountdown/ViewController.swift b/MenubarCountdown/ViewController.swift index db27d4c..35f19bd 100644 --- a/MenubarCountdown/ViewController.swift +++ b/MenubarCountdown/ViewController.swift @@ -30,12 +30,10 @@ class ViewController: NSViewController { // Do any additional setup after loading the view. } - override var representedObject: AnyObject? { + override var representedObject: Any? { didSet { - // Update the view, if already loaded. + // Update the view, if already loaded. } } - - } From 247c2ca7ca8f268042916ddfbc40a6bf986507b9 Mon Sep 17 00:00:00 2001 From: Kristopher Johnson Date: Thu, 17 Oct 2019 19:57:27 -0400 Subject: [PATCH 2/3] Make necessary members of AppDelegate class key-value-coding compliant This change eliminates NIB-loading exceptions at startup. However, the status bar item is not displayed, nor is anything else shown on screen. --- MenubarCountdown/AppDelegate.swift | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/MenubarCountdown/AppDelegate.swift b/MenubarCountdown/AppDelegate.swift index f8101a6..e6c70cc 100644 --- a/MenubarCountdown/AppDelegate.swift +++ b/MenubarCountdown/AppDelegate.swift @@ -33,13 +33,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { var secondsRemaining = 0 /// Indicates whether timer is running - var isTimerRunning = false + @objc var isTimerRunning = false /// Indicates whether the timer can be paused - var canPause = false + @objc var canPause = false /// Indicates whether the timer can be resumed - var canResume = false + @objc var canResume = false var stopwatch: Stopwatch! @@ -76,7 +76,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { updateStatusItemTitle(timeRemaining: 0) if UserDefaults.standard.bool(forKey: AppUserDefaults.ShowStartDialogOnLaunchKey) { - showStartTimerDialog(sender: self) + showStartTimerDialog(self) } } @@ -92,12 +92,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { Timer.scheduledTimer(timeInterval: intervalToNextSecond, target: self, - selector: #selector(nextSecondTimerDidFire(timer:)), + selector: #selector(nextSecondTimerDidFire(_:)), userInfo: nil, repeats: false) } - @objc func nextSecondTimerDidFire(timer: Timer) { + @objc func nextSecondTimerDidFire(_ timer: Timer) { if isTimerRunning { secondsRemaining = Int(round(TimeInterval(timerSettingSeconds) - stopwatch.elapsedTimeInterval())) DTraceTimerTick(Int32(secondsRemaining)) @@ -227,10 +227,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { // MARK: Menu item and button event handlers - @IBAction func showStartTimerDialog(sender: AnyObject) { + @IBAction func showStartTimerDialog(_ sender: AnyObject) { Log.debug("show start timer dialog") - dismissTimerExpiredAlert(sender: sender) + dismissTimerExpiredAlert(sender) if startTimerDialogController == nil { Bundle.main.loadNibNamed("StartTimerDialog", @@ -241,10 +241,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { startTimerDialogController.showDialog() } - @IBAction func startTimerDialogStartButtonWasClicked(sender: AnyObject) { + @IBAction func startTimerDialogStartButtonWasClicked(_ sender: AnyObject) { Log.debug("start button was clicked") - dismissTimerExpiredAlert(sender: sender) + dismissTimerExpiredAlert(sender) startTimerDialogController.dismissDialog(sender: sender) @@ -264,7 +264,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { waitForNextSecond() } - @IBAction func stopTimer(sender: AnyObject) { + @IBAction func stopTimer(_ sender: AnyObject) { Log.debug("stop timer") DTraceStartTimer(Int32(secondsRemaining)) @@ -276,7 +276,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { statusItemView.showIcon() } - @IBAction func pauseTimer(sender: AnyObject) { + @IBAction func pauseTimer(_ sender: AnyObject) { Log.debug("pause timer") DTracePauseTimer(Int32(secondsRemaining)) @@ -285,7 +285,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { canResume = true } - @IBAction func resumeTimer(sender: AnyObject) { + @IBAction func resumeTimer(_ sender: AnyObject) { Log.debug("resume timer") DTraceResumeTimer(Int32(secondsRemaining)) @@ -303,21 +303,21 @@ class AppDelegate: NSObject, NSApplicationDelegate { waitForNextSecond() } - @IBAction func dismissTimerExpiredAlert(sender: AnyObject) { + @IBAction func dismissTimerExpiredAlert(_ sender: AnyObject) { Log.debug("dismiss timer expired alert") if timerExpiredAlertController != nil { timerExpiredAlertController.close() } - stopTimer(sender: sender) + stopTimer(sender) } - @IBAction func restartCountdownWasClicked(sender: AnyObject) { + @IBAction func restartCountdownWasClicked(_ sender: AnyObject) { Log.debug("restart countdown was clicked") - dismissTimerExpiredAlert(sender: sender) - showStartTimerDialog(sender: sender) + dismissTimerExpiredAlert(sender) + showStartTimerDialog(sender) } - @IBAction func showAboutPanel(sender: AnyObject) { + @IBAction func showAboutPanel(_ sender: AnyObject) { Log.debug("show About panel") NSApp.activate(ignoringOtherApps: true) NSApp.orderFrontStandardAboutPanel(sender) From 0ad2417701e30a8c21cd7a72c3f54afd4078b73e Mon Sep 17 00:00:00 2001 From: Kristopher Johnson Date: Thu, 17 Oct 2019 20:25:20 -0400 Subject: [PATCH 3/3] Fix signatures of NSApplicationDelegate method implementations --- MenubarCountdown/AppDelegate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MenubarCountdown/AppDelegate.swift b/MenubarCountdown/AppDelegate.swift index e6c70cc..e12acdc 100644 --- a/MenubarCountdown/AppDelegate.swift +++ b/MenubarCountdown/AppDelegate.swift @@ -57,7 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { AppUserDefaults.registerUserDefaults() } - func applicationDidFinishLaunching(aNotification: NSNotification) { + func applicationDidFinishLaunching(_ notification: Notification) { Log.debug("application did finish launching") stopwatch.reset() @@ -80,7 +80,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } - func applicationWillTerminate(aNotification: NSNotification) { + func applicationWillTerminate(_ notification: Notification) { Log.debug("application will terminate") }