Analyse correcte de JSON dans Swift 3

123

J'essaie de récupérer une réponse JSON et de stocker les résultats dans une variable. Des versions de ce code fonctionnaient dans les versions précédentes de Swift, jusqu'à la sortie de la version GM de Xcode 8. J'ai jeté un œil à quelques articles similaires sur StackOverflow: Swift 2 Parsing JSON - Impossible d'indiquer une valeur de type 'AnyObject' et JSON Parsing dans Swift 3 .

Cependant, il semble que les idées qui y sont véhiculées ne s'appliquent pas dans ce scénario.

Comment analyser correctement la réponse JSON dans Swift 3? Quelque chose a-t-il changé dans la façon dont JSON est lu dans Swift 3?

Voici le code en question (il peut être exécuté dans une aire de jeux):

import Cocoa

let url = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

if let url = NSURL(string: url) {
    if let data = try? Data(contentsOf: url as URL) {
        do {
            let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)

        //Store response in NSDictionary for easy access
        let dict = parsedData as? NSDictionary

        let currentConditions = "\(dict!["currently"]!)"

        //This produces an error, Type 'Any' has no subscript members
        let currentTemperatureF = ("\(dict!["currently"]!["temperature"]!!)" as NSString).doubleValue

            //Display all current conditions from API
            print(currentConditions)

            //Output the current temperature in Fahrenheit
            print(currentTemperatureF)

        }
        //else throw an error detailing what went wrong
        catch let error as NSError {
            print("Details of JSON parsing error:\n \(error)")
        }
    }
}

Edit: Voici un exemple des résultats de l'appel API aprèsprint(currentConditions)

["icon": partly-cloudy-night, "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precipIntensity": 0, "windSpeed": 6.04, "summary": Partly Cloudy, "ozone": 321.13, "temperature": 49.45, "dewPoint": 41.75, "apparentTemperature": 47, "windBearing": 332, "cloudCover": 0.28, "time": 1480846460]
user2563039
la source
Pouvez-vous mettre les exemples de données renvoyés par votre appel API?
Utilisateur
1
Ouais, je viens d'ajouter un échantillon des résultats imprimés après impression (conditions actuelles). J'espère que ça aide.
user2563039
Parse json dans swift4 en utilisant le protocole codable stackoverflow.com/a/52931265/9316566
Naser Mohamed

Réponses:

172

Tout d'abord, ne chargez jamais les données de manière synchrone à partir d'une URL distante , utilisez toujours des méthodes asynchrones comme URLSession.

'Any' n'a pas de membres en indice

se produit parce que le compilateur n'a aucune idée du type des objets intermédiaires (par exemple currentlydans ["currently"]!["temperature"]) et que vous utilisez des types de collection Foundation comme NSDictionaryle compilateur n'a aucune idée du type.

De plus, dans Swift 3, il est nécessaire d'informer le compilateur du type de tous les objets en indice.

Vous devez convertir le résultat de la sérialisation JSON en type réel.

Ce code utilise URLSessionet exclusivement des types natifs Swift

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"

let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
  if error != nil {
    print(error)
  } else {
    do {

      let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
      let currentConditions = parsedData["currently"] as! [String:Any]

      print(currentConditions)

      let currentTemperatureF = currentConditions["temperature"] as! Double
      print(currentTemperatureF)
    } catch let error as NSError {
      print(error)
    }
  }

}.resume()

Pour imprimer toutes les paires clé / valeur, currentConditionsvous pouvez écrire

 let currentConditions = parsedData["currently"] as! [String:Any]

  for (key, value) in currentConditions {
    print("\(key) - \(value) ")
  }

