I am retrieving data in JSON format from a URL using URLSession. After decoding the JSON data, I print it for debugging reasons and also populate an array of struct CVE. The array has data, while inside the function jsonDataRequest, but when I try to get its elements from the ViewController, the array is empty. I searched for quite sometime on how to resolve it, but I am a bit stuck. Below is my code:
Inside the class, I have the following code: The structure
struct CVE : Decodable
{
var CVE : String
var severity : String
var cvss3_score : String? = nil
var public_date : String
var bugzilla_description : String
}
struct CVEdata : Decodable
{
var cves : [CVE]
}
The array I want to use from the ViewController
var arrCVE : [CVE] = []
The function I am calling from ViewController
func jsonDataRequest ()
{
let url = "https://access.redhat.com/hydra/rest/securitydata//cve.json?after=2022-12-26"
let urlObj = URL(string: url)
URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
do
{
// Json to Array
self.jsonCVE = try JSONDecoder().decode([CVE].self, from: data!)
var strCVE : String
var strSeverity : String
var strCvss3_score : String? = nil
var strPublic_date : String
var strBugzilla_description : String
print(self.jsonCVE)
for oCVE in self.jsonCVE
{
print(oCVE.CVE + " " + oCVE.severity + " " + oCVE.public_date + " " + oCVE.bugzilla_description)
// get each the CVE info
strCVE = oCVE.CVE
strSeverity = oCVE.severity
if (oCVE.cvss3_score != nil)
{
print(oCVE.cvss3_score!)
strCvss3_score = oCVE.cvss3_score
}
else
{
print("")
strCvss3_score = ""
}
strPublic_date = oCVE.public_date
strBugzilla_description = oCVE.bugzilla_description
// save it to the array
self.arrCVE.append(CVE(CVE: strCVE, severity: strSeverity, cvss3_score: strCvss3_score, public_date: strPublic_date, bugzilla_description: strBugzilla_description))
print(self.arrCVE)
}
// Logic after response has arrived
DispatchQueue.main.async
{
print("main.async")
}
} catch
{
print(error)
}
}.resume()
}
From the ViewController I instantiate an object from the class and access the array. I am displaying on a UITextView the array.count to see how many rows it contains
let oRedHatCVEs = RedHatCVEs()
oRedHatCVEs.jsonDataRequest()
txtvJSON.text = "Array elements: " + String(oRedHatCVEs.arrCVE.count)
and the result is Array elements: 0
Does it have to do with the asynchronous way the above code works? How can I finally get the array data back to my ViewController?
You are correct, it is to do with the asynchronous function jsonDataRequest
.
There are many ways to deal (i.e wait) for asynchronous function to finish, before using the data.
This sample code shows one way using Swift async/await framework. It also shortens the code to fetch the data.
class RedHatCVEs {
var arrCVE: [CVE] = []
func jsonDataRequest() async {
if let url = URL(string: "https://access.redhat.com/hydra/rest/securitydata/cve.json?after=2022-12-26") {
do {
let (data, _) = try await URLSession.shared.data(from: url)
arrCVE = try JSONDecoder().decode([CVE].self, from: data)
}
catch {
print(error)
}
}
}
}
Use it like this:
let oRedHatCVEs = RedHatCVEs()
Task {
await oRedHatCVEs.jsonDataRequest()
txtvJSON.text = "Array elements: " + String(oRedHatCVEs.arrCVE.count)
}