In this lesson, we work with a heterogeneous data type found in the JSON data.
// Parsing Heterogeneous (mixed types) Type
// ex: postcode on occasion is an Int or a String
// this will lead to decoding errors while parsing
import Foundation
//==============================================
// JSON Data
//==============================================
let json = """
{
"results": [{
"gender": "female",
"name": {
"title": "Mrs",
"first": "April",
"last": "Watts"
},
"location": {
"street": {
"number": 6533,
"name": "E North St"
},
"city": "Kansas City",
"state": "Oklahoma",
"country": "United States",
"postcode": 83696,
"coordinates": {
"latitude": "80.0183",
"longitude": "82.3050"
},
"timezone": {
"offset": "-8:00",
"description": "Pacific Time (US & Canada)"
}
},
"email": "april.watts@example.com",
"login": {
"uuid": "1f72a37b-dad7-42c0-8f38-c61c8cc16fb3",
"username": "orangerabbit451",
"password": "colt",
"salt": "P1EkWcX0",
"md5": "5fd24b5b5533c30092525a7b625aeaa8",
"sha1": "834dff83eea9a0243edfa9ce3cbaf4da2806630e",
"sha256": "b81912ada362314edb0681234bc29792a872025a89b4f1bf7d83f63203feaeb7"
},
"dob": {
"date": "1948-10-23T16:57:10.463Z",
"age": 72
},
"registered": {
"date": "2015-07-27T15:43:32.525Z",
"age": 5
},
"phone": "(880)-397-2424",
"cell": "(681)-007-8567",
"id": {
"name": "SSN",
"value": "901-71-3377"
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/51.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/51.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/51.jpg"
},
"nat": "US"
},
{
"gender": "female",
"name": {
"title": "Ms",
"first": "Sofia",
"last": "Gill"
},
"location": {
"street": {
"number": 7310,
"name": "Frederick Ave"
},
"city": "Georgetown",
"state": "Nova Scotia",
"country": "Canada",
"postcode": "Y5N 0E9",
"coordinates": {
"latitude": "-0.1529",
"longitude": "-79.4845"
},
"timezone": {
"offset": "-1:00",
"description": "Azores, Cape Verde Islands"
}
},
"email": "sofia.gill@example.com",
"login": {
"uuid": "6ad311bd-8acf-401c-a66c-b28e5e6b7570",
"username": "bluedog205",
"password": "boating",
"salt": "XtuMADSl",
"md5": "142db797c2bc687900db98629f78a435",
"sha1": "d71acef64a75c5cb72c4439cbdd313f4f1a3593b",
"sha256": "1ac31d458590319d68de43a7fd8f5e0b4d91f18f48182e6b3feab263bf96ea98"
},
"dob": {
"date": "1978-06-12T22:45:12.529Z",
"age": 42
},
"registered": {
"date": "2009-10-16T05:54:27.726Z",
"age": 11
},
"phone": "818-113-5421",
"cell": "824-320-7893",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/92.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/92.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/92.jpg"
},
"nat": "CA"
}
]
}
""".data(using: .utf8)!
//==============================================
// Swift Model(s)
//==============================================
// Top Level JSON data structure is a dictionary
struct ResultsWrapper: Decodable {
let results: [Person]
}
struct Person: Decodable {
let gender: String
let location: Location
}
struct Location: Decodable {
let city: String
let country: String
let postcode: Postcode
}
enum AppError: Error {
case missingValue // if not an Int or String throw this error
}
// create a custom object to be the data type of postcode
enum Postcode: Decodable {
// handle the two cases in the JSON "postcode" property
// where postcode can be either an (Int) or a (String)
// we will also use associative values to capture the
// value of postcode
// e.g if it's an Int we will capture the Int value
case int(Int)
case string(String)
// overridbe the init(from decoder:) initializer
init(from decoder: Decoder) throws {
// handle the two cases of Int or String
// if it's not an Int or a String then we will throw
// an error
if let intValue = try? decoder.singleValueContainer().decode(Int.self) {
self = .int(intValue)
return
}
if let stringValue = try? decoder.singleValueContainer().decode(String.self) {
self = .string(stringValue)
return
}
throw AppError.missingValue // was not an Int or String
}
}
//==============================================
// decoding JSON to Swift objects
//==============================================
do {
let dictionary = try JSONDecoder().decode(ResultsWrapper.self, from: json)
let people = dictionary.results
// if trying to get to Postcode
let _ = people[0]
let secondPerson = people[1]
let postcode = secondPerson.location.postcode
switch postcode {
case .int(let intValue):
print("postcode is \(intValue)")
case .string(let stringValue):
print("postcode is \(stringValue)")
}
// postcode is Y5N 0E9
//dump(people)
} catch {
dump(error)
}
More on Github