Une note concernant jsonObject(with data:

De nombreux (il semble tout) des tutoriels suggèrent .mutableContainersou des .mutableLeavesoptions qui est tout à fait non - sens dans Swift. Les deux options sont des options Objective-C héritées pour affecter le résultat aux NSMutable...objets. Dans Swift, tout variable est mutable par défaut et le fait de passer l'une de ces options et d'assigner le résultat à une letconstante n'a aucun effet. De plus, la plupart des implémentations ne font jamais muter le JSON désérialisé de toute façon.

La seule option (rare) qui est utile dans Swift est ce .allowFragmentsqui est nécessaire si si l'objet racine JSON pourrait être un type de valeur ( String, Number, Boolou null) au lieu de l' un des types de collecte ( arrayou dictionary). Mais normalement, omettez le optionsparamètre qui signifie Aucune option .

=================================================== ==========================

Quelques considérations générales pour analyser JSON

JSON est un format de texte bien organisé. Il est très facile de lire une chaîne JSON. Lisez attentivement la chaîne . Il n'y a que six types différents: deux types de collection et quatre types de valeur.


Les types de collection sont

  • Array - JSON: objets entre crochets []- Swift: [Any]mais dans la plupart des cas[[String:Any]]
  • Dictionnaire - JSON: objets entre accolades {}- Swift:[String:Any]

Les types de valeur sont

  • Chaîne - JSON: toute valeur entre guillemets "Foo", paire "123"ou "false"- Swift:String
  • Number - JSON: valeurs numériques non entre guillemets 123ou 123.0- Swift: IntouDouble
  • Bool - JSON: trueou false pas entre guillemets - Swift: trueoufalse
  • null - JSON: null- Swift:NSNull

Selon la spécification JSON, toutes les clés des dictionnaires doivent être String.


En gros, il est toujours recommandé d'utiliser des liaisons optionnelles pour dérouler les options en toute sécurité

Si l'objet racine est un dictionnaire ( {}), transtypez le type en[String:Any]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...

et récupérez les valeurs par clés avec ( OneOfSupportedJSONTypesest une collection JSON ou un type de valeur comme décrit ci-dessus.)

if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
    print(foo)
} 

Si l'objet racine est un tableau ( []) transtypez le type en[[String:Any]]

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...

et parcourez le tableau avec

for item in parsedData {
    print(item)
}

Si vous avez besoin d'un élément à un index spécifique, vérifiez également si l'index existe

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
   let item = parsedData[2] as? OneOfSupportedJSONTypes {
      print(item)
    }
}

Dans le cas rare où le JSON est simplement l'un des types de valeur - plutôt qu'un type de collection - vous devez passer l' .allowFragmentsoption et convertir le résultat dans le type de valeur approprié, par exemple

if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...

Apple a publié un article complet dans le blog Swift: Travailler avec JSON dans Swift


=================================================== ==========================

Dans Swift 4+, le Codableprotocole fournit un moyen plus pratique d'analyser JSON directement en structures / classes.

Par exemple, l'exemple JSON donné dans la question (légèrement modifié)

let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""

peut être décodé dans la structure Weather. Les types Swift sont les mêmes que ceux décrits ci-dessus. Il existe quelques options supplémentaires:

  • Les chaînes représentant un URLpeuvent être décodées directement comme URL.
  • L' timeentier peut être décodé comme Dateavec le dateDecodingStrategy .secondsSince1970.
  • Les clés JSON snaked_cased peuvent être converties en camelCase aveckeyDecodingStrategy .convertFromSnakeCase

struct Weather: Decodable {
    let icon, summary: String
    let pressure: Double, humidity, windSpeed : Double
    let ozone, temperature, dewPoint, cloudCover: Double
    let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
    let time: Date
}

let data = Data(jsonString.utf8)
do {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .secondsSince1970
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Weather.self, from: data)
    print(result)
} catch {
    print(error)
}

Autres sources codables:

vadian
la source
C'est très utile. Je suis juste curieux de savoir pourquoi le code ci-dessus n'affiche pas de sortie visible lorsqu'il est exécuté dans une aire de jeux.
user2563039
Comme mentionné ci-dessus, vous devez convertir le résultat ambigu de dict!["currently"]!dans un dictionnaire que le compilateur peut inférer en toute sécurité l'abonnement de clé suivant.
vadian le
1
L'article Apple est plutôt cool, mais je n'ai pas pu le faire fonctionner avec swift 3 pour une raison quelconque. Il se plaint de Type [String: Any]? n'a aucun membre en indice. Il y avait aussi d'autres problèmes, mais j'ai pu le contourner. Quelqu'un a un exemple de son code qui s'exécute réellement?
Teintes du
@Shades Posez une question et publiez votre code. Très probablement, votre problème est lié à des options non emballées.
vadian
Pouvez-vous mettre les exemples de données renvoyés par votre appel API?
Utilisateur
12

Un grand changement qui s'est produit avec Xcode 8 Beta 6 pour Swift 3 était que l'id importe désormais en tant que Anyplutôt que AnyObject.

