Обычно при работе с REST API разработчики стараются обеспечить безопасность кода, чтобы избежать непредсказуемого поведения своих приложений. Это особенно верно при работе с внешними API без документации.
По моему опыту работы с одним из таких API-интерфейсов, я смоделировал одно из свойств объекта JSON в виде перечисления (с двумя случаями).
Как оказалось, был третий неожиданный случай перечисления, который мне нужно было безопасно обработать. Этот неожиданный случай, если его не обработать должным образом, обычно приводит к сбою приложения.
Основная цель этой статьи - показать, как безопасно декодировать перечисления при работе с REST API. Таким образом, если возникнет четвертый неожиданный случай, код будет enum-proof 😎.
Предпосылка:
- Некоторый опыт работы с REST API
- Некоторое понимание протоколов быстрого кодирования и декодирования
Сценарий
Предположим, у вас есть список данных транзакции, возвращаемых REST API. Каждая транзакция имеет тип, который может быть дебетовым или кредитным.
Хотите прочитать эту историю позже? Сохраните в Журнале.
Чтобы декодировать этот объект json, нам нужно настроить то, каким будет наше ожидаемое представление в swift.
Примечательно, что протокол Codable используется в примере кода для включения кодирования данных. Однако озабоченность вызывает поведение транзакции типа , представленной ниже:
Все хорошо, правда? Неправильный…
Декодер выдает следующую ошибку: Cannot initialize TransactionType from invalid String value refund
Что пошло не так ?
Перечисление TransactionType определяется как тип String. Поскольку компилятор Swift обеспечивает автоматическое соответствие RawRepresentable, вы можете создать TransactionType с помощью init?(rawValue:)
initializer. Этот инициализатор необработанного значения не работает.
let refundType = Transaction(rawValue: "refund") //refundType = nil
Кроме того, можно ожидать, что тип транзакции по умолчанию будет иметь значение nil, поскольку строка возврата не может быть инициализирована, верно? Что ж, похоже, что это не относится к перечислениям.
Компилятор может делать за нас много внутренней работы, но здесь нам нужно немного помочь. Выявление этой неудачной ошибки инициализации - это начало, так что давайте поговорим о контейнерах.
Контейнеры
Вы можете рассматривать контейнеры (в терминах non-swift) как место, где компилятор Swift может развернуть объекты внешнего представления (например, JSON) из коробки (т.е. контейнера), чтобы получить эквивалентный быстрый тип (например, массивы, Int, String).
Этот процесс (т. Е. Доступ к контейнеру) называется декодированием. Кодирование рассматривается как обратный процесс.
Не углубляясь в контейнеры, вы можете прочитать красивую статью Федерико Занетелло о том, как работают контейнеры здесь .
Нам нужно реализовать наш собственный init(from decoder: Decoder)
, чтобы взять на себя процесс декодирования по умолчанию, предоставляемый компилятором. Таким образом, мы можем использовать методы, доступные декодеру, для доступа к значениям, которые мы ожидаем от контейнера. Вот как происходит магия.
Два из трех методов, которые нам понадобятся в этой задаче-сценарии, включают:
- Контейнеры с ключами
.container<Key>(keyedBy type: Key.Type)
и - Контейнеры с одним значением
.singleValueContainer()
Контейнеры с ключами
init(from decoder: Decoder)
в этом методе будет определен в структуре Транзакция. Используя метод контейнеров с ключами, мы можем по умолчанию для свойства type установить значение значение nil каждый раз, когда декодирование вызывает ошибку неудачной инициализации.
Таким образом, необязательный TransactionType ? , и затем к типам транзакций можно применить необязательное разворачивание / связывание.
Важно отметить, что мы можем включить TransactionType ? с регистром .none. Это связано с тем, что Optionals в Swift представляют собой перечисление двух случаев:
- .some (TransactionType)
- . нет
Контейнеры с одним значением
В качестве альтернативы мы могли бы указать третий случай (например, unknown ) и использовать метод контейнера с одним значением для доступа к строковому значению типа в JSON.
Мы можем по умолчанию использовать перечисление типа транзакции для случая неизвестно, когда инициализация необработанного значения с помощью декодированного строкового значения не выполняется. Это означает, что тип всегда будет иметь значение .some (TransactionType). Теперь компилятор может обрабатывать декодирование по умолчанию структуры Транзакция, и никаких ошибок не возникает.
Лично я предпочитаю этот подход, чтобы не иметь дело с опциями 😅.
Можно ли использовать эти методы для декодирования перечислений со связанными значениями?
Хм… мы увидим 🤔 но пока мы защищаем от перечисления.
Спасибо за прочтение!! 🙇🏽♂️
📝 Сохраните эту историю в Журнале.
👩💻 Просыпайтесь каждое воскресное утро и слушайте самые интересные истории из области технологий, ожидающие вас в вашем почтовом ящике. Прочтите информационный бюллетень« Примечательно в технологиях .