diff --git a/Mac/AppDefaults.swift b/Mac/AppDefaults.swift deleted file mode 100644 index bc5cffba5..000000000 --- a/Mac/AppDefaults.swift +++ /dev/null @@ -1,400 +0,0 @@ -// -// AppDefaults.swift -// NetNewsWire -// -// Created by Brent Simmons on 9/22/17. -// Copyright © 2017 Ranchero Software. All rights reserved. -// - -import AppKit - -enum FontSize: Int { - case small = 0 - case medium = 1 - case large = 2 - case veryLarge = 3 -} - -final class AppDefaults { - - static let defaultThemeName = "Default" - - static let shared = AppDefaults() - - private static let smallestFontSizeRawValue = FontSize.small.rawValue - private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue - - let isDeveloperBuild: Bool = { - if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" { - return true - } - return false - }() - - var isFirstRun: Bool = { - if UserDefaults.standard.object(forKey: AppDefaultsKey.firstRunDate) as? Date == nil { - firstRunDate = Date() - return true - } - return true - }() - - var windowState: [AnyHashable: Any]? { - get { - return UserDefaults.standard.object(forKey: AppDefaultsKey.windowState) as? [AnyHashable: Any] - } - set { - UserDefaults.standard.set(newValue, forKey: AppDefaultsKey.windowState) - } - } - - var lastImageCacheFlushDate: Date? { - get { - return AppDefaults.date(for: AppDefaultsKey.lastImageCacheFlushDate) - } - set { - AppDefaults.setDate(for: AppDefaultsKey.lastImageCacheFlushDate, newValue) - } - } - - var openInBrowserInBackground: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.openInBrowserInBackground) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.openInBrowserInBackground, newValue) - } - } - - // Special case for this default: store/retrieve it from the shared app group - // defaults, so that it can be resolved by the Safari App Extension. - var subscribeToFeedDefaults: UserDefaults { - if let appGroupID = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String, - let appGroupDefaults = UserDefaults(suiteName: appGroupID) { - return appGroupDefaults - } else { - return UserDefaults.standard - } - } - - var subscribeToFeedsInDefaultBrowser: Bool { - get { - return subscribeToFeedDefaults.bool(forKey: AppDefaultsKey.subscribeToFeedsInDefaultBrowser) - } - set { - subscribeToFeedDefaults.set(newValue, forKey: AppDefaultsKey.subscribeToFeedsInDefaultBrowser) - } - } - - var sidebarFontSize: FontSize { - get { - return fontSize(for: AppDefaultsKey.sidebarFontSize) - } - set { - AppDefaults.setFontSize(for: AppDefaultsKey.sidebarFontSize, newValue) - } - } - - var timelineFontSize: FontSize { - get { - return fontSize(for: AppDefaultsKey.timelineFontSize) - } - set { - AppDefaults.setFontSize(for: AppDefaultsKey.timelineFontSize, newValue) - } - } - - var detailFontSize: FontSize { - get { - return fontSize(for: AppDefaultsKey.detailFontSize) - } - set { - AppDefaults.setFontSize(for: AppDefaultsKey.detailFontSize, newValue) - } - } - - var addFeedAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFeedAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFeedAccountID, newValue) - } - } - - var addFeedFolderName: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFeedFolderName) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFeedFolderName, newValue) - } - } - - var addFolderAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFolderAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFolderAccountID, newValue) - } - } - - var importOPMLAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.importOPMLAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.importOPMLAccountID, newValue) - } - } - - var exportOPMLAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.exportOPMLAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.exportOPMLAccountID, newValue) - } - } - - var defaultBrowserID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.defaultBrowserID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.defaultBrowserID, newValue) - } - } - - var currentThemeName: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.currentThemeName) - } - set { - AppDefaults.setString(for: AppDefaultsKey.currentThemeName, newValue) - } - } - - var showTitleOnMainWindow: Bool { - return AppDefaults.bool(for: AppDefaultsKey.showTitleOnMainWindow) - } - - var showDebugMenu: Bool { - return AppDefaults.bool(for: AppDefaultsKey.showDebugMenu) - } - - var feedDoubleClickMarkAsRead: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.feedDoubleClickMarkAsRead) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.feedDoubleClickMarkAsRead, newValue) - } - } - - var suppressSyncOnLaunch: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.suppressSyncOnLaunch) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.suppressSyncOnLaunch, newValue) - } - } - - var webInspectorEnabled: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.webInspectorEnabled) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.webInspectorEnabled, newValue) - } - } - - var webInspectorStartsAttached: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.webInspectorStartsAttached) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.webInspectorStartsAttached, newValue) - } - } - - var timelineSortDirection: ComparisonResult { - get { - return AppDefaults.sortDirection(for: AppDefaultsKey.timelineSortDirection) - } - set { - AppDefaults.setSortDirection(for: AppDefaultsKey.timelineSortDirection, newValue) - } - } - - var timelineGroupByFeed: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.timelineGroupByFeed) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.timelineGroupByFeed, newValue) - } - } - - var timelineShowsSeparators: Bool { - return AppDefaults.bool(for: AppDefaultsKey.timelineShowsSeparators) - } - - var articleTextSize: ArticleTextSize { - get { - let rawValue = UserDefaults.standard.integer(forKey: AppDefaultsKey.articleTextSize) - return ArticleTextSize(rawValue: rawValue) ?? ArticleTextSize.large - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: AppDefaultsKey.articleTextSize) - } - } - - var refreshInterval: RefreshInterval { - get { - let rawValue = UserDefaults.standard.integer(forKey: AppDefaultsKey.refreshInterval) - return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour - } - set { - UserDefaults.standard.set(newValue.rawValue, forKey: AppDefaultsKey.refreshInterval) - } - } - - var isArticleContentJavascriptEnabled: Bool { - get { - UserDefaults.standard.bool(forKey: AppDefaultsKey.articleContentJavascriptEnabled) - } - set { - UserDefaults.standard.set(newValue, forKey: AppDefaultsKey.articleContentJavascriptEnabled) - } - } - - func registerDefaults() { - #if DEBUG - let showDebugMenu = true - #else - let showDebugMenu = false - #endif - - let defaults: [String: Any] = [ - AppDefaultsKey.sidebarFontSize: FontSize.medium.rawValue, - AppDefaultsKey.timelineFontSize: FontSize.medium.rawValue, - AppDefaultsKey.detailFontSize: FontSize.medium.rawValue, - AppDefaultsKey.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, - AppDefaultsKey.timelineGroupByFeed: false, - "NSScrollViewShouldScrollUnderTitlebar": false, - AppDefaultsKey.refreshInterval: RefreshInterval.everyHour.rawValue, - AppDefaultsKey.showDebugMenu: showDebugMenu, - AppDefaultsKey.currentThemeName: Self.defaultThemeName, - AppDefaultsKey.articleContentJavascriptEnabled: true - ] - - UserDefaults.standard.register(defaults: defaults) - - // It seems that registering a default for NSQuitAlwaysKeepsWindows to true - // is not good enough to get the system to respect it, so we have to literally - // set it as the default to get it to take effect. This overrides a system-wide - // setting in the System Preferences, which is ostensibly meant to "close windows" - // in an app, but has the side-effect of also not preserving or restoring any state - // for the window. Since we've switched to using the standard state preservation and - // restoration mechanisms, and because it seems highly unlikely any user would object - // to NetNewsWire preserving this state, we'll force the preference on. If this becomes - // an issue, this could be changed to proactively look for whether the default has been - // set _by the user_ to false, and respect that default if it is so-set. -// UserDefaults.standard.set(true, forKey: "NSQuitAlwaysKeepsWindows") - // TODO: revisit the above when coming back to state restoration issues. - } - - func actualFontSize(for fontSize: FontSize) -> CGFloat { - switch fontSize { - case .small: - return NSFont.systemFontSize - case .medium: - return actualFontSize(for: .small) + 1.0 - case .large: - return actualFontSize(for: .medium) + 4.0 - case .veryLarge: - return actualFontSize(for: .large) + 8.0 - } - } -} - -private extension AppDefaults { - - static var firstRunDate: Date? { - get { - return AppDefaults.date(for: AppDefaultsKey.firstRunDate) - } - set { - AppDefaults.setDate(for: AppDefaultsKey.firstRunDate, newValue) - } - } - - func fontSize(for key: String) -> FontSize { - // Punted till after 1.0. - return .medium - -// var rawFontSize = int(for: key) -// if rawFontSize < smallestFontSizeRawValue { -// rawFontSize = smallestFontSizeRawValue -// } -// if rawFontSize > largestFontSizeRawValue { -// rawFontSize = largestFontSizeRawValue -// } -// return FontSize(rawValue: rawFontSize)! - } - - static func setFontSize(for key: String, _ fontSize: FontSize) { - setInt(for: key, fontSize.rawValue) - } - - static func string(for key: String) -> String? { - return UserDefaults.standard.string(forKey: key) - } - - static func setString(for key: String, _ value: String?) { - UserDefaults.standard.set(value, forKey: key) - } - - static func bool(for key: String) -> Bool { - return UserDefaults.standard.bool(forKey: key) - } - - static func setBool(for key: String, _ flag: Bool) { - UserDefaults.standard.set(flag, forKey: key) - } - - static func int(for key: String) -> Int { - return UserDefaults.standard.integer(forKey: key) - } - - static func setInt(for key: String, _ x: Int) { - UserDefaults.standard.set(x, forKey: key) - } - - static func date(for key: String) -> Date? { - return UserDefaults.standard.object(forKey: key) as? Date - } - - static func setDate(for key: String, _ date: Date?) { - UserDefaults.standard.set(date, forKey: key) - } - - static func sortDirection(for key: String) -> ComparisonResult { - let rawInt = int(for: key) - if rawInt == ComparisonResult.orderedAscending.rawValue { - return .orderedAscending - } - return .orderedDescending - } - - static func setSortDirection(for key: String, _ value: ComparisonResult) { - if value == .orderedAscending { - setInt(for: key, ComparisonResult.orderedAscending.rawValue) - } else { - setInt(for: key, ComparisonResult.orderedDescending.rawValue) - } - } -} diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 5306d0519..baba89af1 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -37,7 +37,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat var refreshTimer: AccountRefreshTimer? var syncTimer: ArticleStatusSyncTimer? - var lastRefreshInterval = AppDefaults.shared.refreshInterval + var lastRefreshInterval = AppDefaults.refreshInterval var shuttingDown = false { didSet { @@ -168,8 +168,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat NSLog("Failed to start software updater with error: \(error)") } - AppDefaults.shared.registerDefaults() - let isFirstRun = AppDefaults.shared.isFirstRun + AppDefaults.registerDefaults() + let isFirstRun = AppDefaults.isFirstRun if isFirstRun { os_log(.debug, "Is first run.") } @@ -228,7 +228,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat refreshTimer!.update() syncTimer!.update() #else - if AppDefaults.shared.suppressSyncOnLaunch { + if AppDefaults.suppressSyncOnLaunch { refreshTimer!.update() syncTimer!.update() } else { @@ -239,7 +239,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat } #endif - if !AppDefaults.shared.showDebugMenu { + if !AppDefaults.showDebugMenu { debugMenuItem.menu?.removeItem(debugMenuItem) } @@ -330,9 +330,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat updateSortMenuItems() updateGroupByFeedMenuItem() - if lastRefreshInterval != AppDefaults.shared.refreshInterval { + if lastRefreshInterval != AppDefaults.refreshInterval { refreshTimer?.update() - lastRefreshInterval = AppDefaults.shared.refreshInterval + lastRefreshInterval = AppDefaults.refreshInterval } updateDockBadge() @@ -426,7 +426,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat } if item.action == #selector(toggleWebInspectorEnabled(_:)) { - (item as! NSMenuItem).state = AppDefaults.shared.webInspectorEnabled ? .on : .off + (item as! NSMenuItem).state = AppDefaults.webInspectorEnabled ? .on : .off } return true @@ -634,16 +634,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidat @IBAction func sortByOldestArticleOnTop(_ sender: Any?) { - AppDefaults.shared.timelineSortDirection = .orderedAscending + AppDefaults.timelineSortDirection = .orderedAscending } @IBAction func sortByNewestArticleOnTop(_ sender: Any?) { - AppDefaults.shared.timelineSortDirection = .orderedDescending + AppDefaults.timelineSortDirection = .orderedDescending } @IBAction func groupByFeedToggled(_ sender: NSMenuItem) { - AppDefaults.shared.timelineGroupByFeed.toggle() + AppDefaults.timelineGroupByFeed.toggle() } @IBAction func checkForUpdates(_ sender: Any?) { @@ -691,13 +691,13 @@ extension AppDelegate { @IBAction func toggleWebInspectorEnabled(_ sender: Any?) { - let newValue = !AppDefaults.shared.webInspectorEnabled - AppDefaults.shared.webInspectorEnabled = newValue + let newValue = !AppDefaults.webInspectorEnabled + AppDefaults.webInspectorEnabled = newValue // An attached inspector can display incorrectly on certain setups (like mine); default to displaying in a separate window, // and reset the default to a separate window when the preference is toggled off and on again in case the inspector is // accidentally reattached. - AppDefaults.shared.webInspectorStartsAttached = false + AppDefaults.webInspectorStartsAttached = false NotificationCenter.default.post(name: .WebInspectorEnabledDidChange, object: newValue) } } @@ -724,13 +724,13 @@ internal extension AppDelegate { } func updateSortMenuItems() { - let sortByNewestOnTop = AppDefaults.shared.timelineSortDirection == .orderedDescending + let sortByNewestOnTop = AppDefaults.timelineSortDirection == .orderedDescending sortByNewestArticleOnTopMenuItem.state = sortByNewestOnTop ? .on : .off sortByOldestArticleOnTopMenuItem.state = sortByNewestOnTop ? .off : .on } func updateGroupByFeedMenuItem() { - let groupByFeedEnabled = AppDefaults.shared.timelineGroupByFeed + let groupByFeedEnabled = AppDefaults.timelineGroupByFeed groupArticlesByFeedMenuItem.state = groupByFeedEnabled ? .on : .off } diff --git a/Mac/Browser.swift b/Mac/Browser.swift index 45ce5a23c..a9f623f3a 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -16,7 +16,7 @@ struct Browser { /// The user-assigned default browser, or `nil` if none was assigned /// (i.e., the system default should be used). static var defaultBrowser: MacWebBrowser? { - if let bundleID = AppDefaults.shared.defaultBrowserID, let browser = MacWebBrowser(bundleIdentifier: bundleID) { + if let bundleID = AppDefaults.defaultBrowserID, let browser = MacWebBrowser(bundleIdentifier: bundleID) { return browser } @@ -30,7 +30,7 @@ struct Browser { /// - invert: Whether to invert the "open in background in browser" preference static func open(_ urlString: String, invertPreference invert: Bool = false) { // Opens according to prefs. - open(urlString, inBackground: invert ? !AppDefaults.shared.openInBrowserInBackground : AppDefaults.shared.openInBrowserInBackground) + open(urlString, inBackground: invert ? !AppDefaults.openInBrowserInBackground : AppDefaults.openInBrowserInBackground) } /// Opens a URL in the default browser. @@ -64,7 +64,7 @@ struct Browser { extension Browser { static var titleForOpenInBrowserInverted: String { - let openInBackgroundPref = AppDefaults.shared.openInBrowserInBackground + let openInBackgroundPref = AppDefaults.openInBrowserInBackground return openInBackgroundPref ? NSLocalizedString("Open in Browser in Foreground", comment: "Open in Browser in Foreground menu item title") : diff --git a/Mac/MainWindow/AddFolder/AddFolderWindowController.swift b/Mac/MainWindow/AddFolder/AddFolderWindowController.swift index 94581f9ce..8d6edfa00 100644 --- a/Mac/MainWindow/AddFolder/AddFolderWindowController.swift +++ b/Mac/MainWindow/AddFolder/AddFolderWindowController.swift @@ -36,7 +36,7 @@ final class AddFolderWindowController: NSWindowController { // MARK: - NSViewController override func windowDidLoad() { - let preferredAccountID = AppDefaults.shared.addFolderAccountID + let preferredAccountID = AppDefaults.addFolderAccountID accountPopupButton.removeAllItems() let menu = NSMenu() @@ -93,7 +93,7 @@ private extension AddFolderWindowController { } let account = menuItem.representedObject as! Account - AppDefaults.shared.addFolderAccountID = account.accountID + AppDefaults.addFolderAccountID = account.accountID let folderName = self.folderNameTextField.stringValue if folderName.isEmpty { diff --git a/Mac/MainWindow/Detail/DetailViewController.swift b/Mac/MainWindow/Detail/DetailViewController.swift index 6f746ec6c..f812275cf 100644 --- a/Mac/MainWindow/Detail/DetailViewController.swift +++ b/Mac/MainWindow/Detail/DetailViewController.swift @@ -57,7 +57,7 @@ final class DetailViewController: NSViewController, WKUIDelegate { } } - private var isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled + private var isArticleContentJavascriptEnabled = AppDefaults.isArticleContentJavascriptEnabled override func viewDidLoad() { currentWebViewController = regularWebViewController @@ -159,8 +159,8 @@ private extension DetailViewController { } @objc func userDefaultsDidChange(_: Notification) { - if AppDefaults.shared.isArticleContentJavascriptEnabled != isArticleContentJavascriptEnabled { - isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled + if AppDefaults.isArticleContentJavascriptEnabled != isArticleContentJavascriptEnabled { + isArticleContentJavascriptEnabled = AppDefaults.isArticleContentJavascriptEnabled createNewWebViewsAndRestoreState() } } diff --git a/Mac/MainWindow/Detail/DetailWebViewController.swift b/Mac/MainWindow/Detail/DetailWebViewController.swift index 0e6873929..469f21e4d 100644 --- a/Mac/MainWindow/Detail/DetailWebViewController.swift +++ b/Mac/MainWindow/Detail/DetailWebViewController.swift @@ -46,7 +46,7 @@ final class DetailWebViewController: NSViewController { } } - private var articleTextSize = AppDefaults.shared.articleTextSize + private var articleTextSize = AppDefaults.articleTextSize private var webInspectorEnabled: Bool { get { @@ -97,7 +97,7 @@ final class DetailWebViewController: NSViewController { webView.isHidden = true waitingForFirstReload = true - webInspectorEnabled = AppDefaults.shared.webInspectorEnabled + webInspectorEnabled = AppDefaults.webInspectorEnabled NotificationCenter.default.addObserver(self, selector: #selector(webInspectorEnabledDidChange(_:)), name: .WebInspectorEnabledDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .feedIconDidBecomeAvailable, object: nil) @@ -124,8 +124,8 @@ final class DetailWebViewController: NSViewController { } @objc func userDefaultsDidChange(_ note: Notification) { - if articleTextSize != AppDefaults.shared.articleTextSize { - articleTextSize = AppDefaults.shared.articleTextSize + if articleTextSize != AppDefaults.articleTextSize { + articleTextSize = AppDefaults.articleTextSize reloadHTMLMaintainingScrollPosition() } } diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 10324c6c1..41ed9290f 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -128,12 +128,12 @@ final class MainWindowController: NSWindowController, NSUserInterfaceValidations } func saveStateToUserDefaults() { - AppDefaults.shared.windowState = savableState() + AppDefaults.windowState = savableState() window?.saveFrame(usingName: windowAutosaveName) } func restoreStateFromUserDefaults() { - if let state = AppDefaults.shared.windowState { + if let state = AppDefaults.windowState { restoreState(from: state) window?.setFrameUsingName(windowAutosaveName, force: true) } @@ -314,7 +314,7 @@ final class MainWindowController: NSWindowController, NSUserInterfaceValidations } @IBAction func openInBrowser(_ sender: Any?) { - if AppDefaults.shared.openInBrowserInBackground { + if AppDefaults.openInBrowserInBackground { window?.makeKeyAndOrderFront(self) } openArticleInBrowser(sender) @@ -326,11 +326,11 @@ final class MainWindowController: NSWindowController, NSUserInterfaceValidations } @IBAction func openInBrowserUsingOppositeOfSettings(_ sender: Any?) { - if !AppDefaults.shared.openInBrowserInBackground { + if !AppDefaults.openInBrowserInBackground { window?.makeKeyAndOrderFront(self) } if let link = currentLink { - Browser.open(link, inBackground: !AppDefaults.shared.openInBrowserInBackground) + Browser.open(link, inBackground: !AppDefaults.openInBrowserInBackground) } } @@ -1040,7 +1040,7 @@ private extension MainWindowController { } func validateToggleArticleExtractor(_ item: NSValidatedUserInterfaceItem) -> Bool { - guard !AppDefaults.shared.isDeveloperBuild else { + guard !AppDefaults.isDeveloperBuild else { return false } diff --git a/Mac/MainWindow/NNW3/NNW3ImportController.swift b/Mac/MainWindow/NNW3/NNW3ImportController.swift index bdeb7e466..d3843a541 100644 --- a/Mac/MainWindow/NNW3/NNW3ImportController.swift +++ b/Mac/MainWindow/NNW3/NNW3ImportController.swift @@ -82,7 +82,7 @@ private extension NNW3ImportController { guard let account = accessoryViewController.selectedAccount else { return } - AppDefaults.shared.importOPMLAccountID = account.accountID + AppDefaults.importOPMLAccountID = account.accountID NNW3ImportController.importSubscriptionsPlist(subscriptionsPlistURL, into: account) } diff --git a/Mac/MainWindow/NNW3/NNW3OpenPanelAccessoryViewController.swift b/Mac/MainWindow/NNW3/NNW3OpenPanelAccessoryViewController.swift index c6a53edb1..a477d304b 100644 --- a/Mac/MainWindow/NNW3/NNW3OpenPanelAccessoryViewController.swift +++ b/Mac/MainWindow/NNW3/NNW3OpenPanelAccessoryViewController.swift @@ -39,7 +39,7 @@ final class NNW3OpenPanelAccessoryViewController: NSViewController { menuItem.representedObject = account menu.addItem(menuItem) - if account.accountID == AppDefaults.shared.importOPMLAccountID { + if account.accountID == AppDefaults.importOPMLAccountID { accountPopUpButton.select(menuItem) } } diff --git a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift index 3ee4f6f3b..6638f6e61 100644 --- a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift +++ b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift @@ -32,7 +32,7 @@ final class ExportOPMLWindowController: NSWindowController { oneMenuItem.representedObject = oneAccount menu.addItem(oneMenuItem) - if oneAccount.accountID == AppDefaults.shared.exportOPMLAccountID { + if oneAccount.accountID == AppDefaults.exportOPMLAccountID { accountPopUpButton.select(oneMenuItem) } @@ -67,7 +67,7 @@ final class ExportOPMLWindowController: NSWindowController { } let account = menuItem.representedObject as! Account - AppDefaults.shared.exportOPMLAccountID = account.accountID + AppDefaults.exportOPMLAccountID = account.accountID hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK) exportOPML(account: account) diff --git a/Mac/MainWindow/OPML/ImportOPMLWindowController.swift b/Mac/MainWindow/OPML/ImportOPMLWindowController.swift index 4a3fb8e0f..cb3368a35 100644 --- a/Mac/MainWindow/OPML/ImportOPMLWindowController.swift +++ b/Mac/MainWindow/OPML/ImportOPMLWindowController.swift @@ -36,7 +36,7 @@ final class ImportOPMLWindowController: NSWindowController { oneMenuItem.representedObject = oneAccount menu.addItem(oneMenuItem) - if oneAccount.accountID == AppDefaults.shared.importOPMLAccountID { + if oneAccount.accountID == AppDefaults.importOPMLAccountID { accountPopUpButton.select(oneMenuItem) } @@ -71,7 +71,7 @@ final class ImportOPMLWindowController: NSWindowController { } let account = menuItem.representedObject as! Account - AppDefaults.shared.importOPMLAccountID = account.accountID + AppDefaults.importOPMLAccountID = account.accountID hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK) importOPML(account: account) diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift index 14c985d7f..7ef160d4a 100644 --- a/Mac/MainWindow/Sidebar/SidebarViewController.swift +++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift @@ -257,7 +257,7 @@ protocol SidebarDelegate: AnyObject { guard outlineView.clickedRow == outlineView.selectedRow else { return } - if AppDefaults.shared.feedDoubleClickMarkAsRead, let articles = try? singleSelectedFeed?.fetchUnreadArticles() { + if AppDefaults.feedDoubleClickMarkAsRead, let articles = try? singleSelectedFeed?.fetchUnreadArticles() { if let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) { runCommand(markReadCommand) } diff --git a/Mac/MainWindow/Timeline/Cell/TimelineCellAppearance.swift b/Mac/MainWindow/Timeline/Cell/TimelineCellAppearance.swift index 457919e6c..5792a9222 100644 --- a/Mac/MainWindow/Timeline/Cell/TimelineCellAppearance.swift +++ b/Mac/MainWindow/Timeline/Cell/TimelineCellAppearance.swift @@ -44,7 +44,7 @@ struct TimelineCellAppearance: Equatable { init(showIcon: Bool, fontSize: FontSize) { - let actualFontSize = AppDefaults.shared.actualFontSize(for: fontSize) + let actualFontSize = AppDefaults.actualFontSize(for: fontSize) let smallItemFontSize = floor(actualFontSize * 0.90) let largeItemFontSize = actualFontSize diff --git a/Mac/MainWindow/Timeline/TimelineContainerViewController.swift b/Mac/MainWindow/Timeline/TimelineContainerViewController.swift index ff489a347..f8eef0235 100644 --- a/Mac/MainWindow/Timeline/TimelineContainerViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineContainerViewController.swift @@ -178,7 +178,7 @@ private extension TimelineContainerViewController { } func updateViewOptionsPopUpButton() { - if AppDefaults.shared.timelineSortDirection == .orderedAscending { + if AppDefaults.timelineSortDirection == .orderedAscending { newestToOldestMenuItem.state = .off oldestToNewestMenuItem.state = .on viewOptionsPopUpButton.setTitle(oldestToNewestMenuItem.title) @@ -188,7 +188,7 @@ private extension TimelineContainerViewController { viewOptionsPopUpButton.setTitle(newestToOldestMenuItem.title) } - if AppDefaults.shared.timelineGroupByFeed == true { + if AppDefaults.timelineGroupByFeed == true { groupByFeedMenuItem.state = .on } else { groupByFeedMenuItem.state = .off diff --git a/Mac/MainWindow/Timeline/TimelineTableRowView.swift b/Mac/MainWindow/Timeline/TimelineTableRowView.swift index b08cd3b33..01f12b274 100644 --- a/Mac/MainWindow/Timeline/TimelineTableRowView.swift +++ b/Mac/MainWindow/Timeline/TimelineTableRowView.swift @@ -47,7 +47,7 @@ final class TimelineTableRowView: NSTableRowView { } override func viewDidMoveToSuperview() { - if AppDefaults.shared.timelineShowsSeparators { + if AppDefaults.timelineShowsSeparators { addSeparatorView() } } diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 1241f2d40..ddb75cd55 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -161,21 +161,21 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr private var didRegisterForNotifications = false static let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5, maxInterval: 2.0) - private var sortDirection = AppDefaults.shared.timelineSortDirection { + private var sortDirection = AppDefaults.timelineSortDirection { didSet { if sortDirection != oldValue { sortParametersDidChange() } } } - private var groupByFeed = AppDefaults.shared.timelineGroupByFeed { + private var groupByFeed = AppDefaults.timelineGroupByFeed { didSet { if groupByFeed != oldValue { sortParametersDidChange() } } } - private var fontSize: FontSize = AppDefaults.shared.timelineFontSize { + private var fontSize: FontSize = AppDefaults.timelineFontSize { didSet { if fontSize != oldValue { fontSizeDidChange() @@ -657,9 +657,9 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr } @objc func userDefaultsDidChange(_ note: Notification) { - self.fontSize = AppDefaults.shared.timelineFontSize - self.sortDirection = AppDefaults.shared.timelineSortDirection - self.groupByFeed = AppDefaults.shared.timelineGroupByFeed + self.fontSize = AppDefaults.timelineFontSize + self.sortDirection = AppDefaults.timelineSortDirection + self.groupByFeed = AppDefaults.timelineGroupByFeed } // MARK: - Reloading Data diff --git a/Mac/Preferences/Accounts/AddAccountHelpView.swift b/Mac/Preferences/Accounts/AddAccountHelpView.swift index 138652a4d..24cf124d2 100644 --- a/Mac/Preferences/Accounts/AddAccountHelpView.swift +++ b/Mac/Preferences/Accounts/AddAccountHelpView.swift @@ -20,7 +20,7 @@ struct AddAccountHelpView: View { VStack { HStack { ForEach(accountTypes, id: \.self) { accountType in - if !(AppDefaults.shared.isDeveloperBuild && accountType.isDeveloperRestricted) { + if !(AppDefaults.isDeveloperBuild && accountType.isDeveloperRestricted) { Button(action: { if accountType == .cloudKit && AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) { iCloudUnavailableError = true diff --git a/Mac/Preferences/Accounts/AddAccountsView.swift b/Mac/Preferences/Accounts/AddAccountsView.swift index 83da09de9..d5caa0020 100644 --- a/Mac/Preferences/Accounts/AddAccountsView.swift +++ b/Mac/Preferences/Accounts/AddAccountsView.swift @@ -54,7 +54,7 @@ enum AddAccountSections: Int, CaseIterable { case .icloud: return [.cloudKit] case .web: - if AppDefaults.shared.isDeveloperBuild { + if AppDefaults.isDeveloperBuild { return [.bazQux, .feedbin, .feedly, .inoreader, .newsBlur, .theOldReader].filter({ $0.isDeveloperRestricted == false }) } else { return [.bazQux, .feedbin, .feedly, .inoreader, .newsBlur, .theOldReader] @@ -90,7 +90,7 @@ struct AddAccountsView: View { localAccount - if !AppDefaults.shared.isDeveloperBuild { + if !AppDefaults.isDeveloperBuild { icloudAccount } diff --git a/Mac/Preferences/General/GeneralPrefencesViewController.swift b/Mac/Preferences/General/GeneralPrefencesViewController.swift index 8ad06f129..35eee754a 100644 --- a/Mac/Preferences/General/GeneralPrefencesViewController.swift +++ b/Mac/Preferences/General/GeneralPrefencesViewController.swift @@ -65,7 +65,7 @@ final class GeneralPreferencesViewController: NSViewController { return } let bundleID = menuItem.representedObject as? String - AppDefaults.shared.defaultBrowserID = bundleID + AppDefaults.defaultBrowserID = bundleID updateBrowserPopup() } @@ -132,7 +132,7 @@ private extension GeneralPreferencesViewController { menu.addItem(item) } - defaultBrowserPopup.selectItem(at: defaultBrowserPopup.indexOfItem(withRepresentedObject: AppDefaults.shared.defaultBrowserID)) + defaultBrowserPopup.selectItem(at: defaultBrowserPopup.indexOfItem(withRepresentedObject: AppDefaults.defaultBrowserID)) } func updateNotificationSettings() { @@ -161,10 +161,10 @@ private extension GeneralPreferencesViewController { @objc var openFeedsInDefaultNewsReader: Bool { get { - return AppDefaults.shared.subscribeToFeedsInDefaultBrowser + return AppDefaults.subscribeToFeedsInDefaultBrowser } set { - AppDefaults.shared.subscribeToFeedsInDefaultBrowser = newValue + AppDefaults.subscribeToFeedsInDefaultBrowser = newValue } } } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 2896c7853..8046d4b1d 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -277,6 +277,17 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 848D19082D46D78E00DD0819 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Defaults/AppDefaults.swift, + Defaults/ArticleTextSize.swift, + Defaults/FontSize.swift, + Defaults/RefreshInterval.swift, + Defaults/UserInterfaceColorPalette.swift, + ); + target = 6581C73220CED60000F4AD34 /* Subscribe to Feed */; + }; 8498E53C2D27A766009F5438 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -298,7 +309,6 @@ membershipExceptions = ( /Localized/ShareExtension/MainInterface.storyboard, AppAssets.swift, - AppDefaults.swift, Resources/Assets.xcassets, ShareExtension/ShareFolderPickerAccountCell.xib, ShareExtension/ShareFolderPickerCell.swift, @@ -353,7 +363,6 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( /Localized/ShareExtension/ShareViewController.xib, - AppDefaults.swift, ShareExtension/icon.icns, ShareExtension/ShareViewController.swift, ); @@ -412,7 +421,10 @@ 84C1ECED2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( - AppDefaultsKey.swift, + Defaults/AppDefaults.swift, + Defaults/ArticleTextSize.swift, + Defaults/FontSize.swift, + Defaults/UserInterfaceColorPalette.swift, Extensions/IconImage.swift, "Extensions/Node-Extensions.swift", ShareExtension/ExtensionContainers.swift, @@ -446,15 +458,17 @@ 84C1ECF02CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( - AppDefaultsKey.swift, - ArticleRendering/ArticleTextSize.swift, + Defaults/AppDefaults.swift, + Defaults/ArticleTextSize.swift, + Defaults/FontSize.swift, + Defaults/RefreshInterval.swift, + Defaults/UserInterfaceColorPalette.swift, ShareExtension/ExtensionContainers.swift, ShareExtension/ExtensionContainersFile.swift, ShareExtension/ExtensionFeedAddRequest.swift, ShareExtension/ExtensionFeedAddRequestFile.swift, ShareExtension/SafariExt.js, ShareExtension/ShareDefaultContainer.swift, - Timer/RefreshInterval.swift, ); target = 510C415B24E5CDE3008226FD /* NetNewsWire Share Extension */; }; @@ -500,7 +514,7 @@ 84BAE0AA2CE1C4D500402E69 /* Mac */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84BAE1402CE1C4D500402E69 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84BAE1412CE1C4D500402E69 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, 84BAE1422CE1C4D500402E69 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84BAE1432CE1C4D500402E69 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Mac; sourceTree = ""; }; 84C1EB5E2CDFE18E00C7456A /* xcconfig */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84C1EB602CDFE18E00C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (common, ); path = xcconfig; sourceTree = ""; }; 84C1EB7C2CDFE31700C7456A /* Tests */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84C1EB952CDFE31700C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1EB962CDFE31700C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Tests; sourceTree = ""; }; - 84C1EC192CDFE49100C7456A /* Shared */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84C1ECEB2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEC2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECED2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEE2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEF2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECF02CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Shared; sourceTree = ""; }; + 84C1EC192CDFE49100C7456A /* Shared */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84C1ECEB2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEC2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 848D19082D46D78E00DD0819 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECED2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEE2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECEF2CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 84C1ECF02CDFE49100C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Shared; sourceTree = ""; }; 84C1ED042CDFE50500C7456A /* Widget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (84C1ED132CDFE50500C7456A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Widget; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ diff --git a/Shared/AppDefaultsKey.swift b/Shared/AppDefaultsKey.swift deleted file mode 100644 index f8fbac4d7..000000000 --- a/Shared/AppDefaultsKey.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// AppDefaultsKey.swift -// NetNewsWire -// -// Created by Brent Simmons on 1/25/25. -// Copyright © 2025 Ranchero Software. All rights reserved. -// - -import Foundation - -struct AppDefaultsKey { - - static let firstRunDate = "firstRunDate" - static let lastImageCacheFlushDate = "lastImageCacheFlushDate" - static let timelineGroupByFeed = "timelineGroupByFeed" - static let timelineSortDirection = "timelineSortDirection" - static let addFeedAccountID = "addFeedAccountID" - static let addFeedFolderName = "addFeedFolderName" - static let addFolderAccountID = "addFolderAccountID" - static let currentThemeName = "currentThemeName" - static let articleContentJavascriptEnabled = "articleContentJavascriptEnabled" - -#if os(macOS) - - static let windowState = "windowState" - static let sidebarFontSize = "sidebarFontSize" - static let timelineFontSize = "timelineFontSize" - static let detailFontSize = "detailFontSize" - static let openInBrowserInBackground = "openInBrowserInBackground" - static let subscribeToFeedsInDefaultBrowser = "subscribeToFeedsInDefaultBrowser" - static let articleTextSize = "articleTextSize" - static let refreshInterval = "refreshInterval" - static let importOPMLAccountID = "importOPMLAccountID" - static let exportOPMLAccountID = "exportOPMLAccountID" - static let defaultBrowserID = "defaultBrowserID" - - // Hidden prefs - static let showDebugMenu = "ShowDebugMenu" - static let timelineShowsSeparators = "CorreiaSeparators" - static let showTitleOnMainWindow = "KafasisTitleMode" - static let feedDoubleClickMarkAsRead = "GruberFeedDoubleClickMarkAsRead" - static let suppressSyncOnLaunch = "DevroeSuppressSyncOnLaunch" - - static let webInspectorEnabled = "WebInspectorEnabled" - static let webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached" - -#elseif os(iOS) - - static let userInterfaceColorPalette = "userInterfaceColorPalette" - static let refreshClearsReadArticles = "refreshClearsReadArticles" - static let timelineNumberOfLines = "timelineNumberOfLines" - static let timelineIconDimension = "timelineIconSize" - static let articleFullscreenAvailable = "articleFullscreenAvailable" - static let articleFullscreenEnabled = "articleFullscreenEnabled" - static let confirmMarkAllAsRead = "confirmMarkAllAsRead" - static let lastRefresh = "lastRefresh" - static let useSystemBrowser = "useSystemBrowser" - -#endif -} diff --git a/Shared/ArticleRendering/ArticleRenderer.swift b/Shared/ArticleRendering/ArticleRenderer.swift index 77a9f3996..567c5792c 100644 --- a/Shared/ArticleRendering/ArticleRenderer.swift +++ b/Shared/ArticleRendering/ArticleRenderer.swift @@ -227,7 +227,7 @@ private extension ArticleRenderer { d["body"] = body #if os(macOS) - d["text_size_class"] = AppDefaults.shared.articleTextSize.cssClass + d["text_size_class"] = AppDefaults.articleTextSize.cssClass #endif var components = URLComponents() diff --git a/Shared/ArticleRendering/WebViewConfiguration.swift b/Shared/ArticleRendering/WebViewConfiguration.swift index 61f5f1979..f0b075c7a 100644 --- a/Shared/ArticleRendering/WebViewConfiguration.swift +++ b/Shared/ArticleRendering/WebViewConfiguration.swift @@ -46,7 +46,7 @@ private extension WebViewConfiguration { static var webpagePreferences: WKWebpagePreferences { let preferences = WKWebpagePreferences() - preferences.allowsContentJavaScript = AppDefaults.shared.isArticleContentJavascriptEnabled + preferences.allowsContentJavaScript = AppDefaults.isArticleContentJavascriptEnabled return preferences } diff --git a/Shared/ArticleStyles/ArticleThemesManager.swift b/Shared/ArticleStyles/ArticleThemesManager.swift index 6fb4b42ca..b75c6c4a1 100644 --- a/Shared/ArticleStyles/ArticleThemesManager.swift +++ b/Shared/ArticleStyles/ArticleThemesManager.swift @@ -26,11 +26,11 @@ final class ArticleThemesManager: NSObject, NSFilePresenter { var currentThemeName: String { get { - return AppDefaults.shared.currentThemeName ?? AppDefaults.defaultThemeName + return AppDefaults.currentThemeName ?? AppDefaults.defaultThemeName } set { if newValue != currentThemeName { - AppDefaults.shared.currentThemeName = newValue + AppDefaults.currentThemeName = newValue updateThemeNames() updateCurrentTheme() } diff --git a/Shared/Defaults/AppDefaults.swift b/Shared/Defaults/AppDefaults.swift new file mode 100644 index 000000000..6bab11be7 --- /dev/null +++ b/Shared/Defaults/AppDefaults.swift @@ -0,0 +1,662 @@ +// +// AppDefaults.swift +// NetNewsWire +// +// Created by Brent Simmons on 1/25/25. +// Copyright © 2025 Ranchero Software. All rights reserved. +// + +import Foundation +#if os(macOS) +import AppKit +#endif + +struct AppDefaults { + + enum Key: String { + case firstRunDate + case lastImageCacheFlushDate + case timelineGroupByFeed + case timelineSortDirection + case addFeedAccountID + case addFeedFolderName + case addFolderAccountID + case currentThemeName + case articleContentJavascriptEnabled + +#if os(macOS) + case windowState + case sidebarFontSize + case timelineFontSize + case detailFontSize + case openInBrowserInBackground + case subscribeToFeedsInDefaultBrowser + case articleTextSize + case refreshInterval + case importOPMLAccountID + case exportOPMLAccountID + case defaultBrowserID + + case webInspectorEnabled = "WebInspectorEnabled" + case webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached" + + // Hidden prefs + case showDebugMenu + case timelineShowsSeparators = "CorreiaSeparators" + case showTitleOnMainWindow = "KafasisTitleMode" + case feedDoubleClickMarkAsRead = "GruberFeedDoubleClickMarkAsRead" + case suppressSyncOnLaunch = "DevroeSuppressSyncOnLaunch" + +#elseif os(iOS) + case userInterfaceColorPalette + case refreshClearsReadArticles + case timelineNumberOfLines + case timelineIconDimension = "timelineIconSize" + case articleFullscreenAvailable + case articleFullscreenEnabled + case confirmMarkAllAsRead + case lastRefresh + case useSystemBrowser +#endif + } + + static let defaultThemeName = "Default" + + static let isDeveloperBuild: Bool = { + if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" { + return true + } + return false + }() + + static let isFirstRun: Bool = { + if firstRunDate == nil { + firstRunDate = Date() + return true + } + return false + }() + +#if os(macOS) + static func registerDefaults() { + +#if DEBUG + let showDebugMenu = true +#else + let showDebugMenu = false +#endif + + let defaults: [String: Any] = [ + Key.sidebarFontSize.rawValue: FontSize.medium.rawValue, + Key.timelineFontSize.rawValue: FontSize.medium.rawValue, + Key.detailFontSize.rawValue: FontSize.medium.rawValue, + Key.timelineSortDirection.rawValue: ComparisonResult.orderedDescending.rawValue, + Key.timelineGroupByFeed.rawValue: false, + Key.refreshInterval.rawValue: RefreshInterval.everyHour.rawValue, + Key.showDebugMenu.rawValue: showDebugMenu, + Key.currentThemeName.rawValue: Self.defaultThemeName, + Key.articleContentJavascriptEnabled.rawValue: true, + "NSScrollViewShouldScrollUnderTitlebar": false + ] + UserDefaults.standard.register(defaults: defaults) + } +#elseif os(iOS) + + static func registerDefaults() { + + // TODO: migrate all (or as many as possible) out of shared + let sharedDefaults: [String: Any] = [ + Key.userInterfaceColorPalette.rawValue: UserInterfaceColorPalette.automatic.rawValue, + Key.timelineGroupByFeed.rawValue: false, + Key.refreshClearsReadArticles.rawValue: false, + Key.timelineNumberOfLines.rawValue: 2, + Key.timelineIconDimension.rawValue: IconSize.medium.rawValue, + Key.timelineSortDirection.rawValue: ComparisonResult.orderedDescending.rawValue, + Key.articleFullscreenAvailable.rawValue: false, + Key.articleFullscreenEnabled.rawValue: false, + Key.confirmMarkAllAsRead.rawValue: true + ] + appGroupStorage.register(defaults: sharedDefaults) + + let defaults: [String: Any] = [ + Key.currentThemeName.rawValue: Self.defaultThemeName, + Key.articleContentJavascriptEnabled.rawValue: true + ] + UserDefaults.standard.register(defaults: defaults) + } +#endif + + static var addFeedAccountID: String? { + get { + string(key: .addFeedAccountID) + } + set { + setString(newValue, key: .addFeedAccountID) + } + } + + static var addFeedFolderName: String? { + get { + string(key: .addFeedFolderName) + } + set { + setString(newValue, key: .addFeedFolderName) + } + } + + static var addFolderAccountID: String? { + get { + string(key: .addFolderAccountID) + } + set { + setString(newValue, key: .addFolderAccountID) + } + } + + static var currentThemeName: String? { + get { + string(key: .currentThemeName) + } + set { + setString(newValue, key: .currentThemeName) + } + } + + static var isArticleContentJavascriptEnabled: Bool { + get { + bool(key: .articleContentJavascriptEnabled) + } + set { + setBool(newValue, key: .articleContentJavascriptEnabled) + } + } + +#if os(macOS) + static var timelineGroupByFeed: Bool { + get { + bool(key: .timelineGroupByFeed) + } + set { + setBool(newValue, key: .timelineGroupByFeed) + } + } +#elseif os(iOS) + static var timelineGroupByFeed: Bool { + // TODO: migrate to not shared + get { + sharedBool(key: .timelineGroupByFeed) + } + set { + setSharedBool(newValue, key: .timelineGroupByFeed) + } + } +#endif + +#if os(macOS) + static var timelineSortDirection: ComparisonResult { + get { + sortDirection(key: .timelineSortDirection) + } + set { + setSortDirection(newValue, key: .timelineSortDirection) + } + } +#else + static var timelineSortDirection: ComparisonResult { + // TODO: migrate to not shared + get { + sharedSortDirection(key: .timelineSortDirection) + } + set { + setSharedSortDirection(newValue, key: .timelineSortDirection) + } + } +#endif + +#if os(macOS) + static var lastImageCacheFlushDate: Date? { + get { + date(key: .lastImageCacheFlushDate) + } + set { + setDate(newValue, key: .lastImageCacheFlushDate) + } + } +#else + static var lastImageCacheFlushDate: Date? { + // TODO: migrate to not shared + get { + sharedDate(key: .lastImageCacheFlushDate) + } + set { + setSharedDate(newValue, key: .lastImageCacheFlushDate) + } + } +#endif +} + +// MARK: - Mac-only Defaults + +#if os(macOS) + +extension AppDefaults { + + static var windowState: [AnyHashable: Any]? { + get { + UserDefaults.standard.object(forKey: Key.windowState.rawValue) as? [AnyHashable: Any] + } + set { + UserDefaults.standard.set(newValue, forKey: Key.windowState.rawValue) + } + } + + static var sidebarFontSize: FontSize { + get { + fontSize(key: .sidebarFontSize) + } + set { + setFontSize(newValue, key: .sidebarFontSize) + } + } + + static var timelineFontSize: FontSize { + get { + fontSize(key: .timelineFontSize) + } + set { + setFontSize(newValue, key: .timelineFontSize) + } + } + + static var detailFontSize: FontSize { + get { + fontSize(key: .detailFontSize) + } + set { + setFontSize(newValue, key: .detailFontSize) + } + } + + static var importOPMLAccountID: String? { + get { + string(key: .importOPMLAccountID) + } + set { + setString(newValue, key: .importOPMLAccountID) + } + } + + static var exportOPMLAccountID: String? { + get { + string(key: .exportOPMLAccountID) + } + set { + setString(newValue, key: .exportOPMLAccountID) + } + } + + static var defaultBrowserID: String? { + get { + string(key: .defaultBrowserID) + } + set { + setString(newValue, key: .defaultBrowserID) + } + } + + static var refreshInterval: RefreshInterval { + get { + let rawValue = int(key: .refreshInterval) + return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour + } + set { + setInt(newValue.rawValue, key: .refreshInterval) + } + } + + static var openInBrowserInBackground: Bool { + get { + bool(key: .openInBrowserInBackground) + } + set { + setBool(newValue, key: .openInBrowserInBackground) + } + } + + /// Shared with Subscribe to Feed Safari extension. + static var subscribeToFeedsInDefaultBrowser: Bool { + get { + sharedBool(key: .subscribeToFeedsInDefaultBrowser) + } + set { + setSharedBool(newValue, key: .subscribeToFeedsInDefaultBrowser) + } + } + + static var articleTextSize: ArticleTextSize { + get { + let rawValue = int(key: .articleTextSize) + return ArticleTextSize(rawValue: rawValue) ?? ArticleTextSize.large + } + set { + setInt(newValue.rawValue, key: .articleTextSize) + } + } + + static var webInspectorEnabled: Bool { + get { + bool(key: .webInspectorEnabled) + } + set { + setBool(newValue, key: .webInspectorEnabled) + } + } + + static var webInspectorStartsAttached: Bool { + get { + bool(key: .webInspectorStartsAttached) + } + set { + setBool(newValue, key: .webInspectorStartsAttached) + } + } + + private static let smallestFontSizeRawValue = FontSize.small.rawValue + private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue + + static func fontSize(key: Key) -> FontSize { + // Punted till after 1.0. + return .medium + +// var rawFontSize = int(for: key) +// if rawFontSize < smallestFontSizeRawValue { +// rawFontSize = smallestFontSizeRawValue +// } +// if rawFontSize > largestFontSizeRawValue { +// rawFontSize = largestFontSizeRawValue +// } +// return FontSize(rawValue: rawFontSize)! + } + + static func setFontSize(_ fontSize: FontSize, key: Key) { + setInt(fontSize.rawValue, key: key) + } + + static func actualFontSize(for fontSize: FontSize) -> CGFloat { + switch fontSize { + case .small: + return NSFont.systemFontSize + case .medium: + return actualFontSize(for: .small) + 1.0 + case .large: + return actualFontSize(for: .medium) + 4.0 + case .veryLarge: + return actualFontSize(for: .large) + 8.0 + } + } + + // MARK: - Hidden prefs + + static var showTitleOnMainWindow: Bool { + bool(key: .showTitleOnMainWindow) + } + + static var showDebugMenu: Bool { + bool(key: .showDebugMenu) + } + + static var suppressSyncOnLaunch: Bool { + bool(key: .suppressSyncOnLaunch) + } + + static var timelineShowsSeparators: Bool { + bool(key: .timelineShowsSeparators) + } + + static var feedDoubleClickMarkAsRead: Bool { + bool(key: .feedDoubleClickMarkAsRead) + } +} + +#endif + +// MARK: - iOS-only Defaults + +#if os(iOS) + +extension AppDefaults { + + static var userInterfaceColorPalette: UserInterfaceColorPalette { + // TODO: migrate to not shared + get { + if let result = UserInterfaceColorPalette(rawValue: sharedInt(key: .userInterfaceColorPalette)) { + return result + } + return .automatic + } + set { + setSharedInt(newValue.rawValue, key: .userInterfaceColorPalette) + } + } + + static var refreshClearsReadArticles: Bool { + // TODO: migrate to not shared + get { + sharedBool(key: .refreshClearsReadArticles) + } + set { + setSharedBool(newValue, key: .refreshClearsReadArticles) + } + } + + static var useSystemBrowser: Bool { + get { + bool(key: .useSystemBrowser) + } + set { + setBool(newValue, key: .useSystemBrowser) + } + } + + static var timelineIconSize: IconSize { + // TODO: migrate to not shared + get { + let rawValue = sharedInt(key: .timelineIconDimension) + return IconSize(rawValue: rawValue) ?? IconSize.medium + } + set { + setSharedInt(newValue.rawValue, key: .timelineIconDimension) + } + } + + static var articleFullscreenAvailable: Bool { + // TODO: migrate to not shared + get { + sharedBool(key: .articleFullscreenAvailable) + } + set { + setSharedBool(newValue, key: .articleFullscreenAvailable) + } + } + + static var articleFullscreenEnabled: Bool { + // TODO: migrate to not shared + get { + sharedBool(key: .articleFullscreenEnabled) + } + set { + setSharedBool(newValue, key: .articleFullscreenEnabled) + } + } + + static var logicalArticleFullscreenEnabled: Bool { + articleFullscreenAvailable && articleFullscreenEnabled + } + + static var confirmMarkAllAsRead: Bool { + // TODO: migrate to not shared + get { + sharedBool(key: .confirmMarkAllAsRead) + } + set { + setSharedBool(newValue, key: .confirmMarkAllAsRead) + } + } + + static var lastRefresh: Date? { + // TODO: migrate to not shared + get { + sharedDate(key: .lastRefresh) + } + set { + setSharedDate(newValue, key: .lastRefresh) + } + } + + static var timelineNumberOfLines: Int { + // TODO: migrate to not shared + get { + sharedInt(key: .timelineNumberOfLines) + } + set { + setSharedInt(newValue, key: .timelineNumberOfLines) + } + } +} + +#endif + +// MARK: - Private + +private extension AppDefaults { + +#if os(macOS) + static var firstRunDate: Date? { + get { + date(key: .firstRunDate) + } + set { + setDate(newValue, key: .firstRunDate) + } + } +#elseif os(iOS) + static var firstRunDate: Date? { + get { + sharedDate(key: .firstRunDate) + } + set { + setSharedDate(newValue, key: .firstRunDate) + } + } +#endif + + static func bool(key: Key) -> Bool { + UserDefaults.standard.bool(forKey: key.rawValue) + } + + static func setBool(_ flag: Bool, key: Key) { + UserDefaults.standard.set(flag, forKey: key.rawValue) + } + + static func int(key: Key) -> Int { + UserDefaults.standard.integer(forKey: key.rawValue) + } + + static func setInt(_ x: Int, key: Key) { + UserDefaults.standard.set(x, forKey: key.rawValue) + } + + static func date(key: Key) -> Date? { + UserDefaults.standard.object(forKey: key.rawValue) as? Date + } + + static func setDate(_ date: Date?, key: Key) { + UserDefaults.standard.set(date, forKey: key.rawValue) + } + + static func string(key: Key) -> String? { + return UserDefaults.standard.string(forKey: key.rawValue) + } + + static func setString(_ value: String?, key: Key) { + UserDefaults.standard.set(value, forKey: key.rawValue) + } + + static func sortDirection(key: Key) -> ComparisonResult { + let rawInt = int(key: key) + if rawInt == ComparisonResult.orderedAscending.rawValue { + return .orderedAscending + } + return .orderedDescending + } + + static func setSortDirection(_ value: ComparisonResult, key: Key) { + if value == .orderedAscending { + setInt(ComparisonResult.orderedAscending.rawValue, key: key) + } else { + setInt(ComparisonResult.orderedDescending.rawValue, key: key) + } + } +} + +// MARK: - App Group Storage + +// These are for preferences that are shared between the app and extensions and widgets. +// These are to be used *only* for preferences for that are actually shared, which should be rare. + +private extension AppDefaults { + + static var appGroupStorage: UserDefaults = { + +#if os(macOS) + let appGroupSuiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String +#elseif os(iOS) + let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String + let appGroupSuiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)" +#endif + + return UserDefaults(suiteName: appGroupSuiteName)! + }() + + static func sharedBool(key: Key) -> Bool { + appGroupStorage.bool(forKey: key.rawValue) + } + + static func setSharedBool(_ flag: Bool, key: Key) { + appGroupStorage.set(flag, forKey: key.rawValue) + } + +#if os(iOS) + static func sharedInt(key: Key) -> Int { + appGroupStorage.integer(forKey: key.rawValue) + } + + static func setSharedInt(_ x: Int, key: Key) { + appGroupStorage.set(x, forKey: key.rawValue) + } + + static func sharedDate(key: Key) -> Date? { + appGroupStorage.object(forKey: key.rawValue) as? Date + } + + static func setSharedDate(_ date: Date?, key: Key) { + appGroupStorage.set(date, forKey: key.rawValue) + } + + static func sharedSortDirection(key: Key) -> ComparisonResult { + let rawInt = sharedInt(key: key) + if rawInt == ComparisonResult.orderedAscending.rawValue { + return .orderedAscending + } + return .orderedDescending + } + + static func setSharedSortDirection(_ value: ComparisonResult, key: Key) { + if value == .orderedAscending { + setSharedInt(ComparisonResult.orderedAscending.rawValue, key: key) + } else { + setSharedInt(ComparisonResult.orderedDescending.rawValue, key: key) + } + } +#endif +} diff --git a/Shared/ArticleRendering/ArticleTextSize.swift b/Shared/Defaults/ArticleTextSize.swift similarity index 89% rename from Shared/ArticleRendering/ArticleTextSize.swift rename to Shared/Defaults/ArticleTextSize.swift index 456bee65d..d2211cd7d 100644 --- a/Shared/ArticleRendering/ArticleTextSize.swift +++ b/Shared/Defaults/ArticleTextSize.swift @@ -2,8 +2,8 @@ // ArticleTextSize.swift // NetNewsWire // -// Created by Maurice Parker on 11/3/20. -// Copyright © 2020 Ranchero Software. All rights reserved. +// Created by Brent Simmons on 1/26/25. +// Copyright © 2025 Ranchero Software. All rights reserved. // import Foundation @@ -46,5 +46,4 @@ enum ArticleTextSize: Int, CaseIterable, Identifiable { return NSLocalizedString("Extra Extra Large", comment: "XX-Large") } } - } diff --git a/Shared/Defaults/FontSize.swift b/Shared/Defaults/FontSize.swift new file mode 100644 index 000000000..d0ea14b35 --- /dev/null +++ b/Shared/Defaults/FontSize.swift @@ -0,0 +1,16 @@ +// +// FontSize.swift +// NetNewsWire +// +// Created by Brent Simmons on 1/26/25. +// Copyright © 2025 Ranchero Software. All rights reserved. +// + +import Foundation + +enum FontSize: Int { + case small = 0 + case medium = 1 + case large = 2 + case veryLarge = 3 +} diff --git a/Shared/Timer/RefreshInterval.swift b/Shared/Defaults/RefreshInterval.swift similarity index 99% rename from Shared/Timer/RefreshInterval.swift rename to Shared/Defaults/RefreshInterval.swift index d313bfab6..c663d5883 100644 --- a/Shared/Timer/RefreshInterval.swift +++ b/Shared/Defaults/RefreshInterval.swift @@ -56,5 +56,4 @@ enum RefreshInterval: Int, CaseIterable, Identifiable { return NSLocalizedString("Every 8 Hours", comment: "Every 8 Hours") } } - } diff --git a/Shared/Defaults/UserInterfaceColorPalette.swift b/Shared/Defaults/UserInterfaceColorPalette.swift new file mode 100644 index 000000000..a01abec09 --- /dev/null +++ b/Shared/Defaults/UserInterfaceColorPalette.swift @@ -0,0 +1,26 @@ +// +// UserInterfaceColorPalette.swift +// NetNewsWire +// +// Created by Brent Simmons on 1/26/25. +// Copyright © 2025 Ranchero Software. All rights reserved. +// + +import Foundation + +enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable { + case automatic = 0 + case light = 1 + case dark = 2 + + var description: String { + switch self { + case .automatic: + return NSLocalizedString("Automatic", comment: "Automatic") + case .light: + return NSLocalizedString("Light", comment: "Light") + case .dark: + return NSLocalizedString("Dark", comment: "Dark") + } + } +} diff --git a/Shared/Extensions/AddFeedDefaultContainer.swift b/Shared/Extensions/AddFeedDefaultContainer.swift index cd0c8abd6..6e457ea20 100644 --- a/Shared/Extensions/AddFeedDefaultContainer.swift +++ b/Shared/Extensions/AddFeedDefaultContainer.swift @@ -13,8 +13,8 @@ struct AddFeedDefaultContainer { static var defaultContainer: Container? { - if let accountID = AppDefaults.shared.addFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) { - if let folderName = AppDefaults.shared.addFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) { + if let accountID = AppDefaults.addFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) { + if let folderName = AppDefaults.addFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) { return folder } else { return substituteContainerIfNeeded(account: account) @@ -28,11 +28,11 @@ struct AddFeedDefaultContainer { } static func saveDefaultContainer(_ container: Container) { - AppDefaults.shared.addFeedAccountID = container.account?.accountID + AppDefaults.addFeedAccountID = container.account?.accountID if let folder = container as? Folder { - AppDefaults.shared.addFeedFolderName = folder.nameForDisplay + AppDefaults.addFeedFolderName = folder.nameForDisplay } else { - AppDefaults.shared.addFeedFolderName = nil + AppDefaults.addFeedFolderName = nil } } diff --git a/Shared/Extensions/CacheCleaner.swift b/Shared/Extensions/CacheCleaner.swift index 5fe39fe07..c8cc67f0f 100644 --- a/Shared/Extensions/CacheCleaner.swift +++ b/Shared/Extensions/CacheCleaner.swift @@ -16,8 +16,8 @@ struct CacheCleaner { static func purgeIfNecessary() { - guard let flushDate = AppDefaults.shared.lastImageCacheFlushDate else { - AppDefaults.shared.lastImageCacheFlushDate = Date() + guard let flushDate = AppDefaults.lastImageCacheFlushDate else { + AppDefaults.lastImageCacheFlushDate = Date() return } @@ -42,7 +42,7 @@ struct CacheCleaner { } } - AppDefaults.shared.lastImageCacheFlushDate = Date() + AppDefaults.lastImageCacheFlushDate = Date() } } diff --git a/Shared/ShareExtension/ShareDefaultContainer.swift b/Shared/ShareExtension/ShareDefaultContainer.swift index aabbe0c6e..1af9f9637 100644 --- a/Shared/ShareExtension/ShareDefaultContainer.swift +++ b/Shared/ShareExtension/ShareDefaultContainer.swift @@ -12,8 +12,8 @@ struct ShareDefaultContainer { static func defaultContainer(containers: ExtensionContainers) -> ExtensionContainer? { - if let accountID = AppDefaults.shared.addFeedAccountID, let account = containers.accounts.first(where: { $0.accountID == accountID }) { - if let folderName = AppDefaults.shared.addFeedFolderName, let folder = account.folders.first(where: { $0.name == folderName }) { + if let accountID = AppDefaults.addFeedAccountID, let account = containers.accounts.first(where: { $0.accountID == accountID }) { + if let folderName = AppDefaults.addFeedFolderName, let folder = account.folders.first(where: { $0.name == folderName }) { return folder } else { return substituteContainerIfNeeded(account: account) @@ -27,11 +27,11 @@ struct ShareDefaultContainer { } static func saveDefaultContainer(_ container: ExtensionContainer) { - AppDefaults.shared.addFeedAccountID = container.accountID + AppDefaults.addFeedAccountID = container.accountID if let folder = container as? ExtensionFolder { - AppDefaults.shared.addFeedFolderName = folder.name + AppDefaults.addFeedFolderName = folder.name } else { - AppDefaults.shared.addFeedFolderName = nil + AppDefaults.addFeedFolderName = nil } } diff --git a/Shared/Timer/AccountRefreshTimer.swift b/Shared/Timer/AccountRefreshTimer.swift index c642a37ae..7ba1080a4 100644 --- a/Shared/Timer/AccountRefreshTimer.swift +++ b/Shared/Timer/AccountRefreshTimer.swift @@ -20,7 +20,7 @@ final class AccountRefreshTimer { func fireOldTimer() { if let timer = internalTimer { if timer.fireDate < Date() { - if AppDefaults.shared.refreshInterval != .manually { + if AppDefaults.refreshInterval != .manually { timedRefresh(nil) } } @@ -42,7 +42,7 @@ final class AccountRefreshTimer { return } - let refreshInterval = AppDefaults.shared.refreshInterval + let refreshInterval = AppDefaults.refreshInterval if refreshInterval == .manually { invalidate() return diff --git a/iOS/Add/AddFolderViewController.swift b/iOS/Add/AddFolderViewController.swift index fcfcba5d9..112066451 100644 --- a/iOS/Add/AddFolderViewController.swift +++ b/iOS/Add/AddFolderViewController.swift @@ -25,7 +25,7 @@ final class AddFolderViewController: UITableViewController { private var accounts: [Account]! { didSet { - if let predefinedAccount = accounts.first(where: { $0.accountID == AppDefaults.shared.addFolderAccountID }) { + if let predefinedAccount = accounts.first(where: { $0.accountID == AppDefaults.addFolderAccountID }) { selectedAccount = predefinedAccount } else { selectedAccount = accounts[0] @@ -67,7 +67,7 @@ final class AddFolderViewController: UITableViewController { } private func didSelect(_ account: Account) { - AppDefaults.shared.addFolderAccountID = account.accountID + AppDefaults.addFolderAccountID = account.accountID selectedAccount = account } diff --git a/iOS/AppDefaults.swift b/iOS/AppDefaults.swift deleted file mode 100644 index 412d9cfb8..000000000 --- a/iOS/AppDefaults.swift +++ /dev/null @@ -1,293 +0,0 @@ -// -// AppDefaults.swift -// NetNewsWire -// -// Created by Brent Simmons on 9/22/17. -// Copyright © 2017 Ranchero Software. All rights reserved. -// - -import UIKit - -enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable { - case automatic = 0 - case light = 1 - case dark = 2 - - var description: String { - switch self { - case .automatic: - return NSLocalizedString("Automatic", comment: "Automatic") - case .light: - return NSLocalizedString("Light", comment: "Light") - case .dark: - return NSLocalizedString("Dark", comment: "Dark") - } - } - -} - -final class AppDefaults { - - static let defaultThemeName = "Default" - - static let shared = AppDefaults() - private init() {} - - static var store: UserDefaults = { - let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String - let suiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)" - return UserDefaults.init(suiteName: suiteName)! - }() - - let isDeveloperBuild: Bool = { - if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" { - return true - } - return false - }() - - let isFirstRun: Bool = { - if AppDefaults.store.object(forKey: AppDefaultsKey.firstRunDate) as? Date == nil { - firstRunDate = Date() - return true - } - return false - }() - - static var userInterfaceColorPalette: UserInterfaceColorPalette { - get { - if let result = UserInterfaceColorPalette(rawValue: int(for: AppDefaultsKey.userInterfaceColorPalette)) { - return result - } - return .automatic - } - set { - setInt(for: AppDefaultsKey.userInterfaceColorPalette, newValue.rawValue) - } - } - - var addFeedAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFeedAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFeedAccountID, newValue) - } - } - - var addFeedFolderName: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFeedFolderName) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFeedFolderName, newValue) - } - } - - var addFolderAccountID: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.addFolderAccountID) - } - set { - AppDefaults.setString(for: AppDefaultsKey.addFolderAccountID, newValue) - } - } - - var useSystemBrowser: Bool { - get { - return UserDefaults.standard.bool(forKey: AppDefaultsKey.useSystemBrowser) - } - set { - UserDefaults.standard.setValue(newValue, forKey: AppDefaultsKey.useSystemBrowser) - } - } - - var lastImageCacheFlushDate: Date? { - get { - return AppDefaults.date(for: AppDefaultsKey.lastImageCacheFlushDate) - } - set { - AppDefaults.setDate(for: AppDefaultsKey.lastImageCacheFlushDate, newValue) - } - } - - var timelineGroupByFeed: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.timelineGroupByFeed) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.timelineGroupByFeed, newValue) - } - } - - var refreshClearsReadArticles: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.refreshClearsReadArticles) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.refreshClearsReadArticles, newValue) - } - } - - var timelineSortDirection: ComparisonResult { - get { - return AppDefaults.sortDirection(for: AppDefaultsKey.timelineSortDirection) - } - set { - AppDefaults.setSortDirection(for: AppDefaultsKey.timelineSortDirection, newValue) - } - } - - var articleFullscreenAvailable: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.articleFullscreenAvailable) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.articleFullscreenAvailable, newValue) - } - } - - var articleFullscreenEnabled: Bool { - get { - return articleFullscreenAvailable && AppDefaults.bool(for: AppDefaultsKey.articleFullscreenEnabled) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.articleFullscreenEnabled, newValue) - } - } - - var logicalArticleFullscreenEnabled: Bool { - articleFullscreenAvailable && articleFullscreenEnabled - } - - var confirmMarkAllAsRead: Bool { - get { - return AppDefaults.bool(for: AppDefaultsKey.confirmMarkAllAsRead) - } - set { - AppDefaults.setBool(for: AppDefaultsKey.confirmMarkAllAsRead, newValue) - } - } - - var lastRefresh: Date? { - get { - return AppDefaults.date(for: AppDefaultsKey.lastRefresh) - } - set { - AppDefaults.setDate(for: AppDefaultsKey.lastRefresh, newValue) - } - } - - var timelineNumberOfLines: Int { - get { - return AppDefaults.int(for: AppDefaultsKey.timelineNumberOfLines) - } - set { - AppDefaults.setInt(for: AppDefaultsKey.timelineNumberOfLines, newValue) - } - } - - var timelineIconSize: IconSize { - get { - let rawValue = AppDefaults.store.integer(forKey: AppDefaultsKey.timelineIconDimension) - return IconSize(rawValue: rawValue) ?? IconSize.medium - } - set { - AppDefaults.store.set(newValue.rawValue, forKey: AppDefaultsKey.timelineIconDimension) - } - } - - var currentThemeName: String? { - get { - return AppDefaults.string(for: AppDefaultsKey.currentThemeName) - } - set { - AppDefaults.setString(for: AppDefaultsKey.currentThemeName, newValue) - } - } - - var isArticleContentJavascriptEnabled: Bool { - get { - UserDefaults.standard.bool(forKey: AppDefaultsKey.articleContentJavascriptEnabled) - } - set { - UserDefaults.standard.set(newValue, forKey: AppDefaultsKey.articleContentJavascriptEnabled) - } - } - - static func registerDefaults() { - let defaults: [String: Any] = [AppDefaultsKey.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue, - AppDefaultsKey.timelineGroupByFeed: false, - AppDefaultsKey.refreshClearsReadArticles: false, - AppDefaultsKey.timelineNumberOfLines: 2, - AppDefaultsKey.timelineIconDimension: IconSize.medium.rawValue, - AppDefaultsKey.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, - AppDefaultsKey.articleFullscreenAvailable: false, - AppDefaultsKey.articleFullscreenEnabled: false, - AppDefaultsKey.confirmMarkAllAsRead: true, - AppDefaultsKey.currentThemeName: Self.defaultThemeName] - AppDefaults.store.register(defaults: defaults) - } - -} - -private extension AppDefaults { - - static var firstRunDate: Date? { - get { - return date(for: AppDefaultsKey.firstRunDate) - } - set { - setDate(for: AppDefaultsKey.firstRunDate, newValue) - } - } - - static func string(for key: String) -> String? { - return UserDefaults.standard.string(forKey: key) - } - - static func setString(for key: String, _ value: String?) { - UserDefaults.standard.set(value, forKey: key) - } - - static func bool(for key: String) -> Bool { - return AppDefaults.store.bool(forKey: key) - } - - static func setBool(for key: String, _ flag: Bool) { - AppDefaults.store.set(flag, forKey: key) - } - - static func int(for key: String) -> Int { - return AppDefaults.store.integer(forKey: key) - } - - static func setInt(for key: String, _ x: Int) { - AppDefaults.store.set(x, forKey: key) - } - - static func date(for key: String) -> Date? { - return AppDefaults.store.object(forKey: key) as? Date - } - - static func setDate(for key: String, _ date: Date?) { - AppDefaults.store.set(date, forKey: key) - } - - static func sortDirection(for key: String) -> ComparisonResult { - let rawInt = int(for: key) - if rawInt == ComparisonResult.orderedAscending.rawValue { - return .orderedAscending - } - return .orderedDescending - } - - static func setSortDirection(for key: String, _ value: ComparisonResult) { - if value == .orderedAscending { - setInt(for: key, ComparisonResult.orderedAscending.rawValue) - } else { - setInt(for: key, ComparisonResult.orderedDescending.rawValue) - } - } - -} diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 78d77b587..af1edafea 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -76,7 +76,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { AppDefaults.registerDefaults() - let isFirstRun = AppDefaults.shared.isFirstRun + let isFirstRun = AppDefaults.isFirstRun if isFirstRun { logger.info("Is first run.") } @@ -148,7 +148,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC } @objc func accountRefreshDidFinish(_ note: Notification) { - AppDefaults.shared.lastRefresh = Date() + AppDefaults.lastRefresh = Date() } // MARK: - API @@ -180,7 +180,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC extensionFeedAddRequestFile.resume() syncTimer?.update() - if let lastRefresh = AppDefaults.shared.lastRefresh { + if let lastRefresh = AppDefaults.lastRefresh { if Date() > lastRefresh.addingTimeInterval(15 * 60) { AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) } else { diff --git a/iOS/Article/ArticleViewController.swift b/iOS/Article/ArticleViewController.swift index 7404814d2..9e90f1157 100644 --- a/iOS/Article/ArticleViewController.swift +++ b/iOS/Article/ArticleViewController.swift @@ -149,7 +149,7 @@ final class ArticleViewController: UIViewController { articleExtractorButton.buttonState = controller.articleExtractorButtonState self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil) - if AppDefaults.shared.logicalArticleFullscreenEnabled { + if AppDefaults.logicalArticleFullscreenEnabled { controller.hideBars() } @@ -165,7 +165,7 @@ final class ArticleViewController: UIViewController { } override func viewWillAppear(_ animated: Bool) { - let hideToolbars = AppDefaults.shared.logicalArticleFullscreenEnabled + let hideToolbars = AppDefaults.logicalArticleFullscreenEnabled if hideToolbars { currentWebViewController?.hideBars() } else { @@ -219,7 +219,7 @@ final class ArticleViewController: UIViewController { starBarButtonItem.isEnabled = true let permalinkPresent = article.preferredLink != nil - articleExtractorButton.isEnabled = permalinkPresent && !AppDefaults.shared.isDeveloperBuild + articleExtractorButton.isEnabled = permalinkPresent && !AppDefaults.isDeveloperBuild actionBarButtonItem.isEnabled = permalinkPresent if article.status.read { @@ -265,7 +265,7 @@ final class ArticleViewController: UIViewController { @objc func willEnterForeground(_ note: Notification) { // The toolbar will come back on you if you don't hide it again - if AppDefaults.shared.logicalArticleFullscreenEnabled { + if AppDefaults.logicalArticleFullscreenEnabled { currentWebViewController?.hideBars() } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 4fc481288..8b1ddba0d 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -37,7 +37,7 @@ final class WebViewController: UIViewController { private lazy var contextMenuInteraction = UIContextMenuInteraction(delegate: self) private var isFullScreenAvailable: Bool { - return AppDefaults.shared.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed + return AppDefaults.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed } private lazy var articleIconSchemeHandler = ArticleIconSchemeHandler(delegate: self) private lazy var transition = ImageTransition(controller: self) @@ -197,7 +197,7 @@ final class WebViewController: UIViewController { } func showBars() { - AppDefaults.shared.articleFullscreenEnabled = false + AppDefaults.articleFullscreenEnabled = false coordinator.showStatusBar() topShowBarsViewConstraint?.constant = 0 bottomShowBarsViewConstraint?.constant = 0 @@ -208,7 +208,7 @@ final class WebViewController: UIViewController { func hideBars() { if isFullScreenAvailable { - AppDefaults.shared.articleFullscreenEnabled = true + AppDefaults.articleFullscreenEnabled = true coordinator.hideStatusBar() topShowBarsViewConstraint?.constant = -44.0 bottomShowBarsViewConstraint?.constant = 44.0 @@ -271,7 +271,7 @@ final class WebViewController: UIViewController { func openInAppBrowser() { guard let url = article?.preferredURL else { return } - if AppDefaults.shared.useSystemBrowser { + if AppDefaults.useSystemBrowser { UIApplication.shared.open(url, options: [:]) } else { openURLInSafariViewController(url) @@ -381,7 +381,7 @@ extension WebViewController: WKNavigationDelegate { let components = URLComponents(url: url, resolvingAgainstBaseURL: false) if components?.scheme == "http" || components?.scheme == "https" { decisionHandler(.cancel) - if AppDefaults.shared.useSystemBrowser { + if AppDefaults.useSystemBrowser { UIApplication.shared.open(url, options: [:]) } else { UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { didOpen in @@ -674,7 +674,7 @@ private extension WebViewController { topShowBarsView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(topShowBarsView) - if AppDefaults.shared.logicalArticleFullscreenEnabled { + if AppDefaults.logicalArticleFullscreenEnabled { topShowBarsViewConstraint = view.topAnchor.constraint(equalTo: topShowBarsView.bottomAnchor, constant: -44.0) } else { topShowBarsViewConstraint = view.topAnchor.constraint(equalTo: topShowBarsView.bottomAnchor, constant: 0.0) @@ -694,7 +694,7 @@ private extension WebViewController { topShowBarsView.backgroundColor = .clear bottomShowBarsView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(bottomShowBarsView) - if AppDefaults.shared.logicalArticleFullscreenEnabled { + if AppDefaults.logicalArticleFullscreenEnabled { bottomShowBarsViewConstraint = view.bottomAnchor.constraint(equalTo: bottomShowBarsView.topAnchor, constant: 44.0) } else { bottomShowBarsViewConstraint = view.bottomAnchor.constraint(equalTo: bottomShowBarsView.topAnchor, constant: 0.0) diff --git a/iOS/MainTimeline/MarkAsReadAlertController.swift b/iOS/MainTimeline/MarkAsReadAlertController.swift index 50488ef9e..00343a2b1 100644 --- a/iOS/MainTimeline/MarkAsReadAlertController.swift +++ b/iOS/MainTimeline/MarkAsReadAlertController.swift @@ -30,7 +30,7 @@ struct MarkAsReadAlertController { return } - if AppDefaults.shared.confirmMarkAllAsRead { + if AppDefaults.confirmMarkAllAsRead { let alertController = MarkAsReadAlertController.alert(coordinator: coordinator, confirmTitle: confirmTitle, cancelCompletion: cancelCompletion, sourceType: sourceType) { _ in completion() } diff --git a/iOS/MainTimeline/TimelineViewController.swift b/iOS/MainTimeline/TimelineViewController.swift index 8faf69be6..dca1b4442 100644 --- a/iOS/MainTimeline/TimelineViewController.swift +++ b/iOS/MainTimeline/TimelineViewController.swift @@ -90,8 +90,8 @@ final class TimelineViewController: UITableViewController, UndoableCommandRunner tableView.dataSource = dataSource tableView.isPrefetchingEnabled = false - numberOfTextLines = AppDefaults.shared.timelineNumberOfLines - iconSize = AppDefaults.shared.timelineIconSize + numberOfTextLines = AppDefaults.timelineNumberOfLines + iconSize = AppDefaults.timelineIconSize resetEstimatedRowHeight() if let titleView = Bundle.main.loadNibNamed("MainTimelineTitleView", owner: self, options: nil)?[0] as? MainTimelineTitleView { @@ -510,9 +510,9 @@ final class TimelineViewController: UITableViewController, UndoableCommandRunner @objc func userDefaultsDidChange(_ note: Notification) { DispatchQueue.main.async { - if self.numberOfTextLines != AppDefaults.shared.timelineNumberOfLines || self.iconSize != AppDefaults.shared.timelineIconSize { - self.numberOfTextLines = AppDefaults.shared.timelineNumberOfLines - self.iconSize = AppDefaults.shared.timelineIconSize + if self.numberOfTextLines != AppDefaults.timelineNumberOfLines || self.iconSize != AppDefaults.timelineIconSize { + self.numberOfTextLines = AppDefaults.timelineNumberOfLines + self.iconSize = AppDefaults.timelineIconSize self.resetEstimatedRowHeight() self.reloadAllVisibleCells() } diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 4535796fe..34b1c37a0 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -81,7 +81,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner { var isTimelineViewControllerPending = false var isArticleViewControllerPending = false - private(set) var sortDirection = AppDefaults.shared.timelineSortDirection { + private(set) var sortDirection = AppDefaults.timelineSortDirection { didSet { if sortDirection != oldValue { sortParametersDidChange() @@ -89,7 +89,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner { } } - private(set) var groupByFeed = AppDefaults.shared.timelineGroupByFeed { + private(set) var groupByFeed = AppDefaults.timelineGroupByFeed { didSet { if groupByFeed != oldValue { sortParametersDidChange() @@ -491,8 +491,8 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner { } @objc func userDefaultsDidChange(_ note: Notification) { - self.sortDirection = AppDefaults.shared.timelineSortDirection - self.groupByFeed = AppDefaults.shared.timelineGroupByFeed + self.sortDirection = AppDefaults.timelineSortDirection + self.groupByFeed = AppDefaults.timelineGroupByFeed } @objc func accountDidDownloadArticles(_ note: Notification) { @@ -548,7 +548,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner { if isReadFeedsFiltered { rebuildBackingStores() } - if isReadArticlesFiltered && (AppDefaults.shared.refreshClearsReadArticles || !conditional) { + if isReadArticlesFiltered && (AppDefaults.refreshClearsReadArticles || !conditional) { refreshTimeline(resetScroll: false) } } diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index 1bc9ceedc..61862269f 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -30,7 +30,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate { Task { @MainActor in // Ensure Feeds view shows on first run on iPad — otherwise the UI is empty. - if UIDevice.current.userInterfaceIdiom == .pad && AppDefaults.shared.isFirstRun { + if UIDevice.current.userInterfaceIdiom == .pad && AppDefaults.isFirstRun { rootViewController.show(.primary) } } diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index 5126822d0..b355c3df8 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -131,7 +131,7 @@ final class AddAccountViewController: UITableViewController, AddAccountDismissDe case AddAccountSections.icloud.rawValue: cell.comboNameLabel?.text = AddAccountSections.icloud.sectionContent[indexPath.row].localizedAccountName() cell.comboImage?.image = AppAssets.image(for: AddAccountSections.icloud.sectionContent[indexPath.row]) - if AppDefaults.shared.isDeveloperBuild || AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) { + if AppDefaults.isDeveloperBuild || AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) { cell.isUserInteractionEnabled = false cell.comboNameLabel?.isEnabled = false } @@ -139,7 +139,7 @@ final class AddAccountViewController: UITableViewController, AddAccountDismissDe cell.comboNameLabel?.text = AddAccountSections.web.sectionContent[indexPath.row].localizedAccountName() cell.comboImage?.image = AppAssets.image(for: AddAccountSections.web.sectionContent[indexPath.row]) let type = AddAccountSections.web.sectionContent[indexPath.row] - if (type == .feedly || type == .inoreader) && AppDefaults.shared.isDeveloperBuild { + if (type == .feedly || type == .inoreader) && AppDefaults.isDeveloperBuild { cell.isUserInteractionEnabled = false cell.comboNameLabel?.isEnabled = false } diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index dcdf8e6ca..ecb3d22f8 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -49,19 +49,19 @@ final class SettingsViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if AppDefaults.shared.timelineSortDirection == .orderedAscending { + if AppDefaults.timelineSortDirection == .orderedAscending { timelineSortOrderSwitch.isOn = true } else { timelineSortOrderSwitch.isOn = false } - if AppDefaults.shared.timelineGroupByFeed { + if AppDefaults.timelineGroupByFeed { groupByFeedSwitch.isOn = true } else { groupByFeedSwitch.isOn = false } - if AppDefaults.shared.refreshClearsReadArticles { + if AppDefaults.refreshClearsReadArticles { refreshClearsReadArticlesSwitch.isOn = true } else { refreshClearsReadArticlesSwitch.isOn = false @@ -69,13 +69,13 @@ final class SettingsViewController: UITableViewController { articleThemeDetailLabel.text = ArticleThemesManager.shared.currentTheme.name - if AppDefaults.shared.confirmMarkAllAsRead { + if AppDefaults.confirmMarkAllAsRead { confirmMarkAllAsReadSwitch.isOn = true } else { confirmMarkAllAsReadSwitch.isOn = false } - if AppDefaults.shared.articleFullscreenAvailable { + if AppDefaults.articleFullscreenAvailable { showFullscreenArticlesSwitch.isOn = true } else { showFullscreenArticlesSwitch.isOn = false @@ -83,7 +83,7 @@ final class SettingsViewController: UITableViewController { colorPaletteDetailLabel.text = String(describing: AppDefaults.userInterfaceColorPalette) - openLinksInNetNewsWire.isOn = !AppDefaults.shared.useSystemBrowser + openLinksInNetNewsWire.isOn = !AppDefaults.useSystemBrowser let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 32.0, y: 0.0, width: 0.0, height: 0.0)) buildLabel.font = UIFont.systemFont(ofSize: 11.0) @@ -261,49 +261,49 @@ final class SettingsViewController: UITableViewController { @IBAction func switchTimelineOrder(_ sender: Any) { if timelineSortOrderSwitch.isOn { - AppDefaults.shared.timelineSortDirection = .orderedAscending + AppDefaults.timelineSortDirection = .orderedAscending } else { - AppDefaults.shared.timelineSortDirection = .orderedDescending + AppDefaults.timelineSortDirection = .orderedDescending } } @IBAction func switchGroupByFeed(_ sender: Any) { if groupByFeedSwitch.isOn { - AppDefaults.shared.timelineGroupByFeed = true + AppDefaults.timelineGroupByFeed = true } else { - AppDefaults.shared.timelineGroupByFeed = false + AppDefaults.timelineGroupByFeed = false } } @IBAction func switchClearsReadArticles(_ sender: Any) { if refreshClearsReadArticlesSwitch.isOn { - AppDefaults.shared.refreshClearsReadArticles = true + AppDefaults.refreshClearsReadArticles = true } else { - AppDefaults.shared.refreshClearsReadArticles = false + AppDefaults.refreshClearsReadArticles = false } } @IBAction func switchConfirmMarkAllAsRead(_ sender: Any) { if confirmMarkAllAsReadSwitch.isOn { - AppDefaults.shared.confirmMarkAllAsRead = true + AppDefaults.confirmMarkAllAsRead = true } else { - AppDefaults.shared.confirmMarkAllAsRead = false + AppDefaults.confirmMarkAllAsRead = false } } @IBAction func switchFullscreenArticles(_ sender: Any) { if showFullscreenArticlesSwitch.isOn { - AppDefaults.shared.articleFullscreenAvailable = true + AppDefaults.articleFullscreenAvailable = true } else { - AppDefaults.shared.articleFullscreenAvailable = false + AppDefaults.articleFullscreenAvailable = false } } @IBAction func switchBrowserPreference(_ sender: Any) { if openLinksInNetNewsWire.isOn { - AppDefaults.shared.useSystemBrowser = false + AppDefaults.useSystemBrowser = false } else { - AppDefaults.shared.useSystemBrowser = true + AppDefaults.useSystemBrowser = true } } diff --git a/iOS/Settings/TimelineCustomizerViewController.swift b/iOS/Settings/TimelineCustomizerViewController.swift index 34b848acb..6fc607a9e 100644 --- a/iOS/Settings/TimelineCustomizerViewController.swift +++ b/iOS/Settings/TimelineCustomizerViewController.swift @@ -27,11 +27,11 @@ final class TimelineCustomizerViewController: UIViewController { super.viewDidLoad() iconSizeSliderContainerView.layer.cornerRadius = 10 - iconSizeSlider.value = Float(AppDefaults.shared.timelineIconSize.rawValue) + iconSizeSlider.value = Float(AppDefaults.timelineIconSize.rawValue) iconSizeSlider.addTickMarks() numberOfLinesSliderContainerView.layer.cornerRadius = 10 - numberOfLinesSlider.value = Float(AppDefaults.shared.timelineNumberOfLines) + numberOfLinesSlider.value = Float(AppDefaults.timelineNumberOfLines) numberOfLinesSlider.addTickMarks() } @@ -48,12 +48,12 @@ final class TimelineCustomizerViewController: UIViewController { @IBAction func iconSizeChanged(_ sender: Any) { guard let iconSize = IconSize(rawValue: Int(iconSizeSlider.value.rounded())) else { return } - AppDefaults.shared.timelineIconSize = iconSize + AppDefaults.timelineIconSize = iconSize updatePreview() } @IBAction func numberOfLinesChanged(_ sender: Any) { - AppDefaults.shared.timelineNumberOfLines = Int(numberOfLinesSlider.value.rounded()) + AppDefaults.timelineNumberOfLines = Int(numberOfLinesSlider.value.rounded()) updatePreview() } diff --git a/iOS/Settings/TimelinePreviewTableViewController.swift b/iOS/Settings/TimelinePreviewTableViewController.swift index af66a6b1e..fbfdbfe3c 100644 --- a/iOS/Settings/TimelinePreviewTableViewController.swift +++ b/iOS/Settings/TimelinePreviewTableViewController.swift @@ -92,8 +92,8 @@ private extension TimelinePreviewTableViewController { feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, - numberOfLines: AppDefaults.shared.timelineNumberOfLines, - iconSize: AppDefaults.shared.timelineIconSize + numberOfLines: AppDefaults.timelineNumberOfLines, + iconSize: AppDefaults.timelineIconSize ) } }