Cela signifie qu'il parsedDataest renvoyé sous forme de dictionnaire le plus probable avec le type [Any:Any]. Sans utiliser de débogueur, je ne pourrais pas vous dire exactement ce que NSDictionaryfera votre distribution , mais l'erreur que vous voyez est parce que le dict!["currently"]!typeAny

Alors, comment résolvez-vous cela? D'après la façon dont vous l'avez référencé, je suppose qu'il dict!["currently"]!s'agit d'un dictionnaire et que vous avez donc de nombreuses options:

Tout d'abord, vous pouvez faire quelque chose comme ceci:

let currentConditionsDictionary: [String: AnyObject] = dict!["currently"]! as! [String: AnyObject]  

Cela vous donnera un objet dictionnaire que vous pourrez ensuite interroger pour les valeurs et ainsi vous pourrez obtenir votre température comme ceci:

let currentTemperatureF = currentConditionsDictionary["temperature"] as! Double

Ou si vous préférez, vous pouvez le faire en ligne:

let currentTemperatureF = (dict!["currently"]! as! [String: AnyObject])["temperature"]! as! Double

J'espère que cela aide, j'ai bien peur de ne pas avoir eu le temps d'écrire un exemple d'application pour le tester.

Une dernière remarque: la chose la plus simple à faire est peut-être de simplement convertir la charge utile JSON [String: AnyObject]dès le début.

let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as! Dictionary<String, AnyObject>
discorevilo
la source
dict!["currently"]! as! [String: String]va planter
vadian le
Le point auquel il s'est écrasé était la ["température"] !! et essayant également de convertir
discorevilo
[String: String]ne peut pas fonctionner du tout car il y a quelques valeurs numériques. Cela ne fonctionne pas dans un Mac Playground
vadian
@vadian ah, oui je ne les avais pas remarqués et le bac à sable rapide les cachait utilement! - Corrigé [encore une fois] maintenant (et testé sur macOS). Merci de l'avoir signalé :)
discorevilo
6
let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"

let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!

do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] 
{
        print(names)
}
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}
BhuShan PaWar
la source
5

J'ai construit quicktype exactement dans ce but. Collez simplement votre exemple de JSON et quicktype génère cette hiérarchie de types pour vos données API:

struct Forecast {
    let hourly: Hourly
    let daily: Daily
    let currently: Currently
    let flags: Flags
    let longitude: Double
    let latitude: Double
    let offset: Int
    let timezone: String
}

struct Hourly {
    let icon: String
    let data: [Currently]
    let summary: String
}

struct Daily {
    let icon: String
    let data: [Datum]
    let summary: String
}

struct Datum {
    let precipIntensityMax: Double
    let apparentTemperatureMinTime: Int
    let apparentTemperatureLowTime: Int
    let apparentTemperatureHighTime: Int
    let apparentTemperatureHigh: Double
    let apparentTemperatureLow: Double
    let apparentTemperatureMaxTime: Int
    let apparentTemperatureMax: Double
    let apparentTemperatureMin: Double
    let icon: String
    let dewPoint: Double
    let cloudCover: Double
    let humidity: Double
    let ozone: Double
    let moonPhase: Double
    let precipIntensity: Double
    let temperatureHigh: Double
    let pressure: Double
    let precipProbability: Double
    let precipIntensityMaxTime: Int
    let precipType: String?
    let sunriseTime: Int
    let summary: String
    let sunsetTime: Int
    let temperatureMax: Double
    let time: Int
    let temperatureLow: Double
    let temperatureHighTime: Int
    let temperatureLowTime: Int
    let temperatureMin: Double
    let temperatureMaxTime: Int
    let temperatureMinTime: Int
    let uvIndexTime: Int
    let windGust: Double
    let uvIndex: Int
    let windBearing: Int
    let windGustTime: Int
    let windSpeed: Double
}

struct Currently {
    let precipProbability: Double
    let humidity: Double
    let cloudCover: Double
    let apparentTemperature: Double
    let dewPoint: Double
    let ozone: Double
    let icon: String
    let precipIntensity: Double
    let temperature: Double
    let pressure: Double
    let precipType: String?
    let summary: String
    let uvIndex: Int
    let windGust: Double
    let time: Int
    let windBearing: Int
    let windSpeed: Double
}

struct Flags {
    let sources: [String]
    let isdStations: [String]
    let units: String
}

