1

I have got a problem with displaying JSON data on a SwiftUI View. I tried several tutorials and read articles which are related to my problem, but nothing seems appropriate enough.

For example everyone displays JSON data from an API with a fancy list or pictures, but I only want to know how you can simply display one word on a view (without List{}).

I chose the PokeAPI to figure out how to display "Hi Charmander" with the Text() instruction.

Example of a list (and without ObservableObject and @Published)

I want to get rid of the List and use sth. Text(resultsVar[0].name).onAppear(perform: loadData) like instead

import SwiftUI

struct pokeRequest:Codable {
    var results: [Result]

}
struct Result:Codable {
    var name:String

}

struct ViewOne: View {

    @State var resultsVar = [Result]()

    var body: some View {

        VStack{
        //unfortunately this does not work:
            //Text(resultsVar[0].name).onAppear(perform: loadData)
        List(resultsVar, id: \.name) { item in
                   VStack(alignment: .leading) {
                       Text("Hi \(item.name)")
                   }
            }
        .onAppear(perform: loadData)
    }
}
    func loadData(){

        guard let url = URL(string: "https://pokeapi.co/api/v2/pokemon?offset=3&limit=3") else {
            print("Invalid URL")
            return
        }

        let request = URLRequest(url: url)

        URLSession.shared.dataTask(with: request) { data, response, error in

            if let data = data {
            if let decodedResponse = try? JSONDecoder().decode(pokeRequest.self, from: data) {

                DispatchQueue.main.async {
                    self.resultsVar = decodedResponse.results
                    }
                return
                }
            }
            print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
        }.resume()
    }
}

struct ViewOne_Previews: PreviewProvider {
    static var previews: some View {
        ViewOne()
    }
}

Second try with a different approach (without .onAppear()) In this approach I tried with class: Observable Object and @Published but I also didn't come to my wished UI-output.

 import SwiftUI
 struct pokeRequest2:Codable {
     var results2: [pokeEach2]
 }

 struct pokeEach2:Codable {
     var name2:String
 }


 class Webservice:ObservableObject {
     @Published var pokeInfo: [pokeRequest2] = [pokeRequest2]()

     func decodepokemon() {

         let session = URLSession.shared
         let url = URL(string: "https://pokeapi.co/api/v2/pokemon?offset=3&limit=3")!
         let task = session.dataTask(with: url) { data, response, error in

             if error != nil || data == nil {
                 print("Client error!")
                 return
             }

             guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
                 print("Server error!")
                 return
             }

             guard let mime = response.mimeType, mime == "application/json" else {
                 print("Wrong MIME type!")
                 return
             }

             do {
                 let response = try JSONDecoder().decode(pokeRequest2.self, from: data!)

                 print(self.pokeInfo[0].results2[0].name2)

                 DispatchQueue.main.async {
                   self.pokeInfo = [response]
                    }

             }  catch {
                 print("JSON error: \(error.localizedDescription)")
             }
         }.resume()
     }

     init() {
         decodepokemon()
     }
 }

 struct ViewTwo: View {
     @ObservedObject var webservice: Webservice = Webservice()

      var body: some View {

              Text("please help")
        //Does also not work: Text(self.webservice.pokeInfo2[0].results2[0].name2)//.onAppear()
        //Since a few minutes somehow the debug area prints "JSON error: The data couldn’t be read because it is missing." instead of "charmander"
      }
 }
 struct ViewTwo_Previews: PreviewProvider {
     static var previews: some View {
         ViewTwo()
     }
 }

I tried several tutorials and read articles which are related to my problem, but nothing seems appropriate enough. I would highly appreciate any help :-) Thanks in advance!

braendnew
  • 81
  • 1
  • 5

3 Answers3

1

I am by no means an expert (started using Swift less than three days ago) but I think I might have what you are looking for. This enables you to call a simple REST API that contains only a simple dictionary (in my case the dictionary contains "Differential": 5.22) and then display that number as text when a button is pressed. Hope the following is helpful!

struct DifferentialData: Decodable {
var Differential: Double
}

struct ContentView: View {

@State var data = DifferentialData(Differential: 0.00)

func getData() {
    guard let url = URL(string: "Enter your URL here")
    else { return } //Invalid URL
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        let differentials = try!       JSONDecoder().decode(DifferentialData.self, from: data!)
        print(differentials)
        DispatchQueue.main.async {
            self.data = differentials
        }
    }
    .resume()
}

var body: some View {
    
    VStack{
        Text("\(data.Differential)")
        Button("Refresh data") {self.getData()}
    }
}
}
0

I may be misunderstanding the question but in SwiftUI text can be displayed as follows:

Text("Hi" + item.name)

But as I say I'm not sure if that's the question.

dot3
  • 1,078
  • 1
  • 13
  • 21
  • thank you for your answers. but the solution shouldn't deal with `item` anymore, because I want to get rid of the List{} and the loop in it. My view should only contain sth. like: `Text(resultsVar[0].name).onAppear(perform: loadData)` which should display "Charmander" – braendnew Nov 27 '19 at 19:46
0

As you will dynamically change the list items, you have to use .id() modifier. And in order to use .id(), the result must conforms to Hashable. The following code can help you solve the problem.

struct Result:Codable, Hashable {
var name:String

}

struct ViewOne: View {

@State var resultsVar = [Result]() // [Result(name: "firsy"),Result(name: "second"), ]//[Result]()

  var body: some View {

    VStack{
    Spacer()
    //unfortunately this does not work:
        //Text(resultsVar[0].name).onAppear(perform: loadData)
    List(resultsVar, id: \.name) { item in
               VStack(alignment: .leading) {
                   Text("Hi \(item.name)")
               }
        }.id(resultsVar)

}.onAppear(perform: loadData)
E.Coms
  • 11,065
  • 2
  • 23
  • 35