Swift JSON with dynamic Keys

269 Views Asked by At

I am trying to parse JSON using Swift, which has dynamic keys. Tried several ways but still did not find the solution. Could you please help me ?

I am trying to parse NativeName, which is dynamic based on which language country name is present.

API: https://restcountries.com/v3.1/all

struct Objects: Codable {
    let name: Name
    let cca2 : String
    let flag: String
}

struct Name: Codable {
    let common, official: String
    let nativeName: NativeName
}

struct NativeName: Codable {
    var deu : Deu
}
struct Deu: Codable {
    let official, common: String?
}

and here is JSON Model:

class ParsingService {
    
    static let shared = ParsingService()
    
    func fetchData() {
        guard let url = URL(string: "https://restcountries.com/v3.1/all") else {
            print("DEBUG: URL is nill")
            return}
        
        let session = URLSession.shared
        
        let task = session.dataTask(with: url) { data, _, error in
            
            guard let retrievedData = data, error == nil else {
                print("DEBUG: Data is not available")
                return}
            
            print("DEBUG: Data is available \(retrievedData)")

            guard let decodedData = self.JSONParsing(inputData: retrievedData) else {
                print("DEBUG: Missing data")
                return}
            print("DEBUG: Data is there")
            print("DEBUG: \(decodedData[0].cca2)")
            print("DEBUG: \(decodedData[0].flag)")
            print("DEBUG: \(decodedData[0].name.nativeName.deu.official)")

            DispatchQueue.main.async {
               print(decodedData.currencies)
            }
        }
        task.resume()
    }
    
    func JSONParsing(inputData: Data)-> [Objects]? {
        
        let decoder = JSONDecoder()
        
        do {
            let data = try? decoder.decode([Objects].self, from: inputData)
            return data
        } catch {
            print("DEBUG: Cannot get data")
            return nil
        }
    }
    
}
1

There are 1 best solutions below

1
workingdog support Ukraine On

you could try this approach:

struct ContentView: View {
    var body: some View {
        Text("testing")
            .onAppear {
                fetchData() { results in
                    print("---> results: \(results.count) \n")
                    for i in 0..<3 {
                        print("---> results[\(i)]: \(results[i].name.nativeName)")
                    }
                }
            }
    }
    
    // todo deal with errors
    func fetchData(completion: @escaping ([Objects]) -> Void) {
        let url = URL(string: "https://restcountries.com/v3.1/all")
        guard let url = url else { completion([]); return }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { completion([]); return }
            do {
                let results = try JSONDecoder().decode([Objects].self, from: data)
                completion(results)
            }
            catch {
                print("Error: \(error)")
                completion([])
            }
        }.resume()
    }
}

struct Objects: Codable {
    let name: Name
    let cca2 : String
    let flag: String
}

struct Deu: Codable {
    let official, common: String?
}

struct Name: Codable {
    let common, official: String
    let nativeName: NativeName?  // <-- here
}

 // -- here
 struct NativeName: Codable {
   var lang: [String: Deu]

   init(from decoder: Decoder) throws {
      let container = try decoder.singleValueContainer()
      lang = try container.decode([String: Deu].self)
   }

   func encode(to encoder: Encoder) throws {
      // todo
  }
}

Note, you could also use a Tuple, such as var lang: (key: String, value: Deu)