Executing function after data loads into view SwiftUI - json

I want the Snapshot function to be executed once my ContentView has loaded in all the network data. I have no problem with the API call, I just simply want the Snapshot function to run after all data has loaded into ContentView.
Expected result after pressing button:
Expected result after pressing button
Actual result after pressing button:
Actual result after pressing button
ContentView.swift
extension View {
func Snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
struct ContentView: View {
#ObservedObject var FetchResults = fetchResults()
var contentView: some View {
ContentView()
}
var body: some View {
Group {
if FetchResults.dataHasLoaded {
VStack {
Text(FetchResults.clout.postFound?.body ?? "n/a")
.padding()
.background(Color.blue)
.mask(RoundedRectangle(cornerRadius: 30, style: .continuous))
.shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.3), radius: 10, x: 0, y:10)
.padding()
.frame(maxWidth: 300)
}
Button(action: {
let image = contentView.Snapshot()
print(image)
}, label:{Text("press me to print view")})
} else {
Text("loading data")
}
}
}
}
GotPost.swift -- my ViewModel running the API call
class fetchResults: ObservableObject {
#Published var clout = Cloutington()
#Published var dataHasLoaded = false
init() {
getData { clout in
self.clout = clout
}
}
private func getData(completion: #escaping (Cloutington) -> ()) {
let parameters = "{\r\n \"PostHashHex\": \"2f9633c2460f23d9d5690fe9fd058457ebeb01f6f75805aa426cdaab90a1f4b4\"\r\n}"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "https://bitclout.com/api/v0/get-single-post")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = postData
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { (responseData, response, error) in
print(error)
print(response)
print(responseData)
if let resData = responseData {
let decoder = JSONDecoder()
do
{
let finalData = try decoder.decode(Cloutington.self, from: resData)
DispatchQueue.main.async {
completion(finalData)
self.dataHasLoaded = true
}
}
catch (let error)
{
print(error)
}
}
}
task.resume()
}
}
PostFound.swift -- model parsing JSON data
struct Cloutington: Decodable {
var postFound: PostFound?
enum CodingKeys: String, CodingKey {
case postFound = "PostFound"
}
}
struct PostFound: Decodable {
var id: String?
var postHashHex, posterPublicKeyBase58Check, parentStakeID, body: String?
var imageURLs: [String]?
// var recloutedPostEntryResponse: JSONNull?
var creatorBasisPoints, stakeMultipleBasisPoints: Int?
var timestampNanos: Double?
var isHidden: Bool?
var confirmationBlockHeight: Int?
var inMempool: Bool?
var profileEntryResponse: ProfileEntryResponse?
var likeCount, diamondCount: Int?
var isPinned: Bool?
var commentCount, recloutCount: Int?
var diamondsFromSender: Int?
enum CodingKeys: String, CodingKey {
case postHashHex = "PostHashHex"
case posterPublicKeyBase58Check = "PosterPublicKeyBase58Check"
case parentStakeID = "ParentStakeID"
case body = "Body"
case imageURLs = "ImageURLs"
case creatorBasisPoints = "CreatorBasisPoints"
case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
case timestampNanos = "TimestampNanos"
case isHidden = "IsHidden"
case confirmationBlockHeight = "ConfirmationBlockHeight"
case inMempool = "InMempool"
case profileEntryResponse = "ProfileEntryResponse"
case likeCount = "LikeCount"
case diamondCount = "DiamondCount"
case isPinned = "IsPinned"
case commentCount = "CommentCount"
case recloutCount = "RecloutCount"
case diamondsFromSender = "DiamondsFromSender"
}
}
// MARK: - ProfileEntryResponse
struct ProfileEntryResponse: Decodable {
var publicKeyBase58Check, username, profileEntryResponseDescription, profilePic: String?
var isHidden, isReserved, isVerified: Bool?
var coinPriceBitCloutNanos, stakeMultipleBasisPoints: Int?
enum CodingKeys: String, CodingKey {
case publicKeyBase58Check = "PublicKeyBase58Check"
case username = "Username"
case profileEntryResponseDescription = "Description"
case profilePic = "ProfilePic"
case isHidden = "IsHidden"
case isReserved = "IsReserved"
case isVerified = "IsVerified"
case coinPriceBitCloutNanos = "CoinPriceBitCloutNanos"
case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
}
} ```
Really appreciate the help 🙏

You're going to get more and better help in general if you follow the Swift API Design Guidelines for naming types and variables. That means using lower case for the initial letters of properties and methods, and upper case for the initial letters of types. I understand that you may come from a platform like C# where the conventions are different. But when you don't do things the Swift way (or worse, as in your code, where you do both!), you make it harder for us to understand your code.
Also, this is a bad pattern:
#ObservedObject var FetchResults = fetchResults()
The problem is that SwiftUI doesn't understand the ownership of the FetchResults object, and will recreate it each time the view is recreated. In this case, you should use #StateObject instead:
#StateObject var fetchResults = FetchResults()
Check out this article by Paul Hudson for guidance on when to use #StateObject and when to use #ObservableObject.
However, that won't fix the problem you posted about. Your code creates the snapshot by saying contentView.Snapshot(), which calls your contentView computed property, which creates a new ContentView from scratch, which creates a new, unloaded FetchResults (regardless of whether you use #ObservedObject or #StateObject).
You need to reuse the FetchResults that has already loaded its data, so just say self.snapshot() instead:
Button(
action: {
let image = self.snapshot()
print(image)
},
label: {
Text("press me to print view")
}
)

Related

Converting JSON 64bit string to an Image - difficulty

My project accesses a JSON file with 200+ country elements of which one name/value pair is flag: containing what I think is Base64 encryption - example below. I have successfully included the name: and the :flag in a Picker. The Picker works fine and displays the chosen country and update the CoreData & CloudKit (ClientEntity) record with both. The challenge is to call the record for display or editing. I cannot get the flag Image to display - only the default value. It is storing something in the clientFlag field. I was expecting it to store the flag:1920 bytes of JSON data - instead (copied from the console) it is storing "CD_clientFlag" = "{ length=1458, sha256=c1572719d906e1df50fdf296845b0942ef148060c609d585434777b6276a67e9 }";\n
Clearly, my logic is faulty or this field contains a different data format, or both. Any help would be appreciated.
Of course a better method would be to reference the JSON data via the id: or the name:, but I gave up on this due to inexperience and opted to store both name: and flag
Relevant code below:
Successful Picker code in a View
NavigationStack {
Form {
VStack(alignment: .leading) {
clientNameAddress
Group {
Picker(selection: $selectedCountry,
label: Text("")) {
ForEach(countryVM.countries) { country in
HStack {
Text(country.name)
if let flagImage = country.flagImage {
Image(uiImage: flagImage)
} else {
Image(systemName: "questionmark")
}
}.tag(country as Country?)//: HSTACK6
}
}
.padding(.horizontal, 10)
.pickerStyle(MenuPickerStyle())
VStack {
Text("Selected Country: \(selectedCountry?.name ?? "No Name")")
.foregroundColor(.secondary)
if let flagImage = selectedCountry?.flagImage {
Image(uiImage: flagImage)
}
}.padding(.horizontal, 10)
}
Toggle("Existing Client?", isOn: $clientVM.clientExisting)
.toggleStyle(.switch)
.foregroundColor(.blue)
.padding(.horizontal, 10)
// SelectContact()
Button(action: {
assignFlag()
print("AddView \(clientVM.clientHostCountry)")
clientVM.addClient(context: viewContext)
dismiss()
}, label: {
if clientVM.clientItem == nil {
Text("Add Account")
.frame(minWidth: 0, maxWidth: 200)
} else {
Text("Save Modified Client")
.frame(minWidth: 0, maxWidth: 200)
}
})
.tint(.purple)
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle)
}
.navigationTitle(clientVM.clientItem == nil ? "Add Account" : "Edit Account \(clientVM.clientName)")
}.disableAutocorrection(true)
#if os(iOS)
.navigationBarTitle("Add Account")
.navigationBarTitleDisplayMode(.inline)
#endif
}
JSON Example
{
"id": 2,
"name": "Albania",
"isoAlpha2": "AL",
"isoAlpha3": "ALB",
"isoNumeric": 8,
"currency": {
"code": "ALL",
"name": "Lek",
"symbol": "Lek"
},
"flag": "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAUCAIAAAAVyRqTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyRDFBRDI0NTE3NkYxMUUyODY3Q0FBOTFCQzlGNjlDRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyRDFBRDI0NjE3NkYxMUUyODY3Q0FBOTFCQzlGNjlDRiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdERDBDNDA4MTc1MzExRTI4NjdDQUE5MUJDOUY2OUNGIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjJEMUFEMjQ0MTc2RjExRTI4NjdDQUE5MUJDOUY2OUNGIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+GPq/agAAAiRJREFUeNrEVb9rFEEUfm9m9nb3bhNz50UMClopRAsFrUURW1tBrSzsLPwfbPwDbGz8F8QiIkLAKiCkUIKiGBEFwXAhd7fZH7Mz83zZtbC4TdyF4LDF8N7ON9/73jczuN4/A4czBBzaqIUmAA+Q0wjQRzkUCsv4USEHKKs4/0DtWOdAgxLxrUk+mqyHIkLx2eg1k1gA3kwDtYFeFOqVnj5NRwXQip7eGG9+svlPV1wff3mejwuiZ9n2i3zCRWANAta1kaFX9OS1jkdkHdGyCt6blMmel8E3p1OgY6iueL2b/pEtZ5qx5kRCLIhMyK4WMQFt2HzdpEzypZ5OnOVUSoT1gqi6BOvA7ZoDUan5JB3BXxPeOALBahigxloLQO4SFy5hBjMOpuA0zc4ebL4OYExuZl0dxNiRh63MZ4jYXjzJiG77/cuqW8UvqvBO0Ge+jjsplKHmgrCIIeICyke9pXPKZ+kvqPCS1+X6T4vO42iJN/YB22jNIo6cYWN9dfqdya560TxKruKaF32w2abVW2VWtNCa6fRQnpTeD1vcD4anZOdNEa8VCZN9EA6/2+KE9Ob3dUit+XbJHRfqXjBgTZjYhk3nUDAQN/CsDJbDYIfcbvlhU+hqQUpuSo6tcstfYMp8q9z1+7+cyfZMuUe4zZGp/GfLxRm4bbIPu4scYbIJOO6EO+hSVf9y8zLQmGxUKrNDRu7HtSH0n+NHrpr8/1fmtwADAEjB+xzEjgF0AAAAAElFTkSuQmCC"
}
CountryViewModel
import SwiftUI
import Foundation
struct Country: Codable, Identifiable, Comparable, Hashable {
let id = UUID() // New
let name: String
let isoAlpha3: String
let flag: Data?
enum CodingKeys: String, CodingKey {
case name, isoAlpha3, flag
}
var flagImage: UIImage? {
guard let flagData = flag else { return nil }
return UIImage(data: flagData)
}
static func == (lhs: Country, rhs: Country) -> Bool {
lhs.name == rhs.name
}
static func < (lhs: Country, rhs: Country) -> Bool {
lhs.name < rhs.name
}
struct HeadingData: Codable, Identifiable, Hashable {
let id: Int
let name: String
let isoAlpha3: String
let flag: Data
}
struct CurrencyData: Codable, Equatable, Identifiable, Hashable {
let id: UUID
let code: String
let name: String
let symbol: String
}
}
class CountryModel: ObservableObject {
#Published var countries: [Country] = []
init() {
self.countries = Bundle.main.decode("Countries.json")
// let data = self.countries
// let json = try? JSONSerialization.jsonObject(with: data, options: [])
}
}
Computed Property to decode the clientFlag
extension ClientEntity {
var showFlag: Image {
let str = clientFlag ?? Data()
if str.isEmpty {
let flagImage = Image(systemName: "plus.circle")
return flagImage
} else {
let dataDecoded : Data = Data(base64Encoded: str) ?? Data()
guard let flagImage = UIImage.init(data: dataDecoded) else { return Image(systemName: "plus.circle") }
return Image(uiImage: flagImage)
}
}
}
ClientViewModel
import Foundation
import Combine
import CoreData
class ClientViewModel: ObservableObject {
#Published var clientName = ""
#Published var clientAddress1 = ""
#Published var clientAddress2 = ""
#Published var clientPhoneNumber = ""
#Published var clientHostCountry = ""
#Published var clientFlag: Data?
#Published var clientIndustry = ""
#Published var clientLogo: Data?
#Published var clientComments = ""
#Published var clientExisting = false
#Published var clientWebSite: URL?
#Published var clientUpdated: Date = Date()
#Published var clientCreated: Date = Date()
#Published var clientItem: ClientEntity!
#Published var selectedContact: [ContactEntity] = []
func addClient(context: NSManagedObjectContext) {
if clientItem == nil {
let client = ClientEntity(context: context)
client.clientName = clientName
client.clientAddress1 = clientAddress1
client.clientAddress2 = clientAddress2
client.clientPhoneNumber = clientPhoneNumber
print("clientVM \(clientHostCountry)")
client.clientHostCountry = clientHostCountry
client.clientFlag = clientFlag
client.clientIndustry = clientIndustry
// client.clientLogo = clientLogo
client.clientComments = clientComments
client.clientExisting = clientExisting
// client.clientWebSite = clientWebSite
client.clientUpdated = clientUpdated
client.clientCreated = clientCreated
print("Selected Contact: \(selectedContact.description)")
let uniqueContact = Set(selectedContact)
for contact in uniqueContact {
client.addToContacts(contact)
}
} else {
clientItem.clientName = clientName
clientItem.clientAddress1 = clientAddress1
clientItem.clientAddress2 = clientAddress2
clientItem.clientPhoneNumber = clientPhoneNumber
clientItem.clientHostCountry = clientHostCountry
clientItem.clientFlag = clientFlag
clientItem.clientIndustry = clientIndustry
// clientItem.clientLogo = clientLogo
clientItem.clientComments = clientComments
clientItem.clientExisting = clientExisting
// clientItem.clientWebSite = clientWebSite
clientItem.clientUpdated = clientUpdated
clientItem.clientCreated = clientCreated
print("Selected Contact: \(selectedContact.description)")
let uniqueContact = Set(selectedContact)
for contact in uniqueContact {
clientItem.addToContacts(contact)
}
}
save(context: context)
clientName = ""
clientAddress1 = ""
clientAddress2 = ""
clientPhoneNumber = ""
clientHostCountry = ""
clientFlag = Data()
clientIndustry = ""
// clientLogo = Data()
clientComments = ""
clientExisting = false
// clientWebSite = URL(string: "")!
clientUpdated = Date()
clientCreated = Date()
}
func editClient(client: ClientEntity) {
clientItem = client
}
func delete(client: ClientEntity, context: NSManagedObjectContext) {
context.delete(client)
save(context: context)
}
func save(context: NSManagedObjectContext) {
do {
try context.save()
} catch {
fatalError("Error saving: \(error.localizedDescription)")
}
}
func isExisting(client: ClientEntity, context: NSManagedObjectContext) {
client.clientExisting.toggle()
save(context: context)
}
}
I tried adding the .jpeg and .png parameters as well as the reverse of the successful Picker code. No luck.
New Issue
The program crashes with Thread 1: breakpoint 1.2 (1) error (green background?) at the .tag(country as Country) in the Picker. Hovering over the 'country' property shows that it contains the 1st country in the JSON file. The Variables window shows:
country B2Bv5.Country
id Foundation.UUID
uuid (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
name String
_guts _StringGuts
_object _StringObject
_countAndFlagsBits UInt64 13835058055282163723
_object Builtin.BridgeObject 0x4000600001ef0ec0
isoAlpha3 String
_guts _StringGuts
_object _StringObject
_countAndFlagsBits UInt64 4671041
_object NSTaggedPointerString "RCp1xmeaxi" 0xe300000000000000
flag String? some
_guts _StringGuts
_object _StringObject
_countAndFlagsBits UInt64 13835058055282165492
_object Builtin.BridgeObject 0x4000000128065000
CountryViewModel
import SwiftUI
struct Country: Codable, Identifiable, Comparable, Hashable {
let id = UUID() // New
let name: String
let isoAlpha3: String
let flag: String?
enum CodingKeys: String, CodingKey {
case name, isoAlpha3, flag
}
var flagImage: UIImage? {
if let flagString = flag,
let data = flagString.data(using: .utf8),
let decodedData = Data(base64Encoded: data),
let image = UIImage(data: decodedData) {
return image
} else {
let img = UIImage(systemName: "plus.circle")
return img == nil ? UIImage() : img!
}
}
static func == (lhs: Country, rhs: Country) -> Bool {
lhs.name == rhs.name
}
static func < (lhs: Country, rhs: Country) -> Bool {
lhs.name < rhs.name
}
struct HeadingData: Codable, Identifiable, Hashable {
let id: Int
let name: String
let isoAlpha3: String
let flag: String
}
struct CurrencyData: Codable, Equatable, Identifiable, Hashable {
let id: UUID
let code: String
let name: String
let symbol: String
}
}
class CountryModel: ObservableObject {
#Published var countries: [Country] = []
init() {
self.countries = Bundle.main.decode("Countries.json")
}
}
Picker extension
var hostCountry: some View {
VStack(alignment: .leading) {
Picker(selection: $clientVM.clientHostCountry,
label: Text("")) {
ForEach(countryVM.countries) { country in
VStack {
Text(country.name)
Image(uiImage: country.flagImage!)
}.tag(country as Country)
}
.padding(.horizontal, 10)
.padding()
.pickerStyle(MenuPickerStyle())
}.padding(.horizontal)
}
extension ClientEntity
extension ClientEntity {
var showName: String {
return clientName ?? "Undefined"
}
}
extension ClientEntity {
var showExisting: String {
if clientExisting {
return " Client"
} else {
return "New Logo"
}
}
}
You could try this simple approach, to display the Image of the flag, works for me:
As can be seen in the json data, flag is a String.
So make sure you declare let flag: String? in your struct Country, so you can decode the json in your CountryModel.
struct ContentView: View {
let flag = "iVBORw0KGgoAAAANSUhEUgAAAB4AAAAUCAIAAAAVyRqTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyRDFBRDI0NTE3NkYxMUUyODY3Q0FBOTFCQzlGNjlDRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyRDFBRDI0NjE3NkYxMUUyODY3Q0FBOTFCQzlGNjlDRiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjdERDBDNDA4MTc1MzExRTI4NjdDQUE5MUJDOUY2OUNGIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjJEMUFEMjQ0MTc2RjExRTI4NjdDQUE5MUJDOUY2OUNGIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+GPq/agAAAiRJREFUeNrEVb9rFEEUfm9m9nb3bhNz50UMClopRAsFrUURW1tBrSzsLPwfbPwDbGz8F8QiIkLAKiCkUIKiGBEFwXAhd7fZH7Mz83zZtbC4TdyF4LDF8N7ON9/73jczuN4/A4czBBzaqIUmAA+Q0wjQRzkUCsv4USEHKKs4/0DtWOdAgxLxrUk+mqyHIkLx2eg1k1gA3kwDtYFeFOqVnj5NRwXQip7eGG9+svlPV1wff3mejwuiZ9n2i3zCRWANAta1kaFX9OS1jkdkHdGyCt6blMmel8E3p1OgY6iueL2b/pEtZ5qx5kRCLIhMyK4WMQFt2HzdpEzypZ5OnOVUSoT1gqi6BOvA7ZoDUan5JB3BXxPeOALBahigxloLQO4SFy5hBjMOpuA0zc4ebL4OYExuZl0dxNiRh63MZ4jYXjzJiG77/cuqW8UvqvBO0Ge+jjsplKHmgrCIIeICyke9pXPKZ+kvqPCS1+X6T4vO42iJN/YB22jNIo6cYWN9dfqdya560TxKruKaF32w2abVW2VWtNCa6fRQnpTeD1vcD4anZOdNEa8VCZN9EA6/2+KE9Ob3dUit+XbJHRfqXjBgTZjYhk3nUDAQN/CsDJbDYIfcbvlhU+hqQUpuSo6tcstfYMp8q9z1+7+cyfZMuUe4zZGp/GfLxRm4bbIPu4scYbIJOO6EO+hSVf9y8zLQmGxUKrNDRu7HtSH0n+NHrpr8/1fmtwADAEjB+xzEjgF0AAAAAElFTkSuQmCC"
#State var img = UIImage()
var body: some View {
Image(uiImage: img)
.onAppear {
if let data = flag.data(using: .utf8),
let decodedData = Data(base64Encoded: data),
let image = UIImage(data: decodedData) {
img = image
}
}
}
}
EDIT-1
In your model struct Country you may want to use the following code:
struct Country: Codable, Identifiable, Comparable, Hashable {
let id = UUID()
let name: String
let isoAlpha3: String
let flag: String? // <--- here
enum CodingKeys: String, CodingKey {
case name, isoAlpha3, flag
}
// --- here
var flagImage: UIImage? {
if let flagString = flag,
let data = flagString.data(using: .utf8),
let decodedData = Data(base64Encoded: data),
let image = UIImage(data: decodedData) {
return image
} else {
return nil
}
}
//....
}
Once you have your image as Data, you can use that to store it in your clientFlag in your ClientEntity and your ClientViewModel things.
EDIT-2:
In your Picker,
instead of using this if let data = country.flag!.data(using: .utf8),...etc
trying to again decode the flag,
why not use the country.flagImage, this is what you have it for. Modify struct Country with:
var flagImage: UIImage {
if let flagString = flag,
let data = flagString.data(using: .utf8),
let decodedData = Data(base64Encoded: data),
let image = UIImage(data: decodedData) {
return image
} else {
let img = UIImage(systemName: "plus.circle")
return img == nil ? UIImage() : img!
}
}
and in the Picker use:
VStack {
Text(country.name)
Image(uiImage: country.flagImage)
}
Your flag image string iVBORw0KGgoAAAANSUhEUgAAAB4AAAAUCAIAAAAVyRqTAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw is almost base64, but not quite - it's missing the padding characters at the end that Data(base64encoded:) needs.
You can easily add them like this:
let str = "iVBOR...AADw" // your clientFlag string from JSON
let pad = String(repeating: "=", count: 4 - (str.count % 4))
let data = Data(base64Encoded: str + pad)

How to read and display a dictionary from JSON?

I am working on an app that fetches the data from JSON and displays it.
However, I am stuck with an error saying Instance method 'appendInterpolation(_:formatter:)' requires that '[String : Int]' inherit from 'NSObject'
Here is my data structure:
struct Data: Codable {
var message: String
var data: Objects
}
struct Objects: Codable {
var date: String
var day: Int
var resource: String
var stats, increase: [String: Int]
}
Function to fetch the data:
func getData() {
let urlString = "https://russianwarship.rip/api/v1/statistics/latest"
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!) { data, _, error in
if let data = data {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(Data.self, from: data)
self.data = decodedData
} catch {
print("Hey there's an error: \(error.localizedDescription)")
}
}
}.resume()
}
And a ContentView with the #State property to pass the placeholder data:
struct ContentView: View {
#State var data = Data(message: "", data: Objects(date: "123", day: 123, resource: "", stats: ["123" : 1], increase: ["123" : 1]))
var body: some View {
VStack {
Button("refresh") { getData() }
Text("\(data.data.date)")
Text("\(data.data.day)")
Text(data.message)
Text("\(data.data.stats)") //error is here
Here is an example of JSON response
I wonder if the problem is in data structure, because both
Text("\(data.data.date)")
Text("\(data.data.day)")
are working just fine. If there are any workarounds with this issue – please, I would highly appreciate your help!:)
stats is [String: Int], and so when you want to use it, you need to supply the key to get the value Int, the result is an optional that you must unwrap or supply a default value in Text
So use this:
Text("\(data.data.stats["123"] ?? 0)")
And as mentioned in the comments, do not use Data for your struct name.
EDIT-1: there are two ways you can make the struct fields camelCase; one is using the CodingKeys as shown in ItemModel, or at the decoding stage, as shown in the getData() function. Note, I've also updated your models to make them easier to use.
struct DataModel: Codable {
var message: String
var data: ObjectModel
}
struct ObjectModel: Codable {
var date: String
var day: Int
var resource: String
var stats: ItemModel
var increase: ItemModel
}
struct ItemModel: Codable {
var personnelUnits: Int
var tanks: Int
var armouredFightingVehicles: Int
// ...
// manual CodingKeys
// enum CodingKeys: String, CodingKey {
// case tanks
// case personnelUnits = "personnel_units"
// case armouredFightingVehicles = "armoured_fighting_vehicles"
// }
}
struct ContentView: View {
#State var dataModel = DataModel(message: "", data: ObjectModel(date: "123", day: 123, resource: "", stats: ItemModel(personnelUnits: 123, tanks: 456, armouredFightingVehicles: 789), increase: ItemModel(personnelUnits: 3, tanks: 4, armouredFightingVehicles: 5)))
var body: some View {
VStack {
Button("get data from Server") { getData() }
Text("\(dataModel.data.date)")
Text("\(dataModel.data.day)")
Text(dataModel.message)
Text("\(dataModel.data.stats.armouredFightingVehicles)") // <-- here
}
}
func getData() {
let urlString = "https://russianwarship.rip/api/v1/statistics/latest"
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { data, _, error in
if let data = data {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase // <-- here
dataModel = try decoder.decode(DataModel.self, from: data)
} catch {
print("--> error: \(error)")
}
}
}.resume()
}
}
}

Unable to read JSON from rest API

Trying to read this JSON Data:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
But my app keeps crashing and I'm struggling to understand why (still fairly new to swift). I think the data is stored in a dictionary and I'm just handling it incorrectly. Could someone please explain the correct way to decode this JSON and how I would show it on the view? I tried JSONSerialization in place of JSONDecoder but got the same results so not sure if that's the right direction.
Model:
struct Model: Codable{
var userId: Int? = nil
var id: Int? = nil
var title: String? = nil
var completed: Bool? = nil
enum CodingKeys: CodingKey{
case userId, id, title, completed
}
}
JSON Load Function:
func loadData(){
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
//create url request
var request = URLRequest(url: url)
//specify the method to use
request.httpMethod = "GET"
//set HTTP request Header, can set more than one.
request.setValue("application/json", forHTTPHeaderField: "Accept")
//send the request
let dataTask = URLSession.shared.dataTask(with: request){ data, response, error in
if let data = data{
if let users = try? JSONDecoder().decode(Model.self, from: data){
DispatchQueue.main.async {
self.dataInfo = users
}
}else{
print("Data Not Got")
}
}
if let response = response{
print("Response Got")
}
if let error = error{
print("\(error)")
}
}
dataTask.resume()
}
Swift UI View:
struct ContentView: View {
#State var dataInfo = Model()
var body: some View {
VStack{
Button("Go"){
loadData()
}
Text(dataInfo.title!)
}
By using ! after title, you're doing what is called a "force unwrap" -- telling the system that although the variable/property is declared as an Optional (in this case String?) that you're going to guarantee that it is not nil and there is in fact a value there. The problem is, before you've done the API call, that property is in fact nil, causing your program to crash.
Here's one way to change it (explanation follows):
struct Model: Codable{
var userId: Int
var id: Int
var title: String
var completed: Bool
}
struct ContentView: View {
#State var dataInfo : Model?
var body: some View {
VStack{
Button("Go"){
loadData()
}
if let dataInfo = dataInfo {
Text(dataInfo.title)
}
}
}
func loadData() {
// your previous code here
}
}
In this version, dataInfo is an Optional, and it gets set when the API call is made. Then, if let dataInfo = dataInfo does something called "optional binding," basically telling the system to only run the following code in the event that dataInfo isn't nil.
Finally, I've changed your Model to have non-Optional properties, since the API call you're using returns values for all of those fields. If you wanted to keep your previous model, you'd probably want to change my code to something like:
if let title = dataInfo?.title {
Text(title)
}
Check out the Swift Programming Language book for more information on Optionals and how to use them: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

Why is my API call not completely reached?

I am currently building a public transportation app in SwiftUI Xcode 12 beta, where you input origin, destination, time of arrival, and date. The idea is to then convert the origin and destination input (private func FetchOriginCoordinates and private func FetchDestCoordinates) into coordinates using an API. This API will then give that data to another API that plans the route.
The problem is that the route planner API doesn't give any output. I have tried printing self.Trips.append(...) but the list is completely empty. I believe this is because the code doesn't reach that part of the code. I have put some print statements in the code in order to understand where the error is occurring. When I call the API my output in the console is this:
Test 2021-04-16 22:33:19.271246+0200 OnTrack2.0[6592:428359] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed Optional(http://api.sl.se/api2/TravelplannerV3_1/trip.json?key=40892db48b394d3a86b2439f9f3800fd&originExtId=300101115&destExtId=300101426&Date=2021-04-18&Time=09:20&searchForArrival=1) [] 10767 bytes Level 1 Level 1.5
This means that the code only reaches the print("Level 1.5") statement. But never the following print statements such as print("Level 2") or print(self.trips). Why is that and how do I solve it?
import Foundation
struct NominationStructure: Decodable {
var lat: String
var lon: String
enum CodingKeys: String, CodingKey {
case lat = "lat"
case lon = "lon"
}
}
struct NearbyStopsStructure: Decodable {
var stopLocationOrCoordLocation: [stopLocationOrCoordLocationStructure]
}
struct stopLocationOrCoordLocationStructure: Decodable {
var StopLocation: StopLocationStructure
}
struct StopLocationStructure: Decodable {
var mainMastExtId: String
}
struct JSONStructure: Decodable {
var Trip: [TripStructure]
}
struct TripStructure: Decodable {
var LegList: LegListStructure
}
struct LegListStructure: Decodable {
var Leg: [LegStructure]
}
struct LegStructure: Decodable {
var Origin: StationStructure
var Destination: StationStructure
var Product: ProductStructure
var name: String
var type: String
var dist: String
}
struct StationStructure: Decodable {
var time: String
var name: String
var date: String
}
struct ProductStructure: Decodable {
var catIn: String
}
struct LocationInfo {
var iD = String()
var input = String()
var lat = String()
var lon = String()
var name = String()
var time = String()
var date = String()
var vehicleType = String()
var transportType = String()
var dist = String()
var legName = String()
}
//One way is to call your functions when you know a step is completed
enum TripFetchStatus: String {
case start
case fetchedOriginCoordinates
case fetchedOriginId
case fetchedDestinationCoordinates
case fetchedDestinationId
case fetchingTrip
case done
case stopped
}
class PlanTripViewModel: ObservableObject {
//Apple frowns upon frezzing a screen so having a way to update the user on what is going on
//When a step is complete have it do something else
#Published var fetchStatus: TripFetchStatus = .stopped{
didSet{
switch fetchStatus {
case .start:
print("Test")
FetchOriginCoordinates { (cors) in
self.origin.lat = cors[0].lat
self.origin.lon = cors[0].lon
self.fetchStatus = .fetchedOriginCoordinates
}
case .fetchedOriginCoordinates:
self.FetchOriginID { (stops) in
self.origin.iD = stops.stopLocationOrCoordLocation[0].StopLocation.mainMastExtId
self.fetchStatus = .fetchedOriginId
}
case .fetchedOriginId:
FetchDestCoordinates { (cors) in
self.dest.lat = cors[0].lat
self.dest.lon = cors[0].lon
self.fetchStatus = .fetchedDestinationCoordinates
}
case .fetchedDestinationCoordinates:
self.FetchDestID { (stops) in
self.dest.iD = stops.stopLocationOrCoordLocation[0].StopLocation.mainMastExtId
self.fetchStatus = .fetchedDestinationId
}
case .fetchedDestinationId:
//Once you have everthing in place then go to the next API
FetchTrip()
case .fetchingTrip:
print("almost done")
case .done:
print("any other code you need to do")
case .stopped:
print("just a filler")
}
}
}
#Published var trip: LocationInfo = LocationInfo()
#Published var dest: LocationInfo = LocationInfo()
#Published var origin: LocationInfo = LocationInfo()
#Published var arrivalTime = String()
#Published var travelDate = String()
#Published var tripIndex = Int()
#Published var Trips: [Dictionary<String, String>] = []
public func FetchTrip() {
Trips.removeAll()
let tripKey = "40892db48b394d3a86b2439f9f3800fd"
let tripUrl = URL(string: "http://api.sl.se/api2/TravelplannerV3_1/trip.json?key=\(tripKey)&originExtId=\(self.origin.iD)&destExtId=\(self.dest.iD)&Date=\(self.travelDate)&Time=\(self.arrivalTime)&searchForArrival=1")
print(tripUrl)
URLSession.shared.dataTask(with: tripUrl!) {data, response, error in
if let data = data {
print(data)
print("Level 1")
do {
print("Level 1.5")
if let decodedJson = try? JSONDecoder().decode(JSONStructure.self, from: data) {
self.tripIndex = decodedJson.Trip.count - 1
print("Level 2")
for i in 0..<decodedJson.Trip[self.tripIndex].LegList.Leg.count {
self.trip.transportType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].type
if self.trip.transportType == "WALK" {
self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
self.trip.dist = decodedJson.Trip[self.tripIndex].LegList.Leg[i].dist
print(self.origin.name)
print(self.dest.name)
self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "Distance": self.trip.dist])
}
else {
self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
self.trip.legName = decodedJson.Trip[self.tripIndex].LegList.Leg[i].name
print(self.origin.name)
print(self.dest.name)
self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "LegName": self.trip.legName])
}
}
}
} catch {
print(error)
}
}
}.resume()
print(self.Trips)
}
//Simple version just to replicate put your code within
private func FetchOriginCoordinates(completion: #escaping ([NominationStructure]) -> ()) {
let scheme = "https"
let host = "nominatim.openstreetmap.org"
let path = "/search"
let queryItemCountry = URLQueryItem(name: "country", value: "Sweden")
let queryItemCity = URLQueryItem(name: "city", value: "Stockholm")
let queryItemStreet = URLQueryItem(name: "street", value: self.origin.input)
let queryItemFormat = URLQueryItem(name: "format", value: "json")
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItemCountry, queryItemCity, queryItemStreet, queryItemFormat]
URLSession.shared.dataTask(with: urlComponents.url!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode([NominationStructure].self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchDestCoordinates(completion: #escaping ([NominationStructure]) -> ()) {
let scheme = "https"
let host = "nominatim.openstreetmap.org"
let path = "/search"
let queryItemCountry = URLQueryItem(name: "country", value: "Sweden")
let queryItemCity = URLQueryItem(name: "city", value: "Stockholm")
let queryItemStreet = URLQueryItem(name: "street", value: self.dest.input)
let queryItemFormat = URLQueryItem(name: "format", value: "json")
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItemCountry, queryItemCity, queryItemStreet, queryItemFormat]
URLSession.shared.dataTask(with: urlComponents.url!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode([NominationStructure].self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchOriginID(completion: #escaping (NearbyStopsStructure) -> ()) {
let nearbyStopsKey = "8444f9a2f75f4c27937a7165abd532a0"
let stopIDUrl = URL(string: "http://api.sl.se/api2/nearbystopsv2.json?key=\(nearbyStopsKey)&originCoordLat=\(self.origin.lat)&originCoordLong=\(self.origin.lon)&maxNo=1")
URLSession.shared.dataTask(with: stopIDUrl!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode(NearbyStopsStructure.self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchDestID(completion: #escaping (NearbyStopsStructure) -> ()) {
let nearbyStopsKey = "8444f9a2f75f4c27937a7165abd532a0"
let stopIDUrl = URL(string: "http://api.sl.se/api2/nearbystopsv2.json?key=\(nearbyStopsKey)&originCoordLat=\(self.dest.lat)&originCoordLong=\(self.dest.lon)&maxNo=1")
URLSession.shared.dataTask(with: stopIDUrl!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode(NearbyStopsStructure.self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
}

Assign json data to labels

I'm beginner in swift programming and I want to assign the data to labels
so I have this code below
#IBOutlet weak var Infected: WKInterfaceLabel!
#IBOutlet weak var Cured: WKInterfaceLabel!
#IBOutlet weak var Deaths: WKInterfaceLabel!
#IBOutlet weak var OmanInfected: WKInterfaceLabel!
#IBOutlet weak var OmanCured: WKInterfaceLabel!
#IBOutlet weak var OmanDeaths: WKInterfaceLabel!
func check()
{
// MARK: - CoronaData
struct CoronaData: Codable {
var countrydata: [Countrydatum]
var stat: String
}
// MARK: - Countrydatum
struct Countrydatum: Codable {
var info: Info
var totalCases, totalRecovered, totalUnresolved, totalDeaths: Int
var totalNewCasesToday, totalNewDeathsToday, totalActiveCases, totalSeriousCases: Int
var totalDangerRank: Int
enum CodingKeys: String, CodingKey {
case info
case totalCases = "total_cases"
case totalRecovered = "total_recovered"
case totalUnresolved = "total_unresolved"
case totalDeaths = "total_deaths"
case totalNewCasesToday = "total_new_cases_today"
case totalNewDeathsToday = "total_new_deaths_today"
case totalActiveCases = "total_active_cases"
case totalSeriousCases = "total_serious_cases"
case totalDangerRank = "total_danger_rank"
}
}
// MARK: - Info
struct Info: Codable {
var ourid: Int
var title, code: String
var source: String
}
if let url = URL(string: "https://api.thevirustracker.com/free-api?countryTotal=OM") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(Countrydatum.self, from: data)
print(gitData.totalCases as Any)
} catch let error {
print(error)
}
}
}.resume()
}
}
how can I now assign these values to the labels here (Num labels)
I know this is a stupid question to ask but forgive me for that
I just want to finish the program and learn some new things
You can download this free program to make the struct
see this example to use the struct
if let url = URL(string: "https://api.thevirustracker.com/free-api?countryTotal=OM") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let decoder = JSONDecoder()
let profile = try decoder.decode(ProfileResponse.self, from: data)
print(profile.countrydata.first)
} catch let error {
print(error)
}
}
}.resume()
}
You can change the text programmatically using the setText(:) or setAttributedText(:) method.
I replicate the request in Playground the text is different bus the way that you need to access the data is the same, country data is an array so you need to get the first element
import UIKit
import PlaygroundSupport
// MARK: - ProfileResponse
struct ProfileResponse: Codable {
var countrydata: [ProfileCountrydatum]
var stat: String
}
// MARK: - ProfileCountrydatum
struct ProfileCountrydatum: Codable {
var info: ProfileInfo
var totalCases, totalRecovered, totalUnresolved, totalDeaths: Int
var totalNewCasesToday, totalNewDeathsToday, totalActiveCases, totalSeriousCases: Int
var totalDangerRank: Int
enum CodingKeys: String, CodingKey {
case info
case totalCases = "total_cases"
case totalRecovered = "total_recovered"
case totalUnresolved = "total_unresolved"
case totalDeaths = "total_deaths"
case totalNewCasesToday = "total_new_cases_today"
case totalNewDeathsToday = "total_new_deaths_today"
case totalActiveCases = "total_active_cases"
case totalSeriousCases = "total_serious_cases"
case totalDangerRank = "total_danger_rank"
}
}
// MARK: - ProfileInfo
struct ProfileInfo: Codable {
var ourid: Int
var title, code: String
var source: String
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.text = "Hello World!"
label.textColor = .black
view.addSubview(label)
self.view = view
if let url = URL(string: "https://api.thevirustracker.com/free-api?countryTotal=OM") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(ProfileResponse.self, from: data)
label.text = "\(gitData.countrydata.first?.totalDeaths ?? 0)"
} catch let error {
print(error)
}
}
}.resume()
}
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
The countrydata is presented as an array, but the corresponding property in your model is of type String.