NEW: Learn to build the incredible iOS 15 Weather app today! >>

JSON - how to extract one/several string from JSON Server - show value in Text() not list

Forums > SwiftUI

I need some help with a small SwiftUI app - probably with a simple solution - I cant't find! Just started Xcode and Swift a week ago...

I had a look at the free available example: Sending and receiving Codable data with URLSession and SwiftUI from november 18th 2021. See link: https://www.hackingwithswift.com/books/ios-swiftui/sending-and-receiving-codable-data-with-urlsession-and-swiftui

I start to understand whats going on with SwiftUI, the app runs nicely! Now I try to do one app by myself for work.

A App should show 4 string values from a JSON servercall.

B This is the JSON file - its rewritten every couple of minutes on the server: [{"tempNow":"26","tempMax":"159","pressureNow":"159","pressurePeak":"159"}]

JSON URL: https://www.worknode.com/xcode/serverdata.json

C There are 4 values I like to get out of the JSONDecoder as pure string value like: var tempNow, var tempMax, varpressureNow, var pressureMax

D Then I just like to give the value into a Text() for displaying in usuall SwiftUI var body: some View {

VStack(spacing: 10)  {
          Text(tempNow)
    }

}:

So I dont want to have a List but a simple Text() field. Any idears?

Thanks for some help chriskapeller

Here the example code with JSON(from itunes server) and the List:

import SwiftUI

struct Response: Codable { var results: [Result] }

struct Result: Codable { var trackId: Int var trackName: String var collectionName: String }

struct PostListView: View {

@State private var results = [Result]()

var body: some View {
    VStack(spacing: 10)
    {
        List(results, id: \.trackId) { item in
            VStack(alignment: .leading) {
                Text(item.trackName)
                    .font(.headline)
                Text(item.collectionName)
            }
        }
        .task{
            await loadData()
        }

        Text("Value 1") // like to get some value into here
            .padding(20)

        Text("Value 2") // like to get some value into here
            .padding(20)
    }
}

func loadData() async {
    guard let url = URL(string: "https://itunes.apple.com/search?term=tom+petty&entity=song") else {
        print("Invalid URL")
        return
    }
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
            results = decodedResponse.results
            print{results}
        }
    } catch {
        print("Invalid data")
    }
}

}

   

Hi, the issue as I see it is that the data is in an array [ ] and has to wait for the data to load async The way I thought about it is to do is to have a ProgressView until the data is fetched then the view can show the view that you want.

struct ContentView: View {
    @State private var worknodes = [Worknode]()

    var body: some View {
        VStack {
            if worknodes.isEmpty {
                ProgressView()
            } else {
                Text(worknodes[0].tempNow) // you could make a another view and just pass in 
               // WorknodeView(worknode: worknodes[0])
            }
        }
        .task {
            await fetch()
        }
    }

    func fetch() async {
        do  {
            let userURL = URL(string: "https://www.worknode.com/xcode/serverdata.json")!
            async let userItems = try await URLSession.shared.decode([Worknode].self, from: userURL)
            worknodes = try await userItems
        } catch {
            print("Failed to fetch data!")
        }
    }
}

Here is the struct

struct Worknode: Decodable {
  let tempNow: String
  let tempMax: String
  let pressureNow: String
  let pressurePeak: String
}

and this is the extension on URLSession (only included if you use the fetch() method in the ContentView above), however just use the fetch data method you happy with

extension URLSession {
    func decode<T: Decodable>(
        _ type: T.Type = T.self,
        from url: URL,
        keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys,
        dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .deferredToData,
        dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate
    ) async throws  -> T {
        let (data, _) = try await data(from: url)

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = keyDecodingStrategy
        decoder.dataDecodingStrategy = dataDecodingStrategy
        decoder.dateDecodingStrategy = dateDecodingStrategy

        let decoded = try decoder.decode(T.self, from: data)
        return decoded
    }
}

   

Hi Nigel,

thanks for your Insight on this problem. I can follow your code...and could use it in this caase. I guess I will tinker around a bit more.

About the original code: You write that the problem lies in the asynchronous loading and that the empty array [ ] causes further problems. Would it also be a solution to fill the array with some junk data right at the beginning? Then async load the JSON - is this possible or did I not understand the real cause?

tx chriskapeller

   

Hi Chris,

When the View appears the @State private var worknodes = [Worknode]() is an empty array. So if you do

VStack {
    Text(worknodes[0].tempNow)
}

You get Fatal error: Index out of range as there is nothing in array until the asynchronous method has finished! You could have an object in the array to start with, then that would resolve the Fatal error.

@State private var worknodes = [
    Worknode(tempNow: "", tempMax: "", pressureNow: "", pressurePeak: "")
]

However I went with Progress View as sometime the loading from internet can sometime take a while (or never happen due to user internet connection) so went with a ProgressView to tell user something is happening.

   

Hacking with Swift is sponsored by Sentry

SPONSORED With Sentry’s error and performance monitoring for iOS, you see mobile vitals that actually matter, can solve any latency issues quickly, and learn how each release is performing over time.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.