Apple анонсировала Xcode 9 вместе со Swift 4 во время выступления на прошлой неделе в Сан-Хосе на WWDC 2017. Одним из наиболее значительных изменений, внесенных в стандартную библиотеку Swift 4, является серия протоколов для кодирования, декодирования и Сериализация экземпляров типов, которые позволяют пользователям выполнять преобразование в JSON и обратно, а также на локальный диск и обратно. Эти протоколы, в первую очередь Codable, Encodable и Decodable, предназначены для того, чтобы быть собственным ответом Swift на несколько ограничений, с которыми сталкиваются разработчики при работе с сериализацией объектов в Swift 3 или ранее.

Эти протоколы были в дикой природе всего чуть больше недели, но у Apple есть отличная документация, доступная в Интернете. Я потратил некоторое время на эксперименты с этими API, чтобы лучше понять, как они работают, что возможно, а также некоторые преимущества и недостатки, с которыми я могу столкнуться при их использовании в производственной кодовой базе.

До сих пор одним из самых больших открытых вопросов в сообществе Swift было «какую стороннюю платформу вы используете для десериализации JSON? Или скатываешь свою? ». С тех пор, как я впервые начал писать Swift, я экспериментировал с несколькими сторонними фреймворками и с собственной. Разочарование, которое я всегда испытывал по этому поводу, заключалось в том, что каждый фреймворк привносил свой собственный уникальный подход, и все же казалось, что все они плохо обращаются с опциями, не имеют надежной обработки ошибок и используют неинтуитивно понятные пользовательские операторы. Протокол Codable Swift 4 теперь предлагает универсальный и рекомендуемый подход к этой проблеме, поэтому я экспериментировал с ним, основываясь на нескольких ключевых сценариях использования, которые у меня всегда были.

Десериализация

Предположим, у вас есть структура Product, и вы хотите десериализовать экземпляр из ответа JSON, полученного в сетевом запросе. Убедитесь, что Product и любые настраиваемые типы свойств соответствуют протоколу Codable (или просто Decodable, если вы не выполняете сериализацию обратно в JSON).

В точке, где вы хотите десериализовать объект Data в модель Product, инициализируйте объект JSONDecoder и вызовите decoder.decode(_: from:). Эта функция выдаст ошибку, если операция декодирования завершится неудачно, поэтому вы можете заключить ее в оператор do, try catch.

Вот и все. Предполагая, что все имена свойств в модели продукта точно соответствуют именам полей в десериализуемой структуре JSON, Swift позаботится обо всем, что необходимо для инициализации экземпляра вашей модели.

Сериализация

Сериализация Product обратно в JSON просто требует, чтобы модель и все ее типы настраиваемых свойств соответствовали протоколу Codable (или просто Encodable, если вы не выполняете десериализацию из JSON).

В точке, где вы хотите сериализовать Product экземпляр в Data для отправки в сетевом запросе или где-то еще, инициализируйте объект JSONEncoder и вызовите encode(_:). Эта функция также выдаст ошибку, если операция кодирования завершится неудачно, не стесняйтесь заключить ее в do, try, catch снова.

Вложенные типы

Десериализация модели Product, имеющей свойство, которое также является типом Codable, в Swift 4 становится очень простой. Все, что нужно, - это обеспечить соответствие каждого вложенного типа Codable (или просто _23 _ / _ 24_, где это необходимо). JSONDecoder будет обрабатывать декодирование вложенных свойств так же, как декодирует объект верхнего уровня, как и следовало ожидать.

Имена настраиваемых свойств

Конечно, работать с JSON никогда не бывает так просто. Как мобильные инженеры, мы, как правило, не можем управлять используемым нами сетевым API и хотим определить имена настраиваемых полей для свойств, которые мы декодируем из полезной нагрузки JSON.

Важно понимать, что по умолчанию Swift автоматически использует имена свойств, которые вы определили, в качестве имен полей для декодирования из JSON. Определение имен настраиваемых полей для свойств типа Codable так же просто, как определение перечисления для вашего объекта с именем CodingKeys, которое имеет rawValue типа String и соответствует протоколу CodingKey. Вы должны определить случай для каждого свойства в вашей модели. RawValues ​​для каждого случая затем используются в качестве имени поля JSON, которое будет декодировано из JSON.

Пользовательские ключевые пути

В том кратком количестве, что я экспериментировал с более сложными ключевыми путями и структурами JSON, кажется, что подход Swift к их обработке требует некоторого привыкания и предполагает возврат к большому количеству шаблонов.

