I'm new to swift and currently I'm trying to learn how to use API. But I'm getting this error at my viewcontroller and appdelegate.
Here is my code:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
var fetchedCountry = [Country]()
override func viewDidLoad() {
super.viewDidLoad()
parseData()
}
func parseData(){
let url = "https://restcountries.com/v3.1/all"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { data, response, error in
if error != nil{
print("error!")
}
else{
do{
let fetchData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
DispatchQueue.main.async {
for eachFetchedCountry in fetchData{
let eachCountry = eachFetchedCountry as! [String: Any]
let country = eachCountry["name"] as! String //Thread 1 : signal SIGABRT
let capital = eachCountry["capital"] as! String
self.fetchedCountry.append(Country(country: country, capital: capital))
}
print(self.fetchedCountry)
print("aa")
}
}
catch{
print("do try error")
}
}
}
task.resume()
}
class Country {
var country : String
var capital : String
init(country: String, capital: String){
self.capital = capital
self.country = country
}
}
I'm also getting this error:
Output: Could not cast value of type '__NSDictionaryI' (0x1db05bb30) to 'NSString' (0x1db05b2b0).
How can I solve this issue?
CodePudding user response:
The best solution here would be to create structs implementing the Codable
protocol, that represent the answer you are getting from this API. You don´t need to decode every property but only those you are interested in.
struct CountryTransferable: Codable{
let name: Name
let capital: [String]?
}
struct Name: Codable {
let common, official: String
let nativeName: [String: Translation]?
}
struct Translation: Codable {
let official, common: String
}
Then create another init for you Country
(I´d choose a struct over a class) that can take the TransferableCountry
as an argument:
struct Country {
var country : String
var capital : String
init(country: String, capital: String){
self.capital = capital
self.country = country
}
init(_ transferable: CountryTransferable){
// You can choose every other of the name or capital properties. These are just for example.
self.country = transferable.name.official
// As not every country seem to have a capital this can be nil
self.capital = transferable.capital?.first ?? "unknown"
}
}
And decoding would work this way:
if error != nil {
print("error!")
}
else {
do {
// Decode to your defined transferable struct as array and map it to the Country type
let countries = try JSONDecoder().decode([CountryTransferable].self, from: data).map{Country($0)}
DispatchQueue.main.async {
// assign the new values
self.fetchedCountry = countries
}
print(self.fetchedCountry)
print("aa")
}
} catch {
// print something meaningfull here
print(error)
}
}