Il génère également un code de marshaling sans dépendance pour coaxer la valeur de retour de JSONSerialization.jsonObjectdans a Forecast, y compris un constructeur pratique qui prend une chaîne JSON afin que vous puissiez rapidement analyser une Forecastvaleur fortement typée et accéder à ses champs:

let forecast = Forecast.from(json: jsonString)!
print(forecast.daily.data[0].windGustTime)

Vous pouvez installer quicktype à partir de npm avec npm i -g quicktypeou utiliser l'interface utilisateur Web pour obtenir le code généré complet à coller dans votre terrain de jeu.

David Siegel
la source
Les liens échouent.
nul ..
Gain de temps fantastique! Bon travail!
marko
1
Outil génial. Merci.
Ahmet Ardal
4

Mis à jour par la isConnectToNetwork-Functionsuite, grâce à ce post .

J'ai écrit une méthode supplémentaire pour cela:

import SystemConfiguration

func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) {

    if(isConnectedToNetwork() == false){
        completionHandler("-1" as AnyObject)
        return
    }

    let request = NSMutableURLRequest(url: URL(string: link)!)
    request.httpMethod = "POST"
    request.httpBody = postString.data(using: String.Encoding.utf8)

    let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
        guard error == nil && data != nil else { // check for fundamental networking error
            print("error=\(error)")
            return
        }

        if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 { // check for http errors
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(response)")
        }
        //JSON successfull
        do {
            let parseJSON = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
            DispatchQueue.main.async(execute: {
                completionHandler(parseJSON as AnyObject)
            });
        } catch let error as NSError {
            print("Failed to load: \(error.localizedDescription)")
        }
    }
    task.resume()
}

func isConnectedToNetwork() -> Bool {

    var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)

    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
        }
    }

    var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
    if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
        return false
    }

    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    let ret = (isReachable && !needsConnection)

    return ret
}

Alors maintenant, vous pouvez facilement appeler cela dans votre application où vous le souhaitez

loadingJSON("yourDomain.com/login.php", postString:"email=\(userEmail!)&password=\(password!)") { parseJSON in

    if(String(describing: parseJSON) == "-1"){
        print("No Internet")
    } else {

    if let loginSuccessfull = parseJSON["loginSuccessfull"] as? Bool {
        //... do stuff
    }
}
Marco Weber
la source
Marco, quelle serait la meilleure façon d'ajouter cette fonction dans le projet? dans n'importe quel contorller ou modèle?
KamalPanhwar
hé, il vous suffit d'ajouter la première méthode func loadingJSON (...) dans un fichier ou une classe supplémentaire rapide. Après cela, vous pouvez appeler cela à partir de chaque contrôleur de votre projet
Marco Weber
Je l'ai essayé, mais j'aime l'idée de démontrer une solution complète et aussi comment l'utiliser, y compris la méthode d'aide isConnectedToNetwork (), cela m'a donné l'idée de la mettre en œuvre probablement dans un bon code
Amr Angry
donc je viens d'utiliser un nouveau fichier swift (clic gauche sur l'arborescence de votre projet, nouveau fichier ..., fichier swift) et je l'ai appelé jsonhelper.swift. dans ce fichier, vous placez le premier code, loadingJSON () et isConnectedToNetwork (). après cela, vous pouvez utiliser ces deux fonctions dans chaque partie de votre projet. par exemple dans un loginVC, en tant qu'action du bouton de connexion, vous pouvez utiliser le deuxième code, vous devez évidemment changer le domaine, la chaîne de publication et les valeurs paseJson (parseJSON ["loginSuccessfull"]) pour qu'elles correspondent à votre fichier php
Marco Weber
0

