Converts AppDefaults to singleton

This commit is contained in:
Stuart Breckenridge
2020-07-02 09:57:36 +08:00
parent 4e8e792471
commit c2149579c9
19 changed files with 336 additions and 707 deletions

View File

@@ -2,290 +2,352 @@
// AppDefaults.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/28/20.
// Created by Stuart Breckenridge on 1/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import SwiftUI
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")
}
}
}
struct AppDefaults {
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
final class AppDefaults: ObservableObject {
#if os(macOS)
static var shared: UserDefaults = UserDefaults.standard
static let store: UserDefaults = UserDefaults.standard
#endif
#if os(iOS)
static var shared: UserDefaults = {
static let store: UserDefaults = {
let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String
let suiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)"
return UserDefaults.init(suiteName: suiteName)!
}()
#endif
public static let shared = AppDefaults()
private init() {}
struct Key {
// Shared Defaults
static let refreshInterval = "refreshInterval"
static let hideDockUnreadCount = "JustinMillerHideDockUnreadCount"
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let activeExtensionPointIDs = "activeExtensionPointIDs"
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let timelineSortDirection = "timelineSortDirection"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
static let lastRefresh = "lastRefresh"
static let addWebFeedAccountID = "addWebFeedAccountID"
static let addWebFeedFolderName = "addWebFeedFolderName"
static let addFolderAccountID = "addFolderAccountID"
static let timelineSortDirection = "timelineSortDirection"
// iOS Defaults
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
// macOS Defaults
static let windowState = "windowState"
static let sidebarFontSize = "sidebarFontSize"
static let timelineFontSize = "timelineFontSize"
static let detailFontSize = "detailFontSize"
static let openInBrowserInBackground = "openInBrowserInBackground"
static let importOPMLAccountID = "importOPMLAccountID"
static let exportOPMLAccountID = "exportOPMLAccountID"
static let defaultBrowserID = "defaultBrowserID"
static let checkForUpdatesAutomatically = "checkForUpdatesAutomatically"
static let downloadTestBuilds = "downloadTestBuild"
static let sendCrashLogs = "sendCrashLogs"
// Hidden macOS Defaults
static let showDebugMenu = "ShowDebugMenu"
static let timelineShowsSeparators = "CorreiaSeparators"
static let showTitleOnMainWindow = "KafasisTitleMode"
#if !MAC_APP_STORE
static let webInspectorEnabled = "WebInspectorEnabled"
static let webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached"
#endif
}
static let isDeveloperBuild: Bool = {
private static let smallestFontSizeRawValue = FontSize.small.rawValue
private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue
// MARK: Development Builds
let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
static let isFirstRun: Bool = {
if let _ = AppDefaults.shared.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}()
static var refreshInterval: RefreshInterval {
get {
let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval)
return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour
}
// MARK: First Run Details
var firstRunDate: Date? {
set {
UserDefaults.standard.set(newValue.rawValue, forKey: Key.refreshInterval)
AppDefaults.store.setValue(newValue, forKey: Key.firstRunDate)
objectWillChange.send()
}
get {
AppDefaults.store.object(forKey: Key.firstRunDate) as? Date
}
}
static var hideDockUnreadCount: Bool {
return bool(for: Key.hideDockUnreadCount)
// MARK: Refresh Interval
@AppStorage(wrappedValue: 4, Key.refreshInterval, store: store) var interval: Int {
didSet {
objectWillChange.send()
}
}
static var userInterfaceColorPalette: UserInterfaceColorPalette {
var refreshInterval: RefreshInterval {
RefreshInterval(rawValue: interval) ?? RefreshInterval.everyHour
}
// MARK: Dock Badge
@AppStorage(wrappedValue: false, Key.hideDockUnreadCount, store: store) var hideDockUnreadCount {
didSet {
objectWillChange.send()
}
}
// MARK: Color Palette
var userInterfaceColorPalette: UserInterfaceColorPalette {
get {
if let result = UserInterfaceColorPalette(rawValue: int(for: Key.userInterfaceColorPalette)) {
return result
if let palette = UserInterfaceColorPalette(rawValue: AppDefaults.store.integer(forKey: Key.userInterfaceColorPalette)) {
return palette
}
return .automatic
}
set {
setInt(for: Key.userInterfaceColorPalette, newValue.rawValue)
}
}
static var addWebFeedAccountID: String? {
get {
return string(for: Key.addWebFeedAccountID)
}
set {
setString(for: Key.addWebFeedAccountID, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.userInterfaceColorPalette)
objectWillChange.send()
}
}
static var addWebFeedFolderName: String? {
get {
return string(for: Key.addWebFeedFolderName)
}
set {
setString(for: Key.addWebFeedFolderName, newValue)
}
}
// MARK: Feeds & Folders
@AppStorage(Key.addWebFeedAccountID, store: store) var addWebFeedAccountID: String?
static var addFolderAccountID: String? {
get {
return string(for: Key.addFolderAccountID)
}
set {
setString(for: Key.addFolderAccountID, newValue)
}
}
@AppStorage(Key.addWebFeedFolderName, store: store) var addWebFeedFolderName: String?
static var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
@AppStorage(Key.addFolderAccountID, store: store) var addFolderAccountID: String?
@AppStorage(wrappedValue: false, Key.confirmMarkAllAsRead, store: store) var confirmMarkAllAsRead: Bool
// MARK: Extension Points
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
get {
return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
return AppDefaults.store.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
}
set {
UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs)
objectWillChange.send()
}
}
static var lastImageCacheFlushDate: Date? {
get {
return date(for: Key.lastImageCacheFlushDate)
}
// MARK: Image Cache
var lastImageCacheFlushDate: Date? {
set {
setDate(for: Key.lastImageCacheFlushDate, newValue)
AppDefaults.store.setValue(newValue, forKey: Key.lastImageCacheFlushDate)
objectWillChange.send()
}
}
static var timelineGroupByFeed: Bool {
get {
return bool(for: Key.timelineGroupByFeed)
}
set {
setBool(for: Key.timelineGroupByFeed, newValue)
}
}
static var refreshClearsReadArticles: Bool {
get {
return bool(for: Key.refreshClearsReadArticles)
}
set {
setBool(for: Key.refreshClearsReadArticles, newValue)
}
}
static var timelineSortDirection: ComparisonResult {
get {
return sortDirection(for: Key.timelineSortDirection)
}
set {
setSortDirection(for: Key.timelineSortDirection, newValue)
}
}
static var articleFullscreenAvailable: Bool {
get {
return bool(for: Key.articleFullscreenAvailable)
}
set {
setBool(for: Key.articleFullscreenAvailable, newValue)
}
}
static var articleFullscreenEnabled: Bool {
get {
return bool(for: Key.articleFullscreenEnabled)
}
set {
setBool(for: Key.articleFullscreenEnabled, newValue)
}
}
static var confirmMarkAllAsRead: Bool {
get {
return bool(for: Key.confirmMarkAllAsRead)
}
set {
setBool(for: Key.confirmMarkAllAsRead, newValue)
AppDefaults.store.object(forKey: Key.lastImageCacheFlushDate) as? Date
}
}
static var lastRefresh: Date? {
// MARK: Timeline
@AppStorage(wrappedValue: false, Key.timelineGroupByFeed, store: store) var timelineGroupByFeed: Bool
@AppStorage(wrappedValue: 3, Key.timelineNumberOfLines, store: store) var timelineNumberOfLines: Int {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: 40.0, Key.timelineIconSize, store: store) var timelineIconSize: Double {
didSet {
objectWillChange.send()
}
}
/// Set to `true` to sort oldest to newest, `false` for newest to oldest. Default is `false`.
@AppStorage(wrappedValue: false, Key.timelineSortDirection, store: store) var timelineSortDirection: Bool
// MARK: Refresh
@AppStorage(wrappedValue: false, Key.refreshClearsReadArticles, store: store) var refreshClearsReadArticles: Bool
// MARK: Articles
@AppStorage(wrappedValue: false, Key.articleFullscreenAvailable, store: store) var articleFullscreenAvailable: Bool
// MARK: Refresh
var lastRefresh: Date? {
set {
AppDefaults.store.setValue(newValue, forKey: Key.lastRefresh)
objectWillChange.send()
}
get {
return date(for: Key.lastRefresh)
AppDefaults.store.object(forKey: Key.lastRefresh) as? Date
}
}
// MARK: Window State
var windowState: [AnyHashable : Any]? {
get {
return AppDefaults.store.object(forKey: Key.windowState) as? [AnyHashable : Any]
}
set {
setDate(for: Key.lastRefresh, newValue)
UserDefaults.standard.set(newValue, forKey: Key.windowState)
objectWillChange.send()
}
}
static var timelineNumberOfLines: Int {
@AppStorage(wrappedValue: false, Key.openInBrowserInBackground, store: store) var openInBrowserInBackground: Bool {
didSet {
objectWillChange.send()
}
}
var sidebarFontSize: FontSize {
get {
return int(for: Key.timelineNumberOfLines)
return fontSize(for: Key.sidebarFontSize)
}
set {
setInt(for: Key.timelineNumberOfLines, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.sidebarFontSize)
objectWillChange.send()
}
}
static var timelineIconSize: IconSize {
var timelineFontSize: FontSize {
get {
let rawValue = AppDefaults.shared.integer(forKey: Key.timelineIconSize)
return IconSize(rawValue: rawValue) ?? IconSize.medium
return fontSize(for: Key.timelineFontSize)
}
set {
AppDefaults.shared.set(newValue.rawValue, forKey: Key.timelineIconSize)
AppDefaults.store.set(newValue.rawValue, forKey: Key.timelineFontSize)
objectWillChange.send()
}
}
static func registerDefaults() {
let defaults: [String : Any] = [Key.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue,
Key.timelineGroupByFeed: false,
Key.refreshClearsReadArticles: false,
Key.timelineNumberOfLines: 2,
Key.timelineIconSize: IconSize.medium.rawValue,
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
Key.articleFullscreenAvailable: false,
Key.articleFullscreenEnabled: false,
Key.confirmMarkAllAsRead: true]
AppDefaults.shared.register(defaults: defaults)
}
}
private extension AppDefaults {
static var firstRunDate: Date? {
var detailFontSize: FontSize {
get {
return date(for: Key.firstRunDate)
return fontSize(for: Key.detailFontSize)
}
set {
setDate(for: Key.firstRunDate, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.detailFontSize)
objectWillChange.send()
}
}
static func string(for key: String) -> String? {
return AppDefaults.shared.string(forKey: key)
}
static func setString(for key: String, _ value: String?) {
AppDefaults.shared.set(value, forKey: key)
}
static func bool(for key: String) -> Bool {
return AppDefaults.shared.bool(forKey: key)
}
static func setBool(for key: String, _ flag: Bool) {
AppDefaults.shared.set(flag, forKey: key)
}
static func int(for key: String) -> Int {
return AppDefaults.shared.integer(forKey: key)
}
static func setInt(for key: String, _ x: Int) {
AppDefaults.shared.set(x, forKey: key)
}
static func date(for key: String) -> Date? {
return AppDefaults.shared.object(forKey: key) as? Date
}
static func setDate(for key: String, _ date: Date?) {
AppDefaults.shared.set(date, forKey: key)
}
static func sortDirection(for key:String) -> ComparisonResult {
let rawInt = int(for: key)
if rawInt == ComparisonResult.orderedAscending.rawValue {
return .orderedAscending
@AppStorage(Key.importOPMLAccountID, store: store) var importOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
return .orderedDescending
}
static func setSortDirection(for key: String, _ value: ComparisonResult) {
if value == .orderedAscending {
setInt(for: key, ComparisonResult.orderedAscending.rawValue)
@AppStorage(Key.exportOPMLAccountID, store: store) var exportOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
else {
setInt(for: key, ComparisonResult.orderedDescending.rawValue)
}
@AppStorage(Key.defaultBrowserID, store: store) var defaultBrowserID: String? {
didSet {
objectWillChange.send()
}
}
@AppStorage(Key.showTitleOnMainWindow, store: store) var showTitleOnMainWindow: Bool? {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.showDebugMenu, store: store) var showDebugMenu: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.timelineShowsSeparators, store: store) var timelineShowsSeparators: Bool {
didSet {
objectWillChange.send()
}
}
#if !MAC_APP_STORE
@AppStorage(wrappedValue: false, Key.webInspectorEnabled, store: store) var webInspectorEnabled: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.webInspectorStartsAttached, store: store) var webInspectorStartsAttached: Bool {
didSet {
objectWillChange.send()
}
}
#endif
@AppStorage(wrappedValue: true, Key.checkForUpdatesAutomatically, store: store) var checkForUpdatesAutomatically: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.downloadTestBuilds, store: store) var downloadTestBuilds: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: true, Key.sendCrashLogs, store: store) var sendCrashLogs: Bool {
didSet {
objectWillChange.send()
}
}
}
extension AppDefaults {
func isFirstRun() -> Bool {
if let _ = AppDefaults.store.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}
func fontSize(for key: String) -> FontSize {
// Punted till after 1.0.
return .medium
}
}

View File

@@ -1,321 +0,0 @@
//
// AppSettings.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 1/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import SwiftUI
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")
}
}
}
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
final class AppSettings: ObservableObject {
#if os(macOS)
static let store: UserDefaults = UserDefaults.standard
#endif
#if os(iOS)
static let store: UserDefaults = {
let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String
let suiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)"
return UserDefaults.init(suiteName: suiteName)!
}()
#endif
public static let shared = AppSettings()
private init() {}
struct Key {
// Shared Defaults
static let refreshInterval = "refreshInterval"
static let hideDockUnreadCount = "JustinMillerHideDockUnreadCount"
static let activeExtensionPointIDs = "activeExtensionPointIDs"
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
static let firstRunDate = "firstRunDate"
static let lastRefresh = "lastRefresh"
static let addWebFeedAccountID = "addWebFeedAccountID"
static let addWebFeedFolderName = "addWebFeedFolderName"
static let addFolderAccountID = "addFolderAccountID"
static let timelineSortDirection = "timelineSortDirection"
// iOS Defaults
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
// macOS Defaults
static let windowState = "windowState"
static let sidebarFontSize = "sidebarFontSize"
static let timelineFontSize = "timelineFontSize"
static let detailFontSize = "detailFontSize"
static let openInBrowserInBackground = "openInBrowserInBackground"
static let importOPMLAccountID = "importOPMLAccountID"
static let exportOPMLAccountID = "exportOPMLAccountID"
static let defaultBrowserID = "defaultBrowserID"
// Hidden macOS Defaults
static let showDebugMenu = "ShowDebugMenu"
static let timelineShowsSeparators = "CorreiaSeparators"
static let showTitleOnMainWindow = "KafasisTitleMode"
#if !MAC_APP_STORE
static let webInspectorEnabled = "WebInspectorEnabled"
static let webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached"
#endif
}
private static let smallestFontSizeRawValue = FontSize.small.rawValue
private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue
// MARK: Development Builds
let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
// MARK: First Run Details
var firstRunDate: Date? {
set {
AppSettings.store.setValue(newValue, forKey: Key.firstRunDate)
objectWillChange.send()
}
get {
AppSettings.store.object(forKey: Key.firstRunDate) as? Date
}
}
// MARK: Refresh Timings
@AppStorage(wrappedValue: RefreshInterval.everyHour, Key.refreshInterval, store: store) var refreshInterval: RefreshInterval
// MARK: Dock Badge
@AppStorage(wrappedValue: false, Key.hideDockUnreadCount, store: store) var hideDockUnreadCount
// MARK: Color Palette
var userInterfaceColorPalette: UserInterfaceColorPalette {
get {
if let palette = UserInterfaceColorPalette(rawValue: AppSettings.store.integer(forKey: Key.userInterfaceColorPalette)) {
return palette
}
return .automatic
}
set {
AppSettings.store.set(newValue.rawValue, forKey: Key.userInterfaceColorPalette)
objectWillChange.send()
}
}
// MARK: Feeds & Folders
@AppStorage(Key.addWebFeedAccountID, store: store) var addWebFeedAccountID: String?
@AppStorage(Key.addWebFeedFolderName, store: store) var addWebFeedFolderName: String?
@AppStorage(Key.addFolderAccountID, store: store) var addFolderAccountID: String?
@AppStorage(wrappedValue: false, Key.confirmMarkAllAsRead, store: store) var confirmMarkAllAsRead: Bool
// MARK: Extension Points
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
get {
return AppSettings.store.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
}
set {
UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs)
objectWillChange.send()
}
}
// MARK: Image Cache
var lastImageCacheFlushDate: Date? {
set {
AppSettings.store.setValue(newValue, forKey: Key.lastImageCacheFlushDate)
objectWillChange.send()
}
get {
AppSettings.store.object(forKey: Key.lastImageCacheFlushDate) as? Date
}
}
// MARK: Timeline
@AppStorage(wrappedValue: false, Key.timelineGroupByFeed, store: store) var timelineGroupByFeed: Bool
@AppStorage(wrappedValue: 3, Key.timelineNumberOfLines, store: store) var timelineNumberOfLines: Int {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: 40.0, Key.timelineIconSize, store: store) var timelineIconSize: Double {
didSet {
objectWillChange.send()
}
}
/// Set to `true` to sort oldest to newest, `false` for newest to oldest. Default is `false`.
@AppStorage(wrappedValue: false, Key.timelineSortDirection, store: store) var timelineSortDirection: Bool
// MARK: Refresh
@AppStorage(wrappedValue: false, Key.refreshClearsReadArticles, store: store) var refreshClearsReadArticles: Bool
// MARK: Articles
@AppStorage(wrappedValue: false, Key.articleFullscreenAvailable, store: store) var articleFullscreenAvailable: Bool
// MARK: Refresh
var lastRefresh: Date? {
set {
AppSettings.store.setValue(newValue, forKey: Key.lastRefresh)
objectWillChange.send()
}
get {
AppSettings.store.object(forKey: Key.lastRefresh) as? Date
}
}
// MARK: Window State
var windowState: [AnyHashable : Any]? {
get {
return AppSettings.store.object(forKey: Key.windowState) as? [AnyHashable : Any]
}
set {
UserDefaults.standard.set(newValue, forKey: Key.windowState)
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.openInBrowserInBackground, store: store) var openInBrowserInBackground: Bool {
didSet {
objectWillChange.send()
}
}
var sidebarFontSize: FontSize {
get {
return fontSize(for: Key.sidebarFontSize)
}
set {
AppSettings.store.set(newValue.rawValue, forKey: Key.sidebarFontSize)
objectWillChange.send()
}
}
var timelineFontSize: FontSize {
get {
return fontSize(for: Key.timelineFontSize)
}
set {
AppSettings.store.set(newValue.rawValue, forKey: Key.timelineFontSize)
objectWillChange.send()
}
}
var detailFontSize: FontSize {
get {
return fontSize(for: Key.detailFontSize)
}
set {
AppSettings.store.set(newValue.rawValue, forKey: Key.detailFontSize)
objectWillChange.send()
}
}
@AppStorage(Key.importOPMLAccountID, store: store) var importOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
}
@AppStorage(Key.exportOPMLAccountID, store: store) var exportOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
}
@AppStorage(Key.defaultBrowserID, store: store) var defaultBrowserID: String? {
didSet {
objectWillChange.send()
}
}
@AppStorage(Key.showTitleOnMainWindow, store: store) var showTitleOnMainWindow: Bool? {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.showDebugMenu, store: store) var showDebugMenu: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.timelineShowsSeparators, store: store) var timelineShowsSeparators: Bool {
didSet {
objectWillChange.send()
}
}
#if !MAC_APP_STORE
@AppStorage(wrappedValue: false, Key.webInspectorEnabled, store: store) var webInspectorEnabled: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.webInspectorStartsAttached, store: store) var webInspectorStartsAttached: Bool {
didSet {
objectWillChange.send()
}
}
#endif
}
extension AppSettings {
func isFirstRun() -> Bool {
if let _ = AppSettings.store.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}
func fontSize(for key: String) -> FontSize {
// Punted till after 1.0.
return .medium
}
}

