Working with Codable and JSON in Swift

Written by Reinder de Vries on January 20 2021 in App Development, iOS, Swift

Working with Codable and JSON in Swift

Vous pouvez utiliser Codable en Swift pour coder et décoder des formats de données personnalisés, tels que JSON, en objets Swift natifs. Il est incroyablement facile de mapper des objets Swift à des données JSON, et vice versa, en adoptant simplement le protocole Codable.

En tant que développeur iOS pragmatique, vous rencontrerez JSON plus tôt que tard. Chaque service web, de Facebook à Foursquare, utilise le format JSON pour faire entrer et sortir des données de votre application. Comment coder et décoder efficacement ces données JSON en objets Swift ?

Dans ce tutoriel, vous apprendrez à travailler avec des objets JSON dans Swift, en utilisant le protocole Codable. Ce que vous apprendrez, peut être facilement appliqué à d’autres formats de données également. Nous entrerons dans l’encodage et le décodage avec JSONEncoder et JSONDecoder, et je vous montrerai comment faire la médiation entre JSON et les données Swift.

Prêts ? C’est parti.

  1. Démarrez : Codage et décodage
  2. Le protocole Codable expliqué
  3. Travailler avec Codable
  4. Décoder JSON en objets Swift avec Codable
  5. Encoder des objets Swift en JSON avec Codable
  6. Travailler avec des tableaux imbriqués et Codable
  7. Lectures complémentaires

Démarrer : Codage et décodage

Quel problème le protocole Codable de Swift résout-il réellement ? Commençons par un exemple.

Imaginez que vous construisez une application de recettes. L’application affiche diverses recettes dans une liste, y compris les ingrédients, les instructions et les informations de base sur les aliments.

Vous obtenez les données pour l’application à partir d’un service Web, et de leur API basée sur le cloud. Cette API utilise le format de données JSON. Chaque fois que vous demandez une recette au webservice, vous recevez des données JSON en retour.

Voici un exemple de données JSON pour une recette :

{ "name": "Spaghetti Bolognese", "author": "Reinder's Cooking Corner", "url": "https://cookingcorner.com/spaghetti-bolognese", "yield": 4, "ingredients": , "instructions": "Cook spaghetti, fry beef and garlic, add tomatoes, add love, eat!"}

Regardez la structure des données JSON.

Les objets JSON sont enveloppés dans des crochets ondulés { et }, et les tableaux sont enveloppés dans des crochets . Les noms de propriétés sont des chaînes de caractères, enveloppées dans des guillemets ". Les valeurs dans JSON peuvent être des chaînes de caractères, des nombres (sans guillemets), des tableaux ou d’autres objets. Vous pouvez également imbriquer des données, c’est-à-dire des tableaux dans des tableaux, des objets dans des tableaux, etc. pour créer une hiérarchie complexe de données.

JSON est un format de données textuelles que de nombreux services Web utilisent, notamment les API de Twitter, Facebook, Foursquare, etc. Si vous construisez des applications qui utilisent des ressources Web, vous rencontrerez JSON.

Le format JSON est supérieur à XML, une alternative courante, car il est efficace, facilement analysé et lisible par les humains. JSON est un format convenu pour les services Web, les API et les applications. Il est utilisé partout sur le web, dans les apps et les services en ligne, parce que le format est simple et flexible.

Et JSON a une superbe capacité : vous pouvez coder n’importe quel format de données en JSON, et décoder JSON en retour vers n’importe quel format de données. Ce processus d’encodage et de décodage est ce qui rend JSON si puissant.

Vous pouvez prendre vos valeurs Swift String, Int, Double, URL, Date, Data, Array et Dictionary, et les encoder en JSON. Vous les envoyez ensuite au webservice, qui décode les valeurs dans un format natif qu’il comprend. De même, le webservice envoie des données codées en JSON à votre application, et vous décodez les données en types natifs tels que String, Double et Array.

