Я настраиваю свой класс настроек, который получает / устанавливает значения из UserDefaults. Я хочу, чтобы как можно больше кода был универсальным, чтобы свести к минимуму усилия, требуемые всякий раз, когда вводится новый параметр (у меня их уже много, и я ожидаю, что в будущем будет гораздо больше), тем самым уменьшая вероятность любых человеческих ошибок / ошибок .
Я наткнулся на этот ответ, чтобы создать оболочку для UserDefaults:
struct UserDefaultsManager {
static var userDefaults: UserDefaults = .standard
static func set<T>(_ value: T, forKey: String) where T: Encodable {
if let encoded = try? JSONEncoder().encode(value) {
userDefaults.set(encoded, forKey: forKey)
}
}
static func get<T>(forKey: String) -> T? where T: Decodable {
guard let data = userDefaults.value(forKey: forKey) as? Data,
let decodedData = try? JSONDecoder().decode(T.self, from: data)
else { return nil }
return decodedData
}
}
Я создал перечисление для хранения всех ключей настроек:
enum SettingKeys: String {
case TimeFormat = "TimeFormat"
// and many more
}
И у каждой настройки есть свое перечисление:
enum TimeFormat: String, Codable {
case ampm = "12"
case mili = "24"
}
В этом упрощенном примере класса Settings вы можете видеть, что при его создании я инициализирую значение каждого определенного параметра. Я проверяю, был ли найден его ключ настройки в UserDefaults: если да, я использую найденное значение, в противном случае я устанавливаю его по умолчанию и впервые сохраняю его в UserDefaults.
class Settings {
var timeFormat: TimeFormat!
init() {
self.initTimeFormat()
}
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
func setTimeFormat(to format: TimeFormat) {
UserDefaultsManager.set(format, forKey: SettingKeys.TimeFormat.rawValue)
self.timeFormat = format
}
}
Это работает нормально и довольно просто. Однако, забегая вперед, это будет утомительно (и, следовательно, подвержено ошибкам) для репликации для каждого параметра в этом приложении (и для всех остальных, которые я собираюсь сделать в будущем). Есть ли способ обобщения init<name of setting>()
и set<name of setting>()
, при котором я передаю ему ключ для настройки (и ничего более), и он обрабатывает все остальное?
Я определил каждую настройку, чтобы иметь следующие общие элементы:
- ключ настроек (например, SettingsKey.TimeFormat в моем примере)
- уникальный тип (может быть AnyObject, String, Int, Bool и т. д., например, enum TimeFormat в моем примере)
- уникальное свойство (например, timeFormat в моем примере)
- значение по умолчанию (например, TimeFormat.ampm в моем примере)
Спасибо как всегда!
ОБНОВЛЕНИЕ:
Это может иметь или не иметь значение, но, учитывая, что все настройки будут иметь значение по умолчанию, я понял, что они не обязательно должны быть необязательными, но могут иметь значение по умолчанию, установленное при инициализации.
То есть изменить:
var timeFormat: TimeFormat!
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
To:
var timeFormat: TimeFormat = .ampm
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
UserDefaultsManager.set(self.timeFormat, forKey: SettingKeys.TimeFormat.rawValue)
return
}
self.timeFormat = format
}
Функция setTimeFormat()
по-прежнему необходима, когда ее значение изменяется пользователем в приложении.