Эффективно обрабатывать все виды ответов JSON в Swift
Как некоторые из вас, возможно, уже знают, Decodable
— это протокол, используемый для декодирования типов из внешних представлений (таких как JSON и списки свойств), который был выпущен в Swift 4.
Согласно Apple, Decodable
это:
Тип, который может декодировать себя из внешнего представления.
Итак, допустим, нам дан JSON вот так:
[ { "type": "car", "identifier": "12345", "model": "Audi" } ]
Мы можем определить тип Car
, который соответствует Decodable
, и этого должно быть достаточно для его декодирования с использованием экземпляра JSONDecoder
. Вы можете попробовать следующий код на игровой площадке Swift:
Теперь предположим, что серверная команда решает также включить Motorcycles
в тот же ответ:
[ { "type": "car", "identifier": "12345", "model": "Audi" }, { "type": "Motorcyle", "brand": "Yamaha" } ]
В идеальном мире у нас должно быть два разных ключа: один для Cars
и один для Motorcycle
. Но, несмотря на то, что это не так распространено (или, по крайней мере, не должно), иногда мы можем получить плохо оформленный ответ JSON.
Как вы понимаете, нашего типа Car
уже недостаточно для декодирования всего ответа JSON. Это хороший сценарий для использования наших мощных быстрых перечислений.
В этом случае мы будем использовать перечисление Either
, которое представляет собой перечисление, имеющее только два случая, таким образом, только один из них может использоваться одновременно. Об этой кастомной реализации написано много хороших ресурсов, поэтому я не буду вдаваться в подробности.
Обычная реализация перечисления Either
будет выглядеть так:
Чтобы Either
работал с Decodable
, нам нужно немного расширить его функциональные возможности. Если мы попытаемся напрямую декодировать ответ JSON, используя что-то вроде Either<Car, Motorcycle>
, компилятор выдаст ошибку, потому что Either
должен соответствовать протоколу Decodable
.
Учитывая, что перечисление не может содержать никаких сохраненных свойств, ключи кодирования не могут быть синтезированы для Either
. Из-за этого также потребуется реализовать инициализатор init(from: Decoder)
:
Теперь мы должны быть готовы использовать наше перечисление Either
для разбора ответа JSON:
И это должно быть так! При этом вы сможете получить Cars
или Motorcycles
из ответа JSON.
В этот момент вы можете спросить: что произойдет, если мы получим дополнительное транспортное средство? Что, если мы получим два дополнительных? Или, может быть, больше?
Как вы могли догадаться, Either
в этом сценарии работать не будет. Лучшим подходом было бы использование стирания типа для определения типа AnyDecodable
.
Я раскрою эту тему в следующей статье. Вы можете найти полный пример кода в этом репозитории GitHub: