RDowns RDowns - 1 month ago 11
Swift Question

How do I create a custom struct to assign the contents of array into usable variables?

I have a JSON function that previously mapped the contents of an array (

countries
,
divisions
,
teams
(not shown)) into seperate variables using this code:

let task = URLSession.shared.dataTask{ (response: URLResponse?, error: Error?) in

DispatchQueue.main.async
{

if error != nil {
print("error=\(error)")
return
}

do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json)

if let arr = json?["countries"] as? [[String:String]] {
self.countryId = arr.flatMap { $0["id"]!}
self.countries = arr.flatMap { $0["name"]!}
self.teamsTableView.reloadData()
print ("Countries: ",self.countries)
}

if let arr = json?["divisions"] as? [[String:String?]] {

self.divisions = arr.flatMap { ($0["name"]! )}
self.divisionId = arr.flatMap { ($0["id"]! )}
self.teamsTableView.reloadData()
print ("Divisions: ",self.divisions)
}



} catch{
print(error)
}
}
}


However, I have since learned "that mapping multiple arrays as data source in conjunction with flatMap is pretty error-prone", and that I should use a custom struct instead.

How / Where do I begin to write a custom struct to assign the contents of this JSON array into seperate variables?

Thanks

UPDATE:

I have used the code as suggested below and I believe this is along the right lines. Below is the current output.


Countries: [KeepScore.SumbitScoreViewController.Country(name: Optional("Denmark"), countryId: Optional("1")), KeepScore.SumbitScoreViewController.Country(name: Optional("Belgium"), countryId: Optional("2")), KeepScore.SumbitScoreViewController.Country(name: Optional("Brasil"), countryId: Optional("3")),


However by the looks of it, it is also putting the countryId into the variable and I'd like just the name so I could call it in a UITable...What are the next steps in doing this?

Answer

Same idea as you were doing, except instead of mapping each individual property you map the whole country.

//define a struct
struct Country {
    var name: String?
    var countryId: String?

    init(_ dictionary: [String : String]) {
        self.name = dictionary["name"]
        self.countryId = dictionary["id"]
    }
}

//defined in your class
var countries = [Country]()

//when parsing your JSON
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json)

if let arr = json?["countries"] as? [[String : String]] {
    self.countries = arr.flatMap { Country($0) }
    print ("Countries: ",self.countries)
}

Apply the same idea to divisions.

Edit:

In the code you've posted, you extract both the name and the id, that's why I included both in the struct. So you can remove the id from the struct if you wish, or add other variables in the future. Then when you want to extract them and use them in your table view you just do:

let country = countries[indexPath.row]
cell.textLabel.text = country.name
Comments