Swift a une puissante inférence de type. Permet de se débarrasser du passe-partout "si laissé" ou "garde laisse" et forcer le déroulement en utilisant une approche fonctionnelle:

  1. Voici notre JSON. Nous pouvons utiliser JSON optionnel ou habituel. J'utilise optionnel dans notre exemple:

    let json: Dictionary<String, Any>? = ["current": ["temperature": 10]]
  1. Fonctions d'assistance. Nous devons les écrire une seule fois, puis les réutiliser avec n'importe quel dictionnaire:

    /// Curry
    public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
        return { a in
            { f(a, $0) }
        }
    }

    /// Function that takes key and optional dictionary and returns optional value
    public func extract<Key, Value>(_ key: Key, _ json: Dictionary<Key, Any>?) -> Value? {
        return json.flatMap {
            cast($0[key])
        }
    }

    /// Function that takes key and return function that takes optional dictionary and returns optional value
    public func extract<Key, Value>(_ key: Key) -> (Dictionary<Key, Any>?) -> Value? {
        return curry(extract)(key)
    }

    /// Precedence group for our operator
    precedencegroup RightApplyPrecedence {
        associativity: right
        higherThan: AssignmentPrecedence
        lowerThan: TernaryPrecedence
    }

    /// Apply. g § f § a === g(f(a))
    infix operator § : RightApplyPrecedence
    public func §<A, B>(_ f: (A) -> B, _ a: A) -> B {
        return f(a)
    }

    /// Wrapper around operator "as".
    public func cast<A, B>(_ a: A) -> B? {
        return a as? B
    }
  1. Et voici notre magie - extraire la valeur:

    let temperature = (extract("temperature") § extract("current") § json) ?? NSNotFound

Juste une ligne de code et aucun déballage forcé ou casting de type manuel. Ce code fonctionne dans le terrain de jeu, vous pouvez donc le copier et le vérifier. Voici une implémentation sur GitHub.

J. Doe
la source
0

C'est une autre façon de résoudre votre problème. Veuillez donc vérifier la solution ci-dessous. J'espère que cela vous aidera.

let str = "{\"names\": [\"Bob\", \"Tim\", \"Tina\"]}"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
    let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
    if let names = json["names"] as? [String] {
        print(names)
    }
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}
Olcay Ertaş
la source
0

Le problème vient de la méthode d'interaction API. L'analyse JSON n'est modifiée que dans la syntaxe. Le principal problème concerne la manière de récupérer les données. Ce que vous utilisez est un moyen synchrone d'obtenir des données. Cela ne fonctionne pas dans tous les cas. Ce que vous devriez utiliser, c'est un moyen asynchrone de récupérer des données. De cette façon, vous devez demander des données via l'API et attendre qu'elle réponde avec des données. Vous pouvez y parvenir avec une session URL et des bibliothèques tierces comme Alamofire. Vous trouverez ci-dessous le code de la méthode de session URL.

let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL.init(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
    guard error == nil else {
        print(error)
    }
    do {
        let Data = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
        // Note if your data is coming in Array you should be using [Any]()
        //Now your data is parsed in Data variable and you can use it normally
        let currentConditions = Data["currently"] as! [String:Any]
        print(currentConditions)
        let currentTemperatureF = currentConditions["temperature"] as! Double
        print(currentTemperatureF)
    } catch let error as NSError {
        print(error)
    }
}.resume()
Arun K
la source
0
{
    "User":[
      {
        "FirstUser":{
        "name":"John"
        },
       "Information":"XY",
        "SecondUser":{
        "name":"Tom"
      }
     }
   ]
}

Si je crée un modèle en utilisant json précédent En utilisant ce lien [blog]: http://www.jsoncafe.com pour générer une structure codable ou tout format

Modèle

import Foundation
struct RootClass : Codable {
    let user : [Users]?
    enum CodingKeys: String, CodingKey {
        case user = "User"
    }

    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        user = try? values?.decodeIfPresent([Users].self, forKey: .user)
    }
}

struct Users : Codable {
    let firstUser : FirstUser?
    let information : String?
    let secondUser : SecondUser?
    enum CodingKeys: String, CodingKey {
        case firstUser = "FirstUser"
        case information = "Information"
        case secondUser = "SecondUser"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        firstUser = try? FirstUser(from: decoder)
        information = try? values?.decodeIfPresent(String.self, forKey: .information)
        secondUser = try? SecondUser(from: decoder)
    }
}
struct SecondUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}
struct FirstUser : Codable {
    let name : String?
    enum CodingKeys: String, CodingKey {
        case name = "name"
    }
    init(from decoder: Decoder) throws {
        let values = try? decoder.container(keyedBy: CodingKeys.self)
        name = try? values?.decodeIfPresent(String.self, forKey: .name)
    }
}

Analyser

    do {
        let res = try JSONDecoder().decode(RootClass.self, from: data)
        print(res?.user?.first?.firstUser?.name ?? "Yours optional value")
    } catch {
        print(error)
    }
Abishek T
la source