// // Settings.swift // App // // Created by James Magahern on 3/9/21. // import Foundation @propertyWrapper public struct SettingProperty { public var key: String public var defaultValue: T public init(wrappedValue: T, key: String) { self.key = key self.defaultValue = wrappedValue } public var wrappedValue: T { get { guard let rawValue = UserDefaults.standard.object(forKey: key) as? T.RawValue else { return defaultValue } return T(rawValue: rawValue) ?? defaultValue } set { UserDefaults.standard.setValue(newValue.rawValue, forKey: key) } } } // - These coercions into RawRepresentable are stupid. How do I write specializations for each type instead? extension Dictionary: RawRepresentable where Key == String, Value == String { public typealias RawValue = [String: String] public init?(rawValue: [String : String]) { self.init() self.merge(rawValue, uniquingKeysWith: { $1 }) } public var rawValue: [String : String] { return self } } extension String: RawRepresentable { public typealias RawValue = String public init?(rawValue: String) { self.init(rawValue) } public var rawValue: String { return self } } class Settings { static let shared = Settings() public enum SearchProviderSetting: String, CaseIterable { case google = "Google" case duckduckgo = "DuckDuckGo" case searxnor = "Searx.nor" case whoogle = "Whoogle.nor" func provider() -> SearchProvider { switch self { case .google: return SearchProvider.google case .duckduckgo: return SearchProvider.duckduckgo case .searxnor: return SearchProvider.searxnor case .whoogle: return SearchProvider.whoogle } } } @SettingProperty(key: "searchProvider") public var searchProvider: SearchProviderSetting = .searxnor @SettingProperty(key: "redirectRules") public var redirectRules: [String: String] = [:] func redirectRule(for url: URL) -> URL? { if var components = URLComponents(url: url, resolvingAgainstBaseURL: false), var host = url.host { // Remove "www." if necessary. host = host.replacingOccurrences(of: "www.", with: "") if let alternateHost = redirectRules[host] { components.host = alternateHost // Since redirected hosts are usually self-hosted. // If https is required, it will be upgraded anyway. components.scheme = "http" return components.url } } return nil } @SettingProperty(key: "userScript") public var userScript: String = "" @SettingProperty(key: "userStylesheet") public var userStylesheet: String = "" }