View File

@@ -13,13 +13,13 @@ struct MainApp: App {
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate
let preferences = MacPreferences()
#endif
#if os(iOS)
@UIApplicationDelegateAdaptor(AppDelegate.self) private var delegate
#endif
@StateObject private var sceneModel = SceneModel()
@StateObject private var defaults = AppDefaults.shared
@SceneBuilder var body: some Scene {
#if os(macOS)
@@ -134,7 +134,7 @@ struct MainApp: App {
.padding()
.frame(width: 500)
.navigationTitle("Preferences")
.environmentObject(preferences)
.environmentObject(defaults)
}
.windowToolbarStyle(UnifiedWindowToolbarStyle())

View File

@@ -71,9 +71,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppDefaults.registerDefaults()
//AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
let isFirstRun = AppDefaults.shared.isFirstRun()
if isFirstRun {
os_log("Is first run.", log: log, type: .info)
}
@@ -139,7 +139,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
@objc func accountRefreshDidFinish(_ note: Notification) {
AppDefaults.lastRefresh = Date()
AppDefaults.shared.lastRefresh = Date()
}
// MARK: - API
@@ -163,7 +163,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// extensionFeedAddRequestFile.resume()
syncTimer?.update()
if let lastRefresh = AppDefaults.lastRefresh {
if let lastRefresh = AppDefaults.shared.lastRefresh {
if Date() > lastRefresh.addingTimeInterval(15 * 60) {
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
} else {

View File

@@ -57,7 +57,7 @@ struct SettingsView: View {
@Environment(\.presentationMode) var presentationMode
@StateObject private var viewModel = SettingsViewModel()
@StateObject private var settings = AppSettings.shared
@StateObject private var settings = AppDefaults.shared
var body: some View {
NavigationView {

View File

@@ -10,7 +10,7 @@ import SwiftUI
struct TimelineLayoutView: View {
@EnvironmentObject private var appSettings: AppSettings
@EnvironmentObject private var appSettings: AppDefaults
private let sampleTitle = "Lorem dolor sed viverra ipsum. Gravida rutrum quisque non tellus. Rutrum tellus pellentesque eu tincidunt tortor. Sed blandit libero volutpat sed cras ornare. Et netus et malesuada fames ac. Ultrices eros in cursus turpis massa tincidunt dui ut ornare. Lacus sed viverra tellus in. Sollicitudin ac orci phasellus egestas. Purus in mollis nunc sed. Sollicitudin ac orci phasellus egestas tellus rutrum tellus pellentesque. Interdum consectetur libero id faucibus nisl tincidunt eget."
@@ -33,7 +33,7 @@ struct TimelineLayoutView: View {
}
var iconSize: some View {
Slider(value: $appSettings.timelineIconSize, in: 20...60, minimumValueLabel: Text("Small"), maximumValueLabel: Text("Large"), label: {
Slider(value: $appSettings.timelineIconSize, in: 20...60, step: 10, minimumValueLabel: Text("Small"), maximumValueLabel: Text("Large"), label: {
Text(String(appSettings.timelineIconSize))
})
}

View File

@@ -133,8 +133,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
}
#endif
AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
//AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.shared.isFirstRun()
if isFirstRun {
os_log(.debug, log: log, "Is first run.")
}
@@ -245,7 +245,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
// MARK: - Dock Badge
@objc func updateDockBadge() {
let label = unreadCount > 0 && !AppDefaults.hideDockUnreadCount ? "\(unreadCount)" : ""
let label = unreadCount > 0 && !AppDefaults.shared.hideDockUnreadCount ? "\(unreadCount)" : ""
NSApplication.shared.dockTile.badgeLabel = label
}

View File

@@ -32,19 +32,19 @@ struct MacPreferenceViewModel {
struct MacPreferencesView: View {
@EnvironmentObject var preferences: MacPreferences
@EnvironmentObject var defaults: AppDefaults
@State private var viewModel = MacPreferenceViewModel()
var body: some View {
VStack {
if viewModel.currentPreferencePane == .general {
AnyView(GeneralPreferencesView())
AnyView(GeneralPreferencesView().environmentObject(defaults))
}
else if viewModel.currentPreferencePane == .accounts {
AnyView(AccountsPreferencesView())
AnyView(AccountsPreferencesView().environmentObject(defaults))
}
else {
AnyView(AdvancedPreferencesView(preferences: preferences))
AnyView(AdvancedPreferencesView().environmentObject(defaults))
}
}
.toolbar {

View File

@@ -1,96 +0,0 @@
//
// MacPreferences.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
/// The `MacPreferences` object stores all macOS specific user preferences.
class MacPreferences: ObservableObject {
private struct AppKeys {
static let refreshInterval = "refreshInterval"
static let openInBackground = "openInBrowserInBackground"
static let showUnreadCountInDock = "showUnreadCountInDock"
static let checkForUpdatesAutomatically = "checkForAppUpdates"
static let downloadTestBuilds = "downloadTestBuilds"
static let sendCrashLogs = "sendCrashLogs"
}
// Refresh Interval
public let refreshIntervals:[String] = RefreshFrequencies.allCases.map({ $0.description })
@AppStorage(wrappedValue: 0, AppKeys.refreshInterval) var refreshFrequency {
didSet {
objectWillChange.send()
}
}
// Open in background
@AppStorage(wrappedValue: false, AppKeys.openInBackground) var openInBackground {
didSet {
objectWillChange.send()
}
}
// Unread Count in Dock
@AppStorage(wrappedValue: true, AppKeys.showUnreadCountInDock) var showUnreadCountInDock {
didSet {
objectWillChange.send()
}
}
// Check for App Updates
@AppStorage(wrappedValue: true, AppKeys.checkForUpdatesAutomatically) var checkForUpdatesAutomatically {
didSet {
objectWillChange.send()
}
}
// Test builds
@AppStorage(wrappedValue: false, AppKeys.downloadTestBuilds) var downloadTestBuilds {
didSet {
objectWillChange.send()
}
}
// Crash Logs
@AppStorage(wrappedValue: false, AppKeys.sendCrashLogs) var sendCrashLogs {
didSet {
objectWillChange.send()
}
}
}
enum RefreshFrequencies: CaseIterable, CustomStringConvertible {
case refreshEvery10Mins, refreshEvery20Mins, refreshHourly, refreshEvery2Hours, refreshEvery4Hours, refreshEvery8Hours, none
var description: String {
switch self {
case .refreshEvery10Mins:
return "Every 10 minutes"
case .refreshEvery20Mins:
return "Every 20 minutes"
case .refreshHourly:
return "Every hour"
case .refreshEvery2Hours:
return "Every 2 hours"
case .refreshEvery4Hours:
return "Every 4 hours"
case .refreshEvery8Hours:
return "Every 8 hours"
case .none:
return "Manually"
}
}
}

View File

@@ -9,7 +9,7 @@ import SwiftUI
struct AdvancedPreferencesView: View {
@StateObject var preferences: MacPreferences
@EnvironmentObject private var preferences: AppDefaults
var body: some View {
VStack {

View File

@@ -0,0 +1,33 @@
//
// GeneralPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct GeneralPreferencesView: View {
@EnvironmentObject private var defaults: AppDefaults
var body: some View {
VStack {
Form {
Picker("Refresh Feeds",
selection: $defaults.interval,
content: {
ForEach(RefreshInterval.allCases, content: { interval in
Text(interval.description()).tag(interval.rawValue)
})
}).frame(width: 300, alignment: .center)
Toggle("Open webpages in background in browser", isOn: $defaults.openInBrowserInBackground)
Toggle("Hide Unread Count in Dock", isOn: $defaults.hideDockUnreadCount)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -1,33 +0,0 @@
//
// GeneralPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct GeneralPreferencesView: View {
@ObservedObject private var preferences = MacPreferences()
var body: some View {
VStack {
Form {
Picker("Refresh Feeds",
selection: $preferences.refreshFrequency,
content: {
ForEach(0..<preferences.refreshIntervals.count, content: {
Text(preferences.refreshIntervals[$0])
})
}).frame(width: 300, alignment: .center)
Toggle("Open webpages in background in browser", isOn: $preferences.openInBackground)
Toggle("Show Unread Count in Dock", isOn: $preferences.showUnreadCountInDock)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -11,15 +11,16 @@
172199ED24AB2E0100A31D04 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199EC24AB2E0100A31D04 /* SafariView.swift */; };
172199EF24AB372D00A31D04 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199EE24AB372D00A31D04 /* VisualEffectBlur.swift */; };
172199F124AB716900A31D04 /* SidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199F024AB716900A31D04 /* SidebarToolbar.swift */; };
1729528E24AA1A4900D65E66 /* MacPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729526C24AA1A4900D65E66 /* MacPreferences.swift */; };
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */; };
1729529424AA1CAA00D65E66 /* AdvancedPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */; };
1729529524AA1CAA00D65E66 /* GeneralPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */; };
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529624AA1CD000D65E66 /* MacPreferencesView.swift */; };
1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529A24AA1FD200D65E66 /* MacSearchField.swift */; };
172952B024AA287100D65E66 /* CompactSidebarContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172952AF24AA287100D65E66 /* CompactSidebarContainerView.swift */; };
1776E88E24AC5F8A00E78166 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppSettings.swift */; };
1776E88F24AC5F8A00E78166 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppSettings.swift */; };
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
17B223DC24AC24D2001E4592 /* TimelineLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */; };
@@ -489,8 +490,6 @@
51E4992424A8098400B667CB /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
51E4992624A80AAB00B667CB /* AppAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992524A80AAB00B667CB /* AppAssets.swift */; };
51E4992724A80AAB00B667CB /* AppAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992524A80AAB00B667CB /* AppAssets.swift */; };
51E4992924A866F000B667CB /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992824A866F000B667CB /* AppDefaults.swift */; };
51E4992A24A866F000B667CB /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992824A866F000B667CB /* AppDefaults.swift */; };
51E4992B24A8676300B667CB /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
51E4992C24A8676300B667CB /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
51E4992D24A8676300B667CB /* FetchRequestOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CAFCAE22BC8C35007694F0 /* FetchRequestOperation.swift */; };
@@ -511,9 +510,7 @@
51E4993E24A870F900B667CB /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; };
51E4994024A8713B00B667CB /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
51E4994124A8713B00B667CB /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; };
51E4994424A8713C00B667CB /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
51E4994524A872AD00B667CB /* org.sparkle-project.Downloader.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42BC235E71B40081F399 /* org.sparkle-project.Downloader.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
51E4994624A872AD00B667CB /* org.sparkle-project.InstallerConnection.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42B8235E71B40081F399 /* org.sparkle-project.InstallerConnection.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
51E4994724A872AD00B667CB /* org.sparkle-project.InstallerLauncher.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42B6235E71B40081F399 /* org.sparkle-project.InstallerLauncher.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -1697,14 +1694,13 @@
172199EC24AB2E0100A31D04 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
172199EE24AB372D00A31D04 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = "<group>"; };
172199F024AB716900A31D04 /* SidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbar.swift; sourceTree = "<group>"; };
1729526C24AA1A4900D65E66 /* MacPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacPreferences.swift; sourceTree = "<group>"; };
1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsPreferencesView.swift; sourceTree = "<group>"; };
1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPreferencesView.swift; sourceTree = "<group>"; };
1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesView.swift; sourceTree = "<group>"; };
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacPreferencesView.swift; sourceTree = "<group>"; };
1729529A24AA1FD200D65E66 /* MacSearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacSearchField.swift; sourceTree = "<group>"; };
172952AF24AA287100D65E66 /* CompactSidebarContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactSidebarContainerView.swift; sourceTree = "<group>"; };
1776E88D24AC5F8A00E78166 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = "<group>"; };
3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = "<group>"; };
@@ -1922,7 +1918,6 @@
51E4989824A8067000B667CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
51E498B224A806AA00B667CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
51E4992524A80AAB00B667CB /* AppAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; };
51E4992824A866F000B667CB /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
51E4993924A8708800B667CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
51E4993B24A8709900B667CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
51E4995824A873F900B667CB /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
@@ -2359,29 +2354,20 @@
1729528F24AA1A4F00D65E66 /* Preferences */ = {
isa = PBXGroup;
children = (
1729529824AA1CD900D65E66 /* Model */,
1729529924AA1CE100D65E66 /* Views */,
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */,
1729529924AA1CE100D65E66 /* View */,
);
path = Preferences;
sourceTree = "<group>";
};
1729529824AA1CD900D65E66 /* Model */ = {
1729529924AA1CE100D65E66 /* View */ = {
isa = PBXGroup;
children = (
1729526C24AA1A4900D65E66 /* MacPreferences.swift */,
);
path = Model;
sourceTree = "<group>";
};
1729529924AA1CE100D65E66 /* Views */ = {
isa = PBXGroup;
children = (
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */,
1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */,
1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */,
1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */,
);
path = Views;
path = View;
sourceTree = "<group>";
};
17B223B924AC24A8001E4592 /* Submenus */ = {
@@ -2703,8 +2689,7 @@
isa = PBXGroup;
children = (
51E4992524A80AAB00B667CB /* AppAssets.swift */,
51E4992824A866F000B667CB /* AppDefaults.swift */,
1776E88D24AC5F8A00E78166 /* AppSettings.swift */,
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */,
51E4995824A873F900B667CB /* ErrorHandler.swift */,
51C0513624A77DF700194D5E /* MainApp.swift */,
51E499D724A912C200B667CB /* SceneModel.swift */,
@@ -4732,7 +4717,6 @@
51E4995924A873F900B667CB /* ErrorHandler.swift in Sources */,
51392D1B24AC19A000BE0D35 /* SidebarExpandedContainers.swift in Sources */,
51E4992F24A8676400B667CB /* ArticleArray.swift in Sources */,
51E4994424A8713C00B667CB /* RefreshInterval.swift in Sources */,
51E498F824A8085D00B667CB /* UnreadFeed.swift in Sources */,
51E4996A24A8762D00B667CB /* ExtractedArticle.swift in Sources */,
51919FF124AB864A00541E64 /* TimelineModel.swift in Sources */,
@@ -4780,7 +4764,6 @@
51E4993124A8676400B667CB /* FetchRequestOperation.swift in Sources */,
51E4992624A80AAB00B667CB /* AppAssets.swift in Sources */,
51E4995624A8734D00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
51E4992924A866F000B667CB /* AppDefaults.swift in Sources */,
51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */,
51E4990024A808BB00B667CB /* FaviconGenerator.swift in Sources */,
51E4997124A8764C00B667CB /* ActivityType.swift in Sources */,
@@ -4800,6 +4783,7 @@
51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4991924A8090A00B667CB /* CacheCleaner.swift in Sources */,
51E498F724A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */,
51E4993524A867E800B667CB /* AppNotifications.swift in Sources */,
51C0515E24A77DF800194D5E /* MainApp.swift in Sources */,
51919FF724AB8B7700541E64 /* TimelineView.swift in Sources */,
@@ -4807,7 +4791,7 @@
51E4991524A808FF00B667CB /* ArticleStringFormatter.swift in Sources */,
51919FEE24AB85E400541E64 /* TimelineContainerView.swift in Sources */,
51E4995724A8734D00B667CB /* ExtensionPoint.swift in Sources */,
1776E88E24AC5F8A00E78166 /* AppSettings.swift in Sources */,
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
51E4991124A808DE00B667CB /* SmallIconProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4849,12 +4833,11 @@
51E49A0124A91FC100B667CB /* RegularSidebarContainerView.swift in Sources */,
51E4995B24A875D500B667CB /* ArticlePasteboardWriter.swift in Sources */,
51E4993424A867E700B667CB /* UserInfoKey.swift in Sources */,
1776E88F24AC5F8A00E78166 /* AppSettings.swift in Sources */,
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */,
51E4994C24A8734C00B667CB /* RedditFeedProvider-Extensions.swift in Sources */,
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */,
51919FAD24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4994124A8713B00B667CB /* RefreshInterval.swift in Sources */,
51E498C924A8085D00B667CB /* PseudoFeed.swift in Sources */,
51E498FC24A808BA00B667CB /* FaviconURLFinder.swift in Sources */,
51E4991C24A8092000B667CB /* NSAttributedString+NetNewsWire.swift in Sources */,
@@ -4866,6 +4849,7 @@
51E49A0424A91FF600B667CB /* SceneNavigationView.swift in Sources */,
51E498CC24A8085D00B667CB /* SearchFeedDelegate.swift in Sources */,
51E498C824A8085D00B667CB /* SmartFeedsController.swift in Sources */,
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */,
51E4992C24A8676300B667CB /* ArticleSorter.swift in Sources */,
51E4995024A8734C00B667CB /* ExtensionPoint.swift in Sources */,
51E4990E24A808CC00B667CB /* HTMLMetadataDownloader.swift in Sources */,
@@ -4877,7 +4861,6 @@
51E4995A24A873F900B667CB /* ErrorHandler.swift in Sources */,
51E4991F24A8094300B667CB /* RSImage-AppIcons.swift in Sources */,
51E4991224A808FB00B667CB /* AddWebFeedDefaultContainer.swift in Sources */,
1729528E24AA1A4900D65E66 /* MacPreferences.swift in Sources */,
51E4993E24A870F900B667CB /* UserNotificationManager.swift in Sources */,
51E4992E24A8676300B667CB /* FetchRequestQueue.swift in Sources */,
51E498CF24A8085D00B667CB /* SmartFeed.swift in Sources */,
@@ -4889,7 +4872,6 @@
51E498FE24A808BA00B667CB /* FaviconDownloader.swift in Sources */,
51919FA724AA64B000541E64 /* SidebarView.swift in Sources */,
51E498FD24A808BA00B667CB /* ColorHash.swift in Sources */,
51E4992A24A866F000B667CB /* AppDefaults.swift in Sources */,
51E4991824A8090A00B667CB /* CacheCleaner.swift in Sources */,
51E498CD24A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
51E4996124A875F400B667CB /* ArticleRenderer.swift in Sources */,

View File

@@ -38,7 +38,7 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
let activeExtensionPointTypes = activeExtensionPoints.keys.compactMap({ ObjectIdentifier($0.extensionPointType) })
var available = [ExtensionPoint.Type]()
for possibleExtensionPointType in possibleExtensionPointTypes {
if !(AppDefaults.isDeveloperBuild && possibleExtensionPointType.isDeveloperBuildRestricted) {
if !(AppDefaults.shared.isDeveloperBuild && possibleExtensionPointType.isDeveloperBuildRestricted) {
if possibleExtensionPointType.isSinglton {
if !activeExtensionPointTypes.contains(ObjectIdentifier(possibleExtensionPointType)) {
available.append(possibleExtensionPointType)
@@ -109,7 +109,7 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
private extension ExtensionPointManager {
func loadExtensionPoints() {
if let extensionPointUserInfos = AppDefaults.activeExtensionPointIDs {
if let extensionPointUserInfos = AppDefaults.shared.activeExtensionPointIDs {
for extensionPointUserInfo in extensionPointUserInfos {
if let extensionPointID = ExtensionPointIdentifer(userInfo: extensionPointUserInfo) {
activeExtensionPoints[extensionPointID] = extensionPoint(for: extensionPointID)
@@ -119,7 +119,7 @@ private extension ExtensionPointManager {
}
func saveExtensionPointIDs() {
AppDefaults.activeExtensionPointIDs = activeExtensionPoints.keys.map({ $0.userInfo })
AppDefaults.shared.activeExtensionPointIDs = activeExtensionPoints.keys.map({ $0.userInfo })
NotificationCenter.default.post(name: .ActiveExtensionPointsDidChange, object: nil, userInfo: nil)
}

View File

@@ -13,8 +13,8 @@ struct AddWebFeedDefaultContainer {
static var defaultContainer: Container? {
if let accountID = AppDefaults.addWebFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.addWebFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) {
if let accountID = AppDefaults.shared.addWebFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.shared.addWebFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) {
return folder
} else {
return substituteContainerIfNeeded(account: account)
@@ -28,11 +28,11 @@ struct AddWebFeedDefaultContainer {
}
static func saveDefaultContainer(_ container: Container) {
AppDefaults.addWebFeedAccountID = container.account?.accountID
AppDefaults.shared.addWebFeedAccountID = container.account?.accountID
if let folder = container as? Folder {
AppDefaults.addWebFeedFolderName = folder.nameForDisplay
AppDefaults.shared.addWebFeedFolderName = folder.nameForDisplay
} else {
AppDefaults.addWebFeedFolderName = nil
AppDefaults.shared.addWebFeedFolderName = nil
}
}

View File

@@ -15,8 +15,8 @@ struct CacheCleaner {
static func purgeIfNecessary() {
guard let flushDate = AppDefaults.lastImageCacheFlushDate else {
AppDefaults.lastImageCacheFlushDate = Date()
guard let flushDate = AppDefaults.shared.lastImageCacheFlushDate else {
AppDefaults.shared.lastImageCacheFlushDate = Date()
return
}
@@ -41,7 +41,7 @@ struct CacheCleaner {
}
}
AppDefaults.lastImageCacheFlushDate = Date()
AppDefaults.shared.lastImageCacheFlushDate = Date()
}
}

View File

@@ -20,7 +20,7 @@ class AccountRefreshTimer {
func fireOldTimer() {
if let timer = internalTimer {
if timer.fireDate < Date() {
if AppDefaults.refreshInterval != .manually {
if AppDefaults.shared.refreshInterval != .manually {
timedRefresh(nil)
}
}
@@ -42,7 +42,7 @@ class AccountRefreshTimer {
return
}
let refreshInterval = AppDefaults.refreshInterval
let refreshInterval = AppDefaults.shared.refreshInterval
if refreshInterval == .manually {
invalidate()
return

View File

@@ -8,7 +8,7 @@
import Foundation
enum RefreshInterval: Int, CaseIterable {
enum RefreshInterval: Int, CaseIterable, Identifiable {
case manually = 1
case every10Minutes = 2
case every30Minutes = 3
@@ -36,6 +36,8 @@ enum RefreshInterval: Int, CaseIterable {
}
}
var id: String { description() }
func description() -> String {
switch self {
case .manually: