WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Trying to get value from func to ContentView

Forums > SwiftUI

Hi,

I am trying to get data from JSON. So far, I can extract it and print it.

My goal now is to be able to use it in my ContentView so that it can be used in a Text View or something similar. I have tried creating @State variables, passing it as a parameter, etc. etc. and nothing seems to work.

I'm fairly new to SwiftUI, so I appreciate the help!

import SwiftUI
import Foundation

struct GeoService: Codable {
    var status: String
    var results: [GeoResult]
}

struct GeoResult: Codable {

    struct Geometry: Codable {

        struct Location: Codable {

            let lat: Float
            let lng: Float

            init() {
                lat = 32
                lng = 30
            }
        }
        let location: Location
    }
    let formatted_address: String
    let geometry: Geometry
}

struct ContentView: View {

//    @State private var results: Any ?????????

    var body: some View {
        NavigationView {
            Text("Test")
                .navigationTitle("Quotes")
                .task {
                    await handleData()
                }
        }

    }

    func handleData() async {
        let geoResult="""
        {
          "results": [
            {
              "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
              "geometry": {
                "location": {
                  "lat": 37.4224764,
                  "lng": -122.0842499
                }
              }
            },
            {
              "formatted_address": "2902 30th pl se, puyallup, wa, 98374",
              "geometry": {
                "location": {
                  "lat": 120.32132145,
                  "lng": -43.90235469
                }
              }
            }
          ],
          "status": "OK"
        }
        """.data(using: .utf8)!

        let decoder = JSONDecoder()
        print("executing handleData()")
        do {
            let obj = try decoder.decode(GeoService.self, from: geoResult)
            for result in obj.results {
                print("Address: \(result.formatted_address)")
                print("Lat/long: (\(result.geometry.location.lat), \(result.geometry.location.lng))")
            }
        } catch {
            print("Did not work :(")
        }
    }
}

   

@twoStraws cautions us to criticize ideas, not people. So I will try to be careful with my response.

This site, HackingWithSwift, is a community where we try to help others on their path to becoming iOS developers. We follow and support a 100 Day program that @twoStraws has carefully crafted with beginner, intermediate, and advanced topics.

@twoStraws asks that you program each day and follow the program. The forums are here to help answer questions to help those on this journey.

I sense that you are not following @twoStraws' excellent 100 Day program. You come here and dump a load of code, much of it not even related to the problem, and are asking for our help to debug your code on the same day you joined. Maybe this is the idea that I feel deserves criticism.

Perhaps, instead, you might consider following the 100 Day program? When you reach the lessons on decoding JSON you'll have a solid understanding of Views, Decoders, Encoders, Codable protocols, and updating data models. @twoStraws has several very detailed lessons that answer the very questions you're asking. This is what you really need, in my opinion.

We could help you by pointing you to the right lesson in the 100 Days curriculum, or by asking which part of a lesson gave you trouble.

From those lessons, you'll know that JSON is a temporary state. Once loaded into a useful form, perhaps an array of objects, you'll use that to display data in your views.

   

Also, I wonder if you tried to search the forums before posting your question?

Be a nice chap, give it a go!

At the top of the forums section is a very useful search bar! Wonder what that does? Ever so curious?

Helpful Hint

Try this! Enter "JSON" into the search bar. Then press the enter key on your keyboard.

Go on! Be a devil.

   

Hi @QuintonPrice

You very close. Where is the JSON file stored? on the internet or in the bundle(the app). You marked the handleData() with async if in bundle this not needed.

In the GeoResult it has a formatted_address property you should try to use camelcase in swift formattedAddress. But whoa i hear you say then JSON and struct do not match. JSONDecoder can .convertFromSnakeCase if you tell it. PS you do not need the init() in the Location So the model struct look like this

struct GeoResult: Codable {
    struct Geometry: Codable {
        struct Location: Codable {
            let lat: Float
            let lng: Float
        }
        let location: Location
    }

    let formattedAddress: String
    let geometry: Geometry
}
struct GeoService: Codable {
    var status: String
    var results: [GeoResult]
}

Next look at the method

func handleData() {
    let geoResult="""
        {
          "results": [
            {
              "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
              "geometry": {
                "location": {
                  "lat": 37.4224764,
                  "lng": -122.0842499
                }
              }
            },
            {
              "formatted_address": "2902 30th pl se, puyallup, wa, 98374",
              "geometry": {
                "location": {
                  "lat": 120.32132145,
                  "lng": -43.90235469
                }
              }
            }
          ],
          "status": "OK"
        }
        """.data(using: .utf8)!

    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase // <- changes formatted_address to formattedAddress

    print("executing handleData()")

    do {
        let obj = try decoder.decode(GeoService.self, from: geoResult)
        // do not need the for loop
    } catch {
        print("Did not work :(")
    }
}

How the use the data in ContentView add this proprty.

@State private var results = [GeoResult]()

then after let obj = try decoder.decode(GeoService.self, from: geoResult) add

results = obj.results

now you can use in example body

var body: some View {
    NavigationView {
        List(results, id: \.formattedAddress) { result in
            VStack(alignment: .leading) {
                Text(result.formattedAddress)
                    .font(.headline)
                Text("\(result.geometry.location.lat), \(result.geometry.location.lng)")
            }
        }
        .navigationTitle("Quotes")
        .task {
            handleData()
        }
    }
}

I will add the whole code on next post.

   

import SwiftUI

struct ContentView: View {
    @State private var results = [GeoResult]()

    var body: some View {
        NavigationView {
            List(results, id: \.formattedAddress) { result in
                VStack(alignment: .leading) {
                    Text(result.formattedAddress)
                        .font(.headline)
                    Text("\(result.geometry.location.lat), \(result.geometry.location.lng)")
                }
            }
            .navigationTitle("Quotes")
            .task {
                handleData()
            }
        }
    }

    func handleData() {
        let geoResult="""
            {
              "results": [
                {
                  "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
                  "geometry": {
                    "location": {
                      "lat": 37.4224764,
                      "lng": -122.0842499
                    }
                  }
                },
                {
                  "formatted_address": "2902 30th pl se, puyallup, wa, 98374",
                  "geometry": {
                    "location": {
                      "lat": 120.32132145,
                      "lng": -43.90235469
                    }
                  }
                }
              ],
              "status": "OK"
            }
            """.data(using: .utf8)!

        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase

        print("executing handleData()")

        do {
            let obj = try decoder.decode(GeoService.self, from: geoResult)
            results = obj.results
        } catch {
            print("Did not work :(")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct GeoResult: Codable {
    struct Geometry: Codable {
        struct Location: Codable {
            let lat: Float
            let lng: Float
        }
        let location: Location
    }

    let formattedAddress: String
    let geometry: Geometry
}

struct GeoService: Codable {
    var status: String
    var results: [GeoResult]
}

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.