Добавление поддержки Codable в UserDefaults с помощью Swift
Если вы разработчик iOS, скорее всего, вы уже использовали UserDefaults в прошлом для сохранения и загрузки локальных данных.
В настоящее время UserDefaults поддерживает следующие типы данных:
- URL - Any (NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary) - Bool - Double - Float - Int - String
Хорошо, но как насчет Codable? 🤔
Допустим, у нас есть структура Starship, соответствующая Codable.
struct Starship: Codable { let name: String let captain: String } let enterprise = Starship(name: "Enterprise D", captain: "Jean Luc Picard")
Как мы можем сохранить константу enterprise в UserDefaults?
На данный момент единственным способом является преобразование корпоративных данных в данные перед их сохранением. А позже, при получении значения, нам нужно преобразовать Data в Starship.
Довольно скучно, верно? 😴
Разве не было бы полезно иметь пару методов для сохранения и загрузки кодируемого значения?
Улучшение пользовательских значений по умолчанию
Хорошо, давайте добавим в наш проект следующее расширение UserDefault.
extension UserDefaults { func set<Element: Codable>(value: Element, forKey key: String) { let data = try? JSONEncoder().encode(value) UserDefaults.standard.setValue(data, forKey: key) } func codable<Element: Codable>(forKey key: String) -> Element? { guard let data = UserDefaults.standard.data(forKey: key) else { return nil } let element = try? JSONDecoder().decode(Element.self, from: data) return element } }
- Первый метод получает кодируемое значение и сохраняет его в UserDefaults.
- Второй метод возвращает значение Codable, связанное с данным ключом.
Благодаря этим методам теперь мы можем использовать UserDefaults с любым кодируемым типом.
Тест 🔨
Наконец, давайте проведем тест.
struct Starship: Codable { let name: String let captain: String } let enterprise = Starship(name: "Enterprise D", captain: "Jean Luc Picard") UserDefaults.standard.set(value: enterprise, forKey: "enterprise") let value: Starship? = UserDefaults.standard.codable(forKey: "enterprise")
Теперь мы можем напечатать значениеконстанту.
Optional(Starship(name: "Enterprise D", captain: "Jean Luc Picard"))
Зачем нам нужно объявлять тип значения, которое мы получаем? 🤔
Хороший вопрос, зачем нам это писать
let value: Starship? = UserDefaults.standard.codable(forKey: "enterprise")
вместо этого?
let value = UserDefaults.standard.codable(forKey: "enterprise")
В отличие от других методов UserDefaults (например, UserDerfaults.standard.integer(forKey:)), наш метод codable(forKey:) не всегда возвращает один и тот же тип.
Смотри сюда 👇
struct Starship: Codable { let name: String let captain: String } struct SpaceStation: Codable { let name: String } let enterprise = Starship(name: "Enterprise D", captain: "Jean Luc Picard") let deepSpace9 = SpaceStation(name: "Deep Space Nine") UserDefaults.standard.set(value: enterprise, forKey: "enterprise") UserDefaults.standard.set(value: deepSpace9, forKey: "deepSpace9") let value0: Starship? = UserDefaults.standard.codable(forKey: "enterprise") let value1: SpaceStation? = UserDefaults.standard.codable(forKey: "deepSpace9")
Обратите внимание на последние две строки: наш метод возвращает Starship, а затем SpaceStation.
Вот почему нам нужно сообщить нашему методу тип, который мы ожидаем.