Skip to content

Commit

Permalink
Migrate UserDefaults and MainWindowController to Swift
Browse files Browse the repository at this point in the history
  • Loading branch information
joelekstrom committed Jan 11, 2022
1 parent 107c330 commit 884477d
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 116 deletions.
14 changes: 8 additions & 6 deletions Fastmate.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
5D14A8EB22C425B400A5ACE9 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 5D14A8EA22C425B400A5ACE9 /* Credits.rtf */; };
5D18D5022685E2EC002999F0 /* PDFKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D18D5012685E2EC002999F0 /* PDFKit.framework */; };
5D1DDBD227897C4D00244F04 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1DDBD127897C4D00244F04 /* AppDelegate.swift */; };
5D1DDBD4278B718000244F04 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1DDBD3278B718000244F04 /* UserDefaults.swift */; };
5D1DDBD6278C483400244F04 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1DDBD5278C483400244F04 /* MainWindowController.swift */; };
5D39BEDD2122383900693D7E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D39BEDC2122383900693D7E /* Assets.xcassets */; };
5D39BEE32122383900693D7E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D39BEE22122383900693D7E /* main.m */; };
5D6E3B372122FF9E00ED16C9 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6E3B362122FF9E00ED16C9 /* WebViewController.m */; };
5D6E3B3B212824AF00ED16C9 /* Fastmate.js in Resources */ = {isa = PBXBuildFile; fileRef = 5D6E3B3A212824AF00ED16C9 /* Fastmate.js */; };
5D6EF77B212E018700C84B4A /* MainWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6EF77A212E018700C84B4A /* MainWindowController.m */; };
5D8DDE2A244399BD00747135 /* KVOBlockObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D8DDE29244399BD00747135 /* KVOBlockObserver.m */; };
5DA21A162128617900C765BF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5DA21A142128617900C765BF /* Main.storyboard */; };
5DA21A19212865F000C765BF /* UnreadCountObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA21A18212865F000C765BF /* UnreadCountObserver.m */; };
Expand All @@ -31,6 +32,8 @@
5D18D5012685E2EC002999F0 /* PDFKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PDFKit.framework; path = System/Library/Frameworks/PDFKit.framework; sourceTree = SDKROOT; };
5D1DDBD027897C4C00244F04 /* Fastmate-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Fastmate-Bridging-Header.h"; sourceTree = "<group>"; };
5D1DDBD127897C4D00244F04 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5D1DDBD3278B718000244F04 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = "<group>"; };
5D1DDBD5278C483400244F04 /* MainWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = "<group>"; };
5D39BED62122383800693D7E /* Fastmate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Fastmate.app; sourceTree = BUILT_PRODUCTS_DIR; };
5D39BEDC2122383900693D7E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5D39BEE12122383900693D7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand All @@ -39,8 +42,6 @@
5D6E3B352122FF9E00ED16C9 /* WebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebViewController.h; sourceTree = "<group>"; };
5D6E3B362122FF9E00ED16C9 /* WebViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = "<group>"; };
5D6E3B3A212824AF00ED16C9 /* Fastmate.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = Fastmate.js; sourceTree = "<group>"; };
5D6EF779212E018700C84B4A /* MainWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MainWindowController.h; sourceTree = "<group>"; };
5D6EF77A212E018700C84B4A /* MainWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainWindowController.m; sourceTree = "<group>"; };
5D8DDE28244399BD00747135 /* KVOBlockObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KVOBlockObserver.h; sourceTree = "<group>"; };
5D8DDE29244399BD00747135 /* KVOBlockObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KVOBlockObserver.m; sourceTree = "<group>"; };
5D8DDE2B2443AD4800747135 /* UserDefaultsKeys.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserDefaultsKeys.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -96,8 +97,8 @@
isa = PBXGroup;
children = (
5D1DDBD127897C4D00244F04 /* AppDelegate.swift */,
5D6EF779212E018700C84B4A /* MainWindowController.h */,
5D6EF77A212E018700C84B4A /* MainWindowController.m */,
5D1DDBD3278B718000244F04 /* UserDefaults.swift */,
5D1DDBD5278C483400244F04 /* MainWindowController.swift */,
5D6E3B352122FF9E00ED16C9 /* WebViewController.h */,
5D6E3B362122FF9E00ED16C9 /* WebViewController.m */,
5D0F8E93240B910A00287BD1 /* PrintManager.h */,
Expand Down Expand Up @@ -203,8 +204,9 @@
files = (
5D1DDBD227897C4D00244F04 /* AppDelegate.swift in Sources */,
5D8DDE2A244399BD00747135 /* KVOBlockObserver.m in Sources */,
5D1DDBD6278C483400244F04 /* MainWindowController.swift in Sources */,
5DC5E5952457F96D00C30171 /* NotificationCenter.m in Sources */,
5D6EF77B212E018700C84B4A /* MainWindowController.m in Sources */,
5D1DDBD4278B718000244F04 /* UserDefaults.swift in Sources */,
5DA21A19212865F000C765BF /* UnreadCountObserver.m in Sources */,
5D39BEE32122383900693D7E /* main.m in Sources */,
5DCFE8792258CEC5006B1A21 /* SettingsViewController.m in Sources */,
Expand Down
40 changes: 14 additions & 26 deletions Fastmate/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation
import AppKit
import Carbon.HIToolbox
import Combine

class FastmateApplication: NSApplication {
override func sendEvent(_ event: NSEvent) {
Expand All @@ -14,9 +15,8 @@ class FastmateApplication: NSApplication {
}

class AppDelegate: NSObject, NSApplicationDelegate {
private var workspaceDidWakeObserver: Any?
private var statusBarIconObserver: KVOBlockObserver?
private var isAutomaticUpdateCheck = false
private var subscriptions = Set<AnyCancellable>()

lazy var unreadCountObserver = UnreadCountObserver()

Expand All @@ -27,37 +27,25 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationDidFinishLaunching(_ notification: Notification) {
workspaceDidWakeObserver = NSWorkspace.shared.notificationCenter.addObserver(
forName: NSWorkspace.didWakeNotification,
object: nil,
queue: .main
) { _ in
self.mainWebViewController?.reload()
}
NSWorkspace.shared.notificationCenter.publisher(for: NSWorkspace.didWakeNotification, object: nil)
.sink { _ in self.mainWebViewController?.reload() }
.store(in: &subscriptions)

statusBarIconObserver = .observeUserDefaultsKey(ShouldShowStatusBarIconKey) {
self.statusItemVisible = $0
}
UserDefaults.standard.publisher(for: \.shouldShowStatusBarIcon)
.assign(to: \.statusItemVisible, on: self)
.store(in: &subscriptions)

DispatchQueue.global().async {
self.createUserScriptsFolderIfNeeded()
}

FastmateNotificationCenter.sharedInstance().delegate = self
FastmateNotificationCenter.sharedInstance().registerForNotifications()

UserDefaults.standard.registerFastmateDefaults()
}

func applicationDidBecomeActive(_ notification: Notification) {
UserDefaults.standard.register(defaults: [
ArrowNavigatesMessageListKey: false,
AutomaticUpdateChecksKey: true,
ShouldShowUnreadMailIndicatorKey: true,
ShouldShowUnreadMailInDockKey: true,
ShouldShowUnreadMailCountInDockKey: true,
ShouldUseFastmailBetaKey: false,
ShouldUseTransparentTitleBarKey: true
])

performAutomaticUpdateCheckIfNeeded()
}

Expand Down Expand Up @@ -125,7 +113,7 @@ extension AppDelegate: FastmateNotificationCenterDelegate {
// MARK: Update checks
extension AppDelegate: VersionCheckerDelegate {
func performAutomaticUpdateCheckIfNeeded() {
guard UserDefaults.standard.bool(forKey: AutomaticUpdateChecksKey) == true else {
guard UserDefaults.standard.automaticUpdateChecks else {
return
}

Expand Down Expand Up @@ -165,7 +153,7 @@ extension AppDelegate: VersionCheckerDelegate {
}

if alert.suppressionButton?.state == .off {
UserDefaults.standard.set(false, forKey: AutomaticUpdateChecksKey)
UserDefaults.standard.automaticUpdateChecks = false
}
}
}
Expand Down Expand Up @@ -211,14 +199,14 @@ extension AppDelegate {
@objc func handleKey(_ event: NSEvent) -> Bool {
switch Int(event.keyCode) {
case kVK_UpArrow:
if UserDefaults.standard.bool(forKey: ArrowNavigatesMessageListKey) {
if UserDefaults.standard.arrowNavigatesMessageList {
return mainWebViewController?.nextMessage() ?? false
} else {
return false
}

case kVK_DownArrow:
if UserDefaults.standard.bool(forKey: ArrowNavigatesMessageListKey) {
if UserDefaults.standard.arrowNavigatesMessageList {
return mainWebViewController?.previousMessage() ?? false
} else {
return false
Expand Down
2 changes: 1 addition & 1 deletion Fastmate/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" customClass="MainWindowController" sceneMemberID="viewController">
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" customClass="MainWindowController" customModule="Fastmate" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Fastmate" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" titleVisibility="hidden" id="IQv-IB-iLA">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
Expand Down
1 change: 0 additions & 1 deletion Fastmate/Fastmate-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#import "WebViewController.h"
#import "KVOBlockObserver.h"
#import "UserDefaultsKeys.h"
#import "NotificationCenter.h"
#import "UnreadCountObserver.h"
#import "VersionChecker.h"
Expand Down
5 changes: 0 additions & 5 deletions Fastmate/MainWindowController.h

This file was deleted.

58 changes: 0 additions & 58 deletions Fastmate/MainWindowController.m

This file was deleted.

58 changes: 58 additions & 0 deletions Fastmate/MainWindowController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Cocoa
import Combine
import WebKit

class MainWindowController: NSWindowController, NSWindowDelegate {
private var subscriptions = Set<AnyCancellable>()

override func windowDidLoad() {
super.windowDidLoad()
setupSubscribers()

// Fixes that we can't trust that the main window exists in applicationDidFinishLaunching:.
// Here we always know that this content view controller will be the main web view controller,
// so inform the app delegate
let appDelegate = NSApplication.shared.delegate as? AppDelegate
appDelegate?.mainWebViewController = contentViewController as? WebViewController
}

func setupSubscribers() {
guard let webViewController = contentViewController as? WebViewController, let window = window else {
return
}

webViewController.webView.publisher(for: \.title)
.replaceNil(with: "Fastmate")
.assign(to: \.title, on: window)
.store(in: &subscriptions)

UserDefaults.standard.publisher(for: \.mainWindowFrame)
.filter { $0 != NSRect.zero }
.first()
.sink { window.setFrame($0, display: false) }
.store(in: &subscriptions)

UserDefaults.standard.publisher(for: \.shouldUseTransparentTitleBar)
.sink {
window.titlebarAppearsTransparent = $0
window.titleVisibility = $0 ? .hidden : .visible
}
.store(in: &subscriptions)

NotificationCenter.default.publisher(for: NSWindow.didResizeNotification, object: window)
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
.compactMap { ($0.object as? NSWindow)?.frame }
.assign(to: \.mainWindowFrame, on: UserDefaults.standard)
.store(in: &subscriptions)

UserDefaults.standard.publisher(for: \.lastUsedWindowColor)
.assign(to: \.backgroundColor, on: window)
.store(in: &subscriptions)
}

func windowShouldClose(_ sender: NSWindow) -> Bool {
NSApp.hide(sender)
return false
}
}

58 changes: 58 additions & 0 deletions Fastmate/UserDefaults.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Foundation
import Combine

extension UserDefaults {
func registerFastmateDefaults() {
register(defaults: [
#keyPath(arrowNavigatesMessageList): false,
#keyPath(automaticUpdateChecks): true,
#keyPath(shouldShowStatusBarIcon): false,
#keyPath(shouldShowUnreadMailIndicator): true,
#keyPath(shouldShowUnreadMailInDock): true,
#keyPath(shouldShowUnreadMailCountInDock): true,
#keyPath(shouldShowUnreadMailInStatusBar): true,
#keyPath(shouldUseFastmailBeta): false,
#keyPath(shouldUseTransparentTitleBar): true,
#keyPath(watchedFolderType): WatchedFolderType.default.rawValue,
#keyPath(watchedFolders): "",
])
}

// These variable names must exactly match the key string for KVO Combine publishers to work. #keyPath sort of verifies this
@objc dynamic var shouldShowStatusBarIcon: Bool { bool(forKey: #keyPath(shouldShowStatusBarIcon)) }
@objc dynamic var shouldShowUnreadMailIndicator: Bool { bool(forKey: #keyPath(shouldShowUnreadMailIndicator)) }
@objc dynamic var shouldShowUnreadMailInDock: Bool { bool(forKey: #keyPath(shouldShowUnreadMailInDock)) }
@objc dynamic var shouldShowUnreadMailCountInDock: Bool { bool(forKey: #keyPath(shouldShowUnreadMailCountInDock)) }
@objc dynamic var shouldShowUnreadMailInStatusBar: Bool { bool(forKey: #keyPath(shouldShowUnreadMailInStatusBar)) }
@objc dynamic var shouldUseFastmailBeta: Bool { bool(forKey: #keyPath(shouldUseFastmailBeta)) }
@objc dynamic var shouldUseTransparentTitleBar: Bool { bool(forKey: #keyPath(shouldUseTransparentTitleBar)) }
@objc dynamic var watchedFolderType: WatchedFolderType { .init(rawValue: UInt(integer(forKey: #keyPath(watchedFolderType))))! }
@objc dynamic var watchedFolders: String { string(forKey: #keyPath(watchedFolders)) ?? "" }

@objc dynamic var arrowNavigatesMessageList: Bool {
get { bool(forKey: #keyPath(arrowNavigatesMessageList)) }
set { set(newValue, forKey: #keyPath(arrowNavigatesMessageList)) }
}

@objc dynamic var automaticUpdateChecks: Bool {
get { bool(forKey: #keyPath(automaticUpdateChecks)) }
set { set(newValue, forKey: #keyPath(automaticUpdateChecks)) }
}

@objc dynamic var mainWindowFrame: NSRect {
get { NSRectFromString(string(forKey: #keyPath(mainWindowFrame)) ?? "") }
set { set(NSStringFromRect(newValue), forKey: #keyPath(mainWindowFrame)) }
}

@objc dynamic var lastUsedWindowColor: NSColor {
data(forKey: #keyPath(lastUsedWindowColor))
.flatMap { try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSColor.self, from: $0) }
?? NSColor(red: 0.27, green: 0.34, blue: 0.49, alpha: 1.0)
}
}

@objc enum WatchedFolderType: UInt {
case `default`
case all
case specific
}
Loading

0 comments on commit 884477d

Please sign in to comment.