Lorsque votre application de recette reçoit le JSON (voir ci-dessus), il peut alors être décodé en une structure Swift, comme celle-ci :

struct Recipe { var name: String var author: String var url: URL var yield: Int var ingredients: var instructions: String}

En Swift, le protocole Codable est utilisé pour passer d’un objet de données JSON à une classe ou une structure Swift réelle. C’est ce qu’on appelle le décodage, car les données JSON sont décodées dans un format que Swift comprend. Cela fonctionne également dans l’autre sens : encoder des objets Swift en tant que JSON.

Le protocole Codable dans Swift est en fait un alias des protocoles Decodable et Encodable. Comme vous utilisez souvent l’encodage et le décodage ensemble, vous utilisez le protocole Codable pour obtenir les deux protocoles en une seule fois.

La pièce maîtresse du flux de travail d’encodage/décodage est le protocole Codable de Swift. Découvrons comment il fonctionne, ensuite !

Vous n’arrivez pas à distinguer « encodage » et « décodage » ? Pensez-y de la manière suivante : Nous convertissons des données de et en « code », comme une machine Enigma ou un cryptogramme secret. Le codage consiste à convertir des données en code ; en-codage, ou « dans/avec le code ». Décoder signifie convertir le code en données ; décoder, ou « de/hors code ».

Se faire embaucher comme développeur iOS

Apprendre à construire des applications iOS 14 avec Swift 5

S’inscrire à mon cours de développement iOS, et apprendre à démarrer votre carrière en tant que développeur iOS professionnel.

Le protocole codable expliqué

Utiliser JSON avant Swift 4 était un peu un PITA. Vous deviez sérialiser le JSON vous-même avec JSONSerialization, et ensuite faire un type cast de chaque propriété du JSON vers le bon type Swift.

let json = try? JSONSerialization.jsonObject(with: data, options: )if let recipe = json as? { if let yield = recipe as? Int { recipeObject.yield = yield }}

Le snippet ci-dessus ne fait que saisir une valeur yield du JSON, et la faire passer en Int. C’est extrêmement verbeux, et il est difficile de répondre correctement aux erreurs potentielles et aux divergences de type. Même si cela fonctionne, ce n’est pas idéal.

Des bibliothèques comme SwiftyJSON rendent le travail avec JSON beaucoup plus facile, mais vous devez toujours mapper les données JSON à leurs objets et propriétés Swift respectifs.

Avec Swift 4, et plus tard, vous pouvez utiliser le protocole Codable à la place. Votre structure ou classe Swift doit simplement adopter le protocole Codable, et vous obtenez l’encodage et le décodage JSON dès la boîte, gratuitement.

Le protocole Codable est une composition de deux protocoles, Decodable et Encodable. Les deux protocoles sont assez minimaux ; ils ne définissent que les fonctions init(from: Decoder) et encode(to: Encoder), respectivement. En d’autres termes, ces fonctions signifient que pour qu’un type soit « décodable » ou « encodable », il devra « décoder de quelque chose » et « encoder vers quelque chose ».

La vraie magie de Codable se produit avec les protocoles Decoder et Encoder. Ces protocoles sont adoptés par les composants qui codent/décodent divers formats de données, comme JSON. Dans Swift, nous avons quelques -codeurs :

  • PropertyListEncoder et PropertyListDecoder pour les listes de propriétés .plist
  • JSONEncoder et JSONDecoder pour JSON – c’est nous !
  • NSKeyedArchiver peut fonctionner avec Codable, via PropertyListEncoder, Data et NSCoding

Les classes JSONDecoder et JSONEncoder utilisent ces les protocoles Decoder et Encoder pour fournir la fonctionnalité de décodage/encodage de JSON. Cela signifie également que vous pouvez écrire votre propre encodeur/décodeur personnalisé pour Codable, à condition d’adopter les protocoles Decoder et Encoder !

