Home > Mobile >  Trying to access/modify SwiftUI variable from within a function
Trying to access/modify SwiftUI variable from within a function

Time:05-19

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!

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 :(")
        }
    }
}

CodePudding user response:

I moved the get request to its own class so we can leverage ObservableObject and Publish the data. Publishing the data allows the subscribing views to update data when the published data changes. In this case, your ContentView is subscribing to the data via the @ObservedObject var geoData: ResponseData line of code.

Additionally, I added how to access two pieces of relevant data in your view, and displayed it in a list for easy reading. This should give you an idea of how to access/display the data.

Hopefully this provides enough information for you to tweak the answer to get it work the way you desire.

import SwiftUI

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 {
 @ObservedObject var geoData: ResponseData

 var body: some View {
    NavigationView {
        if #available(iOS 15.0, *) {
            List {
                Text(geoData.geoResultsData?.results?[0].formatted_address ?? "Loading")
                Text(String(geoData.geoResultsData?.results?[0].geometry.location.lat ?? 0))
            }
            .navigationTitle("Quotes")
            .task {
                await geoData.handleData()
                print(geoData.geoResultsData, "yessssss")
            }
        } else {
            Text("failure")
        }
    }
  }
}

class ResponseData: ObservableObject {

@Published var geoResultsData: GeoService?

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)
        geoResultsData = obj
    } catch {
        print("Did not work :(")
    }
  }
}

EDIT:

You will need to initialize some data within your app. You can initialize it as empty for the time being, if desired. This can be done by doing something like the following: ContentView(geoData: ResponseData.init())

CodePudding user response:

you could try something simple like this:

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

struct GeoResult: Identifiable, Codable {
    let id = UUID()  // <-- here
    let formatted_address: String
    let geometry: Geometry
}

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

struct ContentView: View {
    @State private var results: [GeoResult] = []  // < --- here
    
    var body: some View {
        NavigationView {
            List {
                ForEach(results) { geo in   // < --- here
                    VStack (alignment: .leading, spacing: 10) {
                        Text(geo.formatted_address).foregroundColor(.blue)
                        Text("lat \(geo.geometry.location.lat)")
                        Text("long \(geo.geometry.location.lng)")
                    }
                }
            }
            .navigationTitle("Quotes")
            .task {
                await handleData()
            }
        }
    }
    
    // --- presumably this will be a call to a server api
    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)!

        do {
            let obj = try JSONDecoder().decode(GeoService.self, from: geoResult)
            results = obj.results  // < --- here
            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 :(")
        }
    }
}
  • Related