diff --git a/Motivation.xcodeproj/project.pbxproj b/Motivation.xcodeproj/project.pbxproj index f9c8436..dc7ca6c 100644 --- a/Motivation.xcodeproj/project.pbxproj +++ b/Motivation.xcodeproj/project.pbxproj @@ -171,11 +171,13 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0920; ORGANIZATIONNAME = "Sam Soffes"; TargetAttributes = { 21F222251B73B5AC00B9A11A = { CreatedOnToolsVersion = 6.4; + LastSwiftMigration = 0830; + ProvisioningStyle = Automatic; }; 21F222391B73B60100B9A11A = { CreatedOnToolsVersion = 6.4; @@ -269,18 +271,26 @@ 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_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_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; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -302,6 +312,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -313,18 +324,26 @@ 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_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_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; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -338,22 +357,26 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; }; name = Release; }; 21F222321B73B5AC00B9A11A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = Motivation/Info.plist; INSTALL_PATH = "$(HOME)/Library/Screen Savers"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.motiviation; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; WRAPPER_EXTENSION = saver; }; name = Debug; @@ -361,15 +384,17 @@ 21F222331B73B5AC00B9A11A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Developer ID Application"; + CODE_SIGN_IDENTITY = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = Motivation/Info.plist; INSTALL_PATH = "$(HOME)/Library/Screen Savers"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.motiviation; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; WRAPPER_EXTENSION = saver; }; name = Release; diff --git a/Motivation/AgeView.swift b/Motivation/AgeView.swift index e983ab3..9169d90 100644 --- a/Motivation/AgeView.swift +++ b/Motivation/AgeView.swift @@ -13,25 +13,25 @@ class AgeView: ScreenSaverView { // MARK: - Properties - private let textLabel: NSTextField = { + fileprivate let textLabel: NSTextField = { let label = NSTextField() label.translatesAutoresizingMaskIntoConstraints = false - label.editable = false + label.isEditable = false label.drawsBackground = false - label.bordered = false - label.bezeled = false - label.selectable = false - label.textColor = .whiteColor() + label.isBordered = false + label.isBezeled = false + label.isSelectable = false + label.textColor = .white return label }() - private lazy var configurationWindowController: NSWindowController = { + fileprivate lazy var configurationWindowController: NSWindowController = { return ConfigurationWindowController() }() - private var motivationLevel: MotivationLevel + fileprivate var motivationLevel: MotivationLevel - private var birthday: NSDate? { + fileprivate var birthday: Date? { didSet { updateFont() } @@ -41,7 +41,7 @@ class AgeView: ScreenSaverView { // MARK: - Initializers convenience init() { - self.init(frame: CGRectZero, isPreview: false) + self.init(frame: CGRect.zero, isPreview: false) } override init!(frame: NSRect, isPreview: Bool) { @@ -57,22 +57,22 @@ class AgeView: ScreenSaverView { } deinit { - NSNotificationCenter.defaultCenter().removeObserver(self) + NotificationCenter.default.removeObserver(self) } // MARK: - NSView - override func drawRect(rect: NSRect) { - let backgroundColor: NSColor = .blackColor() + override func draw(_ rect: NSRect) { + let backgroundColor: NSColor = .black backgroundColor.setFill() - NSBezierPath.fillRect(bounds) + NSBezierPath.fill(bounds) } // If the screen saver changes size, update the font - override func resizeWithOldSuperviewSize(oldSize: NSSize) { - super.resizeWithOldSuperviewSize(oldSize) + override func resize(withOldSuperviewSize oldSize: NSSize) { + super.resize(withOldSuperviewSize: oldSize) updateFont() } @@ -101,7 +101,7 @@ class AgeView: ScreenSaverView { // MARK: - Private /// Shared initializer - private func initialize() { + fileprivate func initialize() { // Set animation time interval animationTimeInterval = 1 / 30 @@ -111,56 +111,56 @@ class AgeView: ScreenSaverView { // Setup the label addSubview(textLabel) addConstraints([ - NSLayoutConstraint(item: textLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0), - NSLayoutConstraint(item: textLabel, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1, constant: 0) + NSLayoutConstraint(item: textLabel, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0), + NSLayoutConstraint(item: textLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0) ]) // Listen for configuration changes - NSNotificationCenter.defaultCenter().addObserver(self, selector: "motivationLevelDidChange:", name: Preferences.motivationLevelDidChangeNotificationName, object: nil) - NSNotificationCenter.defaultCenter().addObserver(self, selector: "birthdayDidChange:", name: Preferences.birthdayDidChangeNotificationName, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(AgeView.motivationLevelDidChange(_:)), name: NSNotification.Name(rawValue: Preferences.motivationLevelDidChangeNotificationName), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(AgeView.birthdayDidChange(_:)), name: NSNotification.Name(rawValue: Preferences.birthdayDidChangeNotificationName), object: nil) } /// Age calculation - private func ageForBirthday(birthday: NSDate) -> Double { - let calendar = NSCalendar.currentCalendar() - let now = NSDate() + fileprivate func ageForBirthday(_ birthday: Date) -> Double { + let calendar = Calendar.current + let now = Date() // An age is defined as the number of years you've been alive plus the number of days, seconds, and nanoseconds // you've been alive out of that many units in the current year. - let components = calendar.components([NSCalendarUnit.Year, NSCalendarUnit.Day, NSCalendarUnit.Second, NSCalendarUnit.Nanosecond], fromDate: birthday, toDate: now, options: []) + let components = (calendar as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.day, NSCalendar.Unit.second, NSCalendar.Unit.nanosecond], from: birthday, to: now, options: []) // We calculate these every time since the values can change when you cross a boundary. Things are too // complicated to try to figure out when that is and cache them. NSCalendar is made for this. let daysInYear = Double(calendar.daysInYear(now) ?? 365) - let hoursInDay = Double(calendar.rangeOfUnit(NSCalendarUnit.Hour, inUnit: NSCalendarUnit.Day, forDate: now).length) - let minutesInHour = Double(calendar.rangeOfUnit(NSCalendarUnit.Minute, inUnit: NSCalendarUnit.Hour, forDate: now).length) - let secondsInMinute = Double(calendar.rangeOfUnit(NSCalendarUnit.Second, inUnit: NSCalendarUnit.Minute, forDate: now).length) - let nanosecondsInSecond = Double(calendar.rangeOfUnit(NSCalendarUnit.Nanosecond, inUnit: NSCalendarUnit.Second, forDate: now).length) + let hoursInDay = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.hour, in: NSCalendar.Unit.day, for: now).length) + let minutesInHour = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.minute, in: NSCalendar.Unit.hour, for: now).length) + let secondsInMinute = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.second, in: NSCalendar.Unit.minute, for: now).length) + let nanosecondsInSecond = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.nanosecond, in: NSCalendar.Unit.second, for: now).length) // Now that we have all of the values, assembling them is easy. We don't get minutes and hours from the calendar // since it will overflow nicely to seconds. We need days and years since the number of days in a year changes // more frequently. This will handle leap seconds, days, and years. - let seconds = Double(components.second) + (Double(components.nanosecond) / nanosecondsInSecond) + let seconds = Double(components.second!) + (Double(components.nanosecond!) / nanosecondsInSecond) let minutes = seconds / secondsInMinute let hours = minutes / minutesInHour - let days = Double(components.day) + (hours / hoursInDay) - let years = Double(components.year) + (days / daysInYear) + let days = Double(components.day!) + (hours / hoursInDay) + let years = Double(components.year!) + (days / daysInYear) return years } /// Motiviation level changed - @objc private func motivationLevelDidChange(notification: NSNotification?) { + @objc fileprivate func motivationLevelDidChange(_ notification: Notification?) { motivationLevel = Preferences().motivationLevel } /// Birthday changed - @objc private func birthdayDidChange(notification: NSNotification?) { + @objc fileprivate func birthdayDidChange(_ notification: Notification?) { birthday = Preferences().birthday } /// Update the font for the current size - private func updateFont() { + fileprivate func updateFont() { if birthday != nil { textLabel.font = fontWithSize(bounds.width / 10) } else { @@ -169,17 +169,17 @@ class AgeView: ScreenSaverView { } /// Get a font - private func fontWithSize(fontSize: CGFloat, monospace: Bool = true) -> NSFont { + fileprivate func fontWithSize(_ fontSize: CGFloat, monospace: Bool = true) -> NSFont { let font: NSFont if #available(OSX 10.11, *) { - font = .systemFontOfSize(fontSize, weight: NSFontWeightThin) + font = .systemFont(ofSize: fontSize, weight: NSFontWeightThin) } else { font = NSFont(name: "HelveticaNeue-Thin", size: fontSize)! } let fontDescriptor: NSFontDescriptor if monospace { - fontDescriptor = font.fontDescriptor.fontDescriptorByAddingAttributes([ + fontDescriptor = font.fontDescriptor.addingAttributes([ NSFontFeatureSettingsAttribute: [ [ NSFontFeatureTypeIdentifierKey: kNumberSpacingType, diff --git a/Motivation/ConfigurationWindowController.swift b/Motivation/ConfigurationWindowController.swift index 06bd6a7..cf12b14 100644 --- a/Motivation/ConfigurationWindowController.swift +++ b/Motivation/ConfigurationWindowController.swift @@ -27,23 +27,23 @@ class ConfigurationWindowController: NSWindowController { super.windowDidLoad() switch Preferences().motivationLevel { - case .Light: lightRadio.state = NSOnState - case .Moderate: moderateRadio.state = NSOnState - case .Terrifying: terrifyingRadio.state = NSOnState + case .light: lightRadio.state = NSOnState + case .moderate: moderateRadio.state = NSOnState + case .terrifying: terrifyingRadio.state = NSOnState } } // MARK: - Action - @IBAction func close(sender: AnyObject?) { + @IBAction func close(_ sender: AnyObject?) { if let window = window { window.sheetParent?.endSheet(window) } } - @IBAction func levelDidChange(sender: AnyObject?) { - guard let button = sender as? NSButton, level = MotivationLevel(rawValue: UInt(button.tag)) else { return } + @IBAction func levelDidChange(_ sender: AnyObject?) { + guard let button = sender as? NSButton, let level = MotivationLevel(rawValue: UInt(button.tag)) else { return } Preferences().motivationLevel = level } } diff --git a/Motivation/NSCalendar+Motivation.swift b/Motivation/NSCalendar+Motivation.swift index 1cbe0b1..5dec3fb 100644 --- a/Motivation/NSCalendar+Motivation.swift +++ b/Motivation/NSCalendar+Motivation.swift @@ -8,27 +8,26 @@ import Foundation -extension NSCalendar { - func daysInYear(date: NSDate = NSDate()) -> Int? { - let year = components([NSCalendarUnit.Year], fromDate: date).year - return daysInYear(year) +extension Calendar { + func daysInYear(_ date: Date = Date()) -> Int? { + let year = dateComponents([.year], from: date).year + return daysInYear(year!) } - func daysInYear(year: Int) -> Int? { - guard let begin = lastDayOfYear(year - 1), end = lastDayOfYear(year) else { return nil } - return components([NSCalendarUnit.Day], fromDate: begin, toDate: end, options: []).day + func daysInYear(_ year: Int) -> Int? { + guard let begin = lastDayOfYear(year - 1), let end = lastDayOfYear(year) else { return nil } + return dateComponents([.day], from: begin, to: end).day } - func lastDayOfYear(year: Int) -> NSDate? { - let components = NSDateComponents() + func lastDayOfYear(_ year: Int) -> Date? { + var components = DateComponents() components.year = year - guard let years = dateFromComponents(components) else { return nil } + guard let years = date(from: components) else { return nil } - components.month = rangeOfUnit(NSCalendarUnit.Month, inUnit: NSCalendarUnit.Year, forDate: years).length - guard let months = dateFromComponents(components) else { return nil } + components.month = range(of: Calendar.Component.month, in: Calendar.Component.year, for: years)?.count + guard let months = date(from: components) else { return nil } - components.day = rangeOfUnit(NSCalendarUnit.Day, inUnit: NSCalendarUnit.Month, forDate: months).length - - return dateFromComponents(components) + components.day = range(of: Calendar.Component.day, in: Calendar.Component.month, for: months)?.count + return date(from: components) } } diff --git a/Motivation/Preferences.swift b/Motivation/Preferences.swift index 9f5b899..e4947e4 100644 --- a/Motivation/Preferences.swift +++ b/Motivation/Preferences.swift @@ -10,13 +10,13 @@ import Foundation import ScreenSaver enum MotivationLevel: UInt { - case Light, Moderate, Terrifying + case light, moderate, terrifying var decimalPlaces: UInt { switch self { - case Light: return 7 - case Moderate: return 8 - case Terrifying: return 9 + case .light: return 7 + case .moderate: return 8 + case .terrifying: return 9 } } } @@ -28,40 +28,40 @@ class Preferences: NSObject { static var birthdayDidChangeNotificationName = "Preferences.birthdayDidChangeNotification" static var motivationLevelDidChangeNotificationName = "Preferences.motivationLevelDidChangeNotification" - var birthday: NSDate? { + var birthday: Date? { get { - let timestamp = defaults?.objectForKey("Birthday") as? NSTimeInterval - return timestamp.map { NSDate(timeIntervalSince1970: $0) } + let timestamp = defaults?.object(forKey: "Birthday") as? TimeInterval + return timestamp.map { Date(timeIntervalSince1970: $0) } } set { if let date = newValue { - defaults?.setObject(date.timeIntervalSince1970, forKey: "Birthday") + defaults?.set(date.timeIntervalSince1970, forKey: "Birthday") } else { - defaults?.removeObjectForKey("Birthday") + defaults?.removeObject(forKey: "Birthday") } defaults?.synchronize() - NSNotificationCenter.defaultCenter().postNotificationName(self.dynamicType.birthdayDidChangeNotificationName, object: newValue) + NotificationCenter.default.post(name: Notification.Name(rawValue: type(of: self).birthdayDidChangeNotificationName), object: newValue) } } var motivationLevel: MotivationLevel { get { - let uint = defaults?.objectForKey("MotivationLevel") as? UInt - return uint.flatMap { MotivationLevel(rawValue: $0) } ?? .Terrifying + let uint = defaults?.object(forKey: "MotivationLevel") as? UInt + return uint.flatMap { MotivationLevel(rawValue: $0) } ?? .terrifying } set { - defaults?.setObject(newValue.rawValue, forKey: "MotivationLevel") + defaults?.set(newValue.rawValue, forKey: "MotivationLevel") defaults?.synchronize() - NSNotificationCenter.defaultCenter().postNotificationName(self.dynamicType.motivationLevelDidChangeNotificationName, object: newValue.rawValue) + NotificationCenter.default.post(name: Notification.Name(rawValue: type(of: self).motivationLevelDidChangeNotificationName), object: newValue.rawValue) } } - private let defaults: ScreenSaverDefaults? = { - let bundleIdentifier = NSBundle(forClass: Preferences.self).bundleIdentifier + fileprivate let defaults: ScreenSaverDefaults? = { + let bundleIdentifier = Bundle(for: Preferences.self).bundleIdentifier return bundleIdentifier.flatMap { ScreenSaverDefaults(forModuleWithName: $0) } }() } diff --git a/Preview/PreviewWindowController.swift b/Preview/PreviewWindowController.swift index 2c086f5..083a1ff 100644 --- a/Preview/PreviewWindowController.swift +++ b/Preview/PreviewWindowController.swift @@ -22,7 +22,7 @@ class PreviewWindowController: NSWindowController { window?.contentView = screenSaver - NSTimer.scheduledTimerWithTimeInterval(screenSaver.animationTimeInterval, target: screenSaver, selector: "animateOneFrame", userInfo: nil, repeats: true) + _ = Timer.scheduledTimer(timeInterval: screenSaver.animationTimeInterval, target: screenSaver, selector: #selector(AgeView.animateOneFrame), userInfo: nil, repeats: true) }