За годы работы разработчиком iOS, я собрал множество инструментов и полезных штук, которые облегчают процесс разработки. В этой статье, я хочу поделиться одним из таких инструментов. Это будет не большая статья. Я покажу, как пользоваться этой утилитой, продемонстрирую её в действии. Надеюсь, что статья окажется полезной для вас.
Работа с UserDefaults может быть удобной и простой, в этом поможет обертка UserDefaultsWrapper
. Давайте разберемся, как она работает.
UserDefaultsWrapper
– это свойство-обертка (property wrapper
), которое облегчает сохранение и извлечение данных из UserDefaults
. Он особенно полезен, когда вы работаете с типами данных, такими как URL
, массивы, словари, примитивные типы (String
, Int
, Bool
и т.д.).
import Foundation
/// - Parameters:
/// - key: Data will be saved and extracted using this key
/// - defaultValue: This value will be substituted when there is no value
/// - userDefaults: NSUserDefaults is a hierarchical persistent interprocess (optionally distributed) key-value store, optimized for storing user settings.
///
/// Types an UserDefaultsWrapper can work with:
///```
/// URL?
/// [Any]?
/// [String : Any]?
/// String?
/// [String]?
/// Data?
/// Bool
/// Int
/// Float
/// Double
/// ```
///
/// - Warning:
/// If the type is not optional, default == nil will cause a crash.
///
/// Exemple:
/// ```
/// class User {
///
/// @UserDefaultsWrapper<String>(key: "FirstName", default: "John") var firstName
/// @UserDefaultsWrapper(key: "LastName", default: "John") var lastName: String
///
/// // OR
///
/// @UserDefaultsWrapper(key: "Age") var age: Int?
/// @UserDefaultsWrapper(key: "Old", default: 6) var old: Int?
/// }
///
/// let user = User()
/// print(user.firstName) // John
/// user.firstName = "Misha"
/// print(user.firstName) // Misha
/// $user.firstName.removeObject()
///
/// print(user.old) // optional(6)
/// user.old = 18
/// print(user.old) // optional(18)
/// user.old = nil
/// print(user.old) // optional(6)
///
/// print(user.age) // nil
/// user.age = 18
/// print(user.age) // optional(18)
/// user.age = nil
/// print(user.age) // nil
/// ```
@propertyWrapper
public struct UserDefaultsWrapper<T> {
private let key: String
private let defaultValue: T!
private let userDefaults: UserDefaults
public var wrappedValue: T {
get {
let anyValue = userDefaults.value(forKey: key)
let value: T = (anyValue as? T) ?? defaultValue
return value
}
set {
if let optional = newValue as? AnyOptional, optional.isNil {
userDefaults.removeObject(forKey: key)
if let defaultValue = defaultValue {
self.set(newValue: defaultValue)
}
} else {
self.set(newValue: newValue)
}
userDefaults.synchronize()
}
}
public var projectedValue: ActionUserDefault { self }
/// - Parameters:
/// - key: Data will be saved and extracted using this key
/// - defaultValue: This value will be substituted when there is no value
/// - userDefaults: NSUserDefaults is a hierarchical persistent interprocess (optionally distributed) key-value store, optimized for storing user settings.
///
/// - Warning:
/// If the type is not optional, default == nil will cause a crash.
public init(key: String, default defaultValue: T? = nil, userDefaults: UserDefaults = .standard) {
self.key = key
self.userDefaults = userDefaults
self.defaultValue = defaultValue
}
private func set(newValue: T) {
userDefaults.setValue(newValue, forKey: key)
}
}
// MARK: - ActionUserDefault
extension UserDefaultsWrapper: ActionUserDefault {
public func removeObject() {
userDefaults.removeObject(forKey: key)
if let defaultValue = defaultValue {
self.set(newValue: defaultValue)
}
}
}
// MARK: - Helper classes
private protocol AnyOptional {
var isNil: Bool { get }
}
extension Optional: AnyOptional {
var isNil: Bool { self == nil }
}
public protocol ActionUserDefault {
func removeObject()
}
Использование
Пример использования UserDefaultsWrapper
может выглядеть так:
class User {
@UserDefaultsWrapper<String>(key: "FirstName", default: "John") var firstName
@UserDefaultsWrapper(key: "LastName", default: "John") var lastName: String
@UserDefaultsWrapper(key: "Age") var age: Int?
@UserDefaultsWrapper(key: "Old", default: 6) var old: Int?
}
Здесь firstName
, lastName
, Age
и Old
– это свойства, которые автоматически сохраняют свои значения в UserDefaults
и извлекают их оттуда.
UserDefaultsWrapper
также включает в себя метод removeObject()
, который удаляет значение из UserDefaults
и, при наличии, возвращает значение по умолчанию.
$user.firstName.removeObject()
Преимущества и Применение
UserDefaultsWrapper
упрощает и делает более безопасным работу с UserDefaults
. Он уменьшает повторяющийся код и предотвращает возможные ошибки, связанные с неправильным ключом при сохранении и извлечении данных. Используя этот подход, вы можете улучшить структуру и читаемость вашего кода.