Vous avez besoin d’un rafraîchissement sur les protocoles en Swift ? Lisez Protocoles en Swift expliqués pour rattraper le retard.

Travailler avec Codable

Regardons un exemple. Nous allons mettre en correspondance certaines données JSON avec une structure Swift. Nous décodons essentiellement le JSON vers un objet Swift réel.

D’abord, nous créons une structure appelée User. Comme ceci :

struct User: Codable { var first_name: String var last_name: String var country: String}

La struct User a trois propriétés simples de type String, et se conforme au protocole Codable.

Puis, écrivons un peu de JSON. Voici le JSON avec lequel nous allons travailler:

{ "first_name": "John", "last_name": "Doe", "country": "United Kingdom"}

Les données JSON entrent généralement dans votre application comme la réponse d’une requête de webservice, ou par le biais d’un fichier .json, mais pour cet exemple, nous mettons simplement le JSON dans un String. Comme ceci:

let jsonString = """{ "first_name": "John", "last_name": "Doe", "country": "United Kingdom"}"""

Note : Le code ci-dessus utilise le triple guillemet """ pour créer des chaînes de caractères de plusieurs lignes. Neat!

Ce que nous faisons ensuite, c’est décoder le JSON et le transformer en un objet User. Comme ceci:

let jsonData = jsonString.data(using: .utf8)!let user = try! JSONDecoder().decode(User.self, from: jsonData)print(user.last_name)// Output: Doe

Voici ce qui se passe:

  • D’abord, vous transformez jsonString en un objet Data en appelant la fonction data(using:) sur la chaîne. C’est une étape intermédiaire nécessaire.
  • Puis, vous créez un objet JSONDecoder et appelez immédiatement la fonction decode(_:from:) sur celui-ci. Cela transforme le jsonData en un objet de type User, en décodant le JSON. Le type User.self est fourni en paramètre.
  • Enfin, vous imprimez le nom de famille de l’utilisateur avec print(user.last_name). Cette valeur user a User comme type, donc c’est un objet Swift réel !

Facile, non ? Vous avez essentiellement « mappé » l’objet JSON à un struct Swift, et décodé le format JSON à un objet natif avec lequel Swift peut travailler.

Dans le code ci-dessus, nous ignorons toutes les erreurs lancées par decode(_:from:) avec try!. Dans votre propre code, vous devrez gérer les erreurs avec un bloc do-try-catch. Une erreur qui peut être jetée, par exemple, vient du fait de fournir du JSON invalide.

Le User.self est un métatype, une référence au type User lui-même. Nous disons au décodeur que nous voulons décoder les données avec la structure User, en lui fournissant une référence à ce type.

Une erreur courante lors de la définition de votre type Codable, comme la structure User, est la non-concordance des clés et des propriétés. Si vous avez ajouté une propriété à votre struct, qui n’est pas présente dans le JSON ou qui a un type différent, vous obtiendrez une erreur lors du décodage du JSON. L’inverse, c’est-à-dire une propriété qui n’existe pas dans votre structure mais qui existe dans le JSON, ne pose pas de problème. Le plus simple est de déboguer une propriété à la fois, c’est-à-dire de continuer à ajouter des propriétés jusqu’à ce que le JSON ne se décode plus sans erreur. Vous pouvez également déterminer quelles propriétés/clés inclure ou ignorer avec l’enum CodingKeys (voir ci-dessous).

Décodage de JSON en objets Swift avec Codable

Regardons à nouveau l’exemple de la section précédente, et développons-le. Voici le JSON avec lequel nous travaillons:

let jsonString = """{ "first_name": "John", "last_name": "Doe", "country": "United Kingdom"}"""

Nous transformons ensuite cela en un objet Data. Comme ceci:

let jsonData = jsonString.data(using: .utf8)!

C’est une étape intermédiaire nécessaire. Au lieu de représenter le JSON comme une chaîne de caractères, nous stockons maintenant le JSON comme un objet Data natif. Le codage de caractères que nous utilisons pour cette chaîne est UTF8.

Si vous regardez de près, vous verrez que le code ci-dessus utilise le déballage forcé pour travailler avec la valeur de retour optionnelle de data(using:). Déballons cette option de manière plus élégante !

if let jsonData = jsonString.data(using: .utf8) { // Use `jsonData`} else { // Respond to error }

Avec le code ci-dessus, nous pouvons répondre aux erreurs lorsque data(using:) renvoie nil. Vous pourriez par exemple afficher un message d’erreur, ou laisser silencieusement la tâche échouer et enregistrer les informations de diagnostic dans un journal.

Puis, nous créons un nouvel objet JSONDecoder. Comme ceci:

let decoder = JSONDecoder()

Nous utilisons ensuite ce décodeur pour décoder les données JSON. Comme ceci:

let user = try! decoder.decode(User.self, from: jsonData)

Cependant, la fonction decode(_:from:) peut lancer des erreurs. Le code ci-dessus se plante à chaque fois que cela se produit. Encore une fois, nous voulons répondre à toutes les erreurs qui pourraient se produire. Comme ceci:

do { let user = try decoder.decode(User.self, from: jsonData) print(user.last_name)} catch { print(error.localizedDescription)}

Et le bloc de code entier ressemble maintenant à ce qui suit. Vous voyez comment c’est différent ?

if let jsonData = jsonString.data(using: .utf8){ let decoder = JSONDecoder() do { let user = try decoder.decode(User.self, from: jsonData) print(user.last_name) } catch { print(error.localizedDescription) }}

Ne jamais taire les erreurs. Attrapez l’erreur et répondez-y, soit avec l’UI/UX, en réessayant la tâche, ou en enregistrant, en plantant et en la réparant.

Travailler avec CodingKeys

Que faire si nos propriétés JSON, comme first_name et/ou firstName, sont différentes des propriétés de la structure Swift ? C’est là que CodingKeys entre en jeu.

Chaque classe ou structure conforme à Codable peut déclarer une énumération imbriquée spéciale appelée CodingKeys. Vous l’utilisez pour définir les propriétés qui doivent être codées et décodées, y compris leurs noms.

Regardons un exemple. Dans la User struct ci-dessous, nous avons changé les noms des propriétés de snake_case à camelCase. Par exemple, la clé first_name est maintenant appelée firstName.

struct User:Codable { var firstName: String var lastName: String var country: String enum CodingKeys: String, CodingKey { case firstName = "first_name" case lastName = "last_name" case country }}

Lorsque vous utilisez la struct ci-dessus avec les exemples des sections précédentes, vous verrez que vous pouvez utiliser un objet User avec les nouveaux noms de propriété. Comme ceci:

print(user.firstName)// Output: Johnprint(user.country)// Output: United Kingdom

L’énumération CodingKeys fait correspondre ses cas aux propriétés, et utilise les valeurs de chaîne pour sélectionner les bons noms de propriété dans les données JSON. Le cas dans l’énumération est le nom de la propriété que vous voulez utiliser dans la structure, et sa valeur est le nom de la clé dans les données JSON.

Codage des objets Swift en JSON avec Codable

Jusqu’à présent, nous nous sommes concentrés sur le décodage des données JSON en objets Swift. Qu’en est-il dans l’autre sens ? Peut-on également coder des objets en JSON ? Oui, pourquoi pas !

var user = User()user.firstName = "Bob"user.lastName = "and Alice"user.country = "Cryptoland"let jsonData = try! JSONEncoder().encode(user)let jsonString = String(data: jsonData, encoding: .utf8)!print(jsonString)

Et la sortie est :

{"country":"Cryptoland","first_name":"Bob","last_name":"and Alice"}

Que se passe-t-il ici ?

  • D’abord, vous créez un objet User et assignez quelques valeurs à ses propriétés
  • Puis, vous utilisez encode(_:) pour encoder l’objet user en un objet JSON Data
  • Enfin, vous convertissez l’objet Data en String et l’imprimez

Si vous regardez attentivement, vous verrez que l’encodage suit les mêmes étapes que le décodage, sauf en sens inverse. Aller de l’objet Swift, à travers le décodeur, résultant en une chaîne JSON.

Comme précédemment, pouvons-nous étendre l’exemple pour traiter les erreurs ? Oui ! Comme ceci :

let encoder = JSONEncoder()encoder.outputFormatting = .prettyPrinteddo { let jsonData = try encoder.encode(user) if let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) }} catch { print(error.localizedDescription)}

Dans l’exemple ci-dessus, nous utilisons également la propriété outputFormatting de l’encodeur pour  » joliment imprimer  » les données JSON. Cela ajoute des espaces, des tabulations et des nouvelles lignes pour rendre la chaîne JSON plus facile à lire. Comme ceci:

{ "country" : "Cryptoland", "first_name" : "Bob", "last_name" : "and Alice"}

Awesome!

Il faut noter que les dictionnaires dans Swift, et dans JSON, n’ont pas un ordre de tri fixe. C’est pourquoi vous verrez des ordres de tri variables pour les clés country, first_name, etc. dans le JSON, si vous exécutez le code ci-dessus plusieurs fois. La plupart des services Web qui produisent du JSON appliqueront un ordre de tri fixe par le biais d’un schéma, ce qui facilite définitivement la lecture et la navigation dans le JSON.

Travailler avec des tableaux imbriqués et codables

Jusqu’ici, nous avons travaillé avec des objets JSON simples – juste une classe avec quelques propriétés. Mais que faire si les données avec lesquelles vous travaillez sont plus complexes ?

Par exemple, les données JSON que vous pouvez obtenir en retour d’un webservice Twitter ou Facebook sont souvent imbriquées. C’est-à-dire que les données auxquelles vous voulez accéder sont enfouies dans 2 ou 3 niveaux de tableaux et de dictionnaires JSON. Et maintenant !

Regardons d’abord un exemple simple. Voici la structure Swift avec laquelle nous allons travailler :

struct User: Codable { var first_name: String var last_name: String}

Puis, voici les données JSON que nous voulons décoder :

let jsonString = """"""

Si vous regardez attentivement, vous voyez que les données dont nous avons besoin sont stockées sous forme de tableau. Le JSON n’est pas un objet simple, mais c’est un tableau avec 3 objets User. Nous savons qu’il s’agit d’un tableau d’objets, grâce aux crochets (tableau) et aux crochets ondulés { } (objet). Les éléments multiples sont toujours séparés par des virgules.

Comment pouvons-nous décoder ces User objets ? Voici comment :

let jsonData = jsonString.data(using: .utf8)!let users = try! JSONDecoder().decode(.self, from: jsonData)for user in users { print(user.first_name)}

Le bout de code ci-dessus est extrêmement similaire à ce que vous avez vu auparavant, mais il y a une différence importante. Vous voyez le type que nous fournissons à la fonction decode(_:from:) ? Le type est (avec .self), ou « array of User objects ». Au lieu de travailler avec un seul objet User, nous voulons décoder un tas d’entre eux, et cela est désigné avec le type tableau .

A la suite, nous allons discuter de la façon dont vous pouvez travailler avec des types imbriqués plus complexes. Considérons, par exemple, que le tableau d’objets User est imbriqué dans un dictionnaire JSON. Comme ceci:

let jsonString = """{ "users": }"""

Dans l’extrait JSON ci-dessus, l’élément de niveau supérieur est un dictionnaire (ou « objet »). Il ne possède qu’une seule paire clé-valeur : un tableau users. Les données que nous voulons sont à l’intérieur de ce tableau. Comment y accéder ?

Il est important que notre approche ne soit pas trop compliquée. Il est facile de penser que vous allez avoir besoin d’une analyse JSON avancée, mais il s’avère que nous pouvons en fait également imbriquer des structs Swift. Nous allons décrire l’ensemble du JSON comme une structure, avec la structure User à l’intérieur.

Voyez ça :

struct Response: Codable{ struct User: Codable { var first_name: String var last_name: String } var users: }

Voici ce qui se passe :

  • Le dictionnaire JSON de premier niveau correspond au type Response. Comme précédemment, ce type est conforme à Codable, pour supporter l’encodage et le décodage JSON.
  • Dans cette structure, nous avons défini une autre structure appelée User. C’est exactement le même type que nous avons utilisé auparavant. Cette structure est « imbriquée ».
  • La structure Response a une propriété appelée users de type , ou tableau d’objets User.

Ce qui est cool, c’est que sémantiquement, les deux structures de données JSON et Swift sont exactement les mêmes. Elles utilisent juste une syntaxe différente. Nous avons défini un tableau imbriqué à l’intérieur du dictionnaire de niveau supérieur, tout comme la structure Response a une structure imbriquée User et une propriété users à l’intérieur.

Le faire fonctionner est un morceau de gâteau maintenant. Regardez ça :

let jsonData = jsonString.data(using: .utf8)!let response = try! JSONDecoder().decode(Response.self, from: jsonData)for user in response.users { print(user.first_name)}

Vous voyez comment nous utilisons le type Response pour décoder le JSON, et ensuite boucler sur la propriété response.users ? Vérifiez cela avec la structure Response, et le JSON. Nous prenons la paire clé-valeur users dans le dictionnaire de niveau supérieur, et nous mappons les objets à l’intérieur en objets User. Neat!

L’utilisation de types imbriqués est une grande approche polyvalente pour les structures de données JSON complexes. Lorsque vous rencontrez un peu de JSON que vous ne pouvez pas facilement représenter dans un type tangible, comme User ou Tweet, essayez d’étendre le type à quelque chose comme Response ou UserCollection. Au lieu d’aller plus loin, allez plus loin et intégrez le JSON complet. Utilisez des types imbriqués pour atteindre les données que vous voulez. Il est également utile de noter ici que vous n’avez pas besoin d’ajouter la structure User dans la structure Response – elle peut aller à l’extérieur de celle-ci, aussi.

Se faire embaucher comme développeur iOS

Apprendre à construire des applications iOS 14 avec Swift 5

S’inscrire à mon cours de développement iOS, et apprendre à démarrer votre carrière en tant que développeur iOS professionnel.

Lectures complémentaires

Et c’est tout ce qu’il y a à faire ! Vous savez maintenant :

  • Comment utiliser Codable pour créer des objets qui peuvent être encodés et décodés
  • Comment utiliser JSONDecoder et JSONEncoder pour encoder et décoder des objets JSON et leurs homologues Swift
  • À quoi sert l’encodage et le décodage, et pourquoi c’est important dans le développement iOS quotidien
  • Comment travailler avec des données JSON plus complexes, et comment utiliser les types imbriqués

Vous voulez en savoir plus ? Consultez ces ressources :

  • Tableaux, dictionnaires et structures
  • Travailler avec JSON en Swift avec SwiftyJSON
  • Introduction de la programmation orientée objet en Swift
  • Comment : Trouver un élément dans un tableau en Swift
  • Comment utiliser la documentation développeur d’Apple pour le plaisir et le profit
  • Comment trouver des chaînes de caractères avec des expressions régulières en Swift
  • Pourquoi l’architecture des applications est importante
  • . App Architecture Matters
  • Les chaînes de caractères en Swift expliquées
  • Travailler avec des fichiers sur iOS avec Swift
  • Stocker des données avec NSCoding et NSKeyedArchiver

.

Laisser un commentaire