Типичная структура, с которой вы можете столкнуться, - это свойства объекта, вложенные в объект верхнего уровня, например, в данном случае поле под названием «продукт». Важно понимать, что подход Swift к этим вложенным путям ключей основан на использовании концепции «контейнера». Каждый уровень объекта в структуре JSON считается Контейнером в иерархии, и Swift JSONDecoder будет декодировать на основе отдельных контейнеров.

Поскольку ваш тип Swift Codable имеет плоский список свойств, по умолчанию JSONDecoder будет пытаться декодировать ваши свойства только из контейнера верхнего уровня в полезной нагрузке JSON. В противном случае вам нужно будет написать собственную реализацию функции public init(from decoder:) throws и вручную разделить вложенные контейнеры, содержащие ваши свойства.

В приведенном выше примере определено отдельное перечисление CodingKey, содержащее имя поля для контейнерных продуктов верхнего уровня. В инициализаторе Decodable значения контейнера верхнего уровня десериализуются с использованием этого ключа, а затем каждое последующее вложенное значение во вложенном контейнере десериализуется одно за другим.

Понятно, что обработка в Swift сложных ключевых путей и структур JSON опирается на множество шаблонов и требует возврата к ручному декодированию каждого свойства, как мы привыкли. Но во многом этот недостаток побуждает разработчиков тесно сотрудничать с командами API для определения более простых структур JSON, где это возможно.

Обработка ошибок

Одним из самых больших недостатков многих сторонних фреймворков десериализации / сериализации JSON было отсутствие функций тщательной обработки ошибок. Существует множество различных причин, по которым объект может не пройти процесс десериализации, например, отсутствие поля JSON, неправильный тип или сбой пользовательского преобразования.

Без какой-либо реализации обработки ошибок может быть невероятно сложно отлаживать проблемы в производственных приложениях, когда что-то идет не так. Таким образом, для выяснения первопричины этих проблем необходимо установить консоль отладки, установить бесчисленное количество точек останова и пройти через асинхронный код, чтобы выяснить, какие поля не отправляются или отправляются неправильно.

Объекты JSONEncoder и JSONDecoder Swift вызывают ошибки при сбое декодирования / кодирования, и эти ошибки дают разработчикам четкую и конкретную обратную связь о том, что именно пошло не так.

Если необязательное свойство не найдено в полезной нагрузке JSON, JSONDecoder выдаст DecodingError по причине сбоя операции, например .dataCorrupted, .keyNotFound, .typeMismatch и .valueNotFound.

Эти случаи ошибок описаны далее в документации Apple DecodingError.

Пользовательские преобразования

Расшифровка поля определенного типа из полезной нагрузки JSON и выполнение преобразования этого типа в другой тип также является обычной операцией, выполняемой при взаимодействии с JSON.

Достичь этого с помощью протокола Swift Codable так же просто, как определить собственную реализацию инициализатора Decodable, как было показано ранее. Выполните декодирование своего поля и сделайте преобразование, прежде чем устанавливать его в качестве значения свойства в инициализаторе. В этом случае поле creationDate декодируется как String и сохраняется в константе. Затем он преобразуется в Date с помощью DateFormatter и устанавливается в свойстве creationDate по мере необходимости.

Заключение

У меня был очень короткий шанс поэкспериментировать и понять, как работает новый Codable протокол Swift 4. Я рассмотрел ряд типичных случаев использования, с которыми я регулярно сталкиваюсь ежедневно. В большинстве случаев API-интерфейсы программирования Swift 4 проще в использовании и гораздо более лаконичны, чем любые сторонние фреймворки, которые я использовал раньше. Я невероятно рад возможности использовать функции обработки ошибок, которые Apple предоставляет из коробки. Я полностью намерен расширить это и, надеюсь, создать систему, в которой ошибки декодирования JSON обнаруживаются в пользовательском интерфейсе в среде отладочной сборки, чтобы внешние группы API могли отлаживать проблемы со своими API-интерфейсами с помощью приложения без какого-либо вмешательства со стороны меня или кого-либо мобильный разработчик на проекте.

Я также с нетерпением жду возможности больше протестировать эти API в производственном проекте и услышать от более широкого сообщества Swift об их выводах из этих изменений и о том, как они влияют на приложения всех форм и размеров.

Спасибо за прочтение! Следуйте за мной в твиттере @phillfarrugia, чтобы получать больше новостей, писем и тирадов.

Изначально этот пост был опубликован здесь http://www.phillfarrugia.com/2017/06/13/encoding-and-decoding-json-with-swift-4/