0

I'm trying to get the data from a Rest Api to download and render in a list view in SwiftUI. I think I manage to get the JSON to download and assign to all the relevant Structs correctly but nothing displays in the list view on the simulator when I go to build it.

I'm not even sure I need to have the 'Enum CodingKeys in there.

Can anyone point out where I may be going wrong?

import Foundation
import SwiftUI
import Combine


struct ContentView: View {
    @ObservedObject var fetcher = LaunchDataFetcher()

    var body: some View {
        VStack {
            List(fetcher.launches) { launch in
                VStack (alignment: .leading) {
                    Text(launch.mission_name)
                    Text(launch.details)
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                }
            }
        }
    }
}

public class LaunchDataFetcher: ObservableObject {

    @Published var launches = [launch]()
    init(){
        load()
    }

    func load() {
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async {
                        self.launches = decodedLists
                    }
                }else {
                    print("No Data")
                }
            } catch {
                print ("Error")
            }

        }.resume()

    }
}

struct launch: Codable {
    public var flight_number: Int
    public var mission_name: String
    public var details: String

    enum CodingKeys: String, CodingKey {
           case flight_number = "flight_number"
           case mission_name = "mission_name"
           case details = "details"
        }
}

// Now conform to Identifiable
extension launch: Identifiable {
    var id: Int { return flight_number }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Anjali Shah
  • 720
  • 7
  • 21
Lofty
  • 13
  • 5
  • You don't need the CodingKeys if key and struct member have the same name. You don't even need the CodingKeys if you name the struct members *camelCased* and add the `convertFromSnakeCase` key decoding strategy. – vadian Dec 03 '19 at 09:58
  • I thought that was the case. Thanks for the advice. – Lofty Dec 03 '19 at 10:33

3 Answers3

0

First of all, I try to find which error of your code and recognize the launch.details of response data somewhere have null. So that I just mark this property Optional and it work.

For more details, you can refer below code :

struct launch: Codable {
    public var flight_number: Int
    public var mission_name: String
    public var details: String?

    enum CodingKeys: String, CodingKey {
           case flight_number = "flight_number"
           case mission_name = "mission_name"
           case details = "details"
        }
}

At catch line, get the error message to know exactly what's happen

func load() {
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async {
                        print(decodedLists)
                        self.launches = decodedLists
                    }
                }else {
                    print("No Data")
                }
            } catch let parsingError {
                print ("Error", parsingError)
            }

        }.resume()
    }

Hope this help!

Trai Nguyen
  • 809
  • 1
  • 8
  • 22
0

Any messages in console? I think you need to add NSAppTransportSecurity>NSAllowsArbitraryLoads>YES in your .plist

Transport security has blocked a cleartext HTTP

Ernist Isabekov
  • 1,205
  • 13
  • 20
0

With the help from Trai Nguyen, I managed to get the simulator to display the data.

All I was missing was to ensure the variable named 'details' had an optional property (by adding on the "?") - like so:

 public var details: String?

and then making sure when I render the text in my view, I had to give it a value to insert if the return is null - like so:

Text(launch.details ?? "No Data Found")

Here is the complete code that works for me:

import Foundation
import SwiftUI
import Combine


struct ContentView: View {
    @ObservedObject var fetcher = LaunchDataFetcher()

    var body: some View {
        VStack {
            List(fetcher.launches) { launch in
                VStack (alignment: .leading) {
                    Text(launch.mission_name)

                    Text(launch.details ?? "No Data Found")
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                }
            }
        }
    }
}


public class LaunchDataFetcher: ObservableObject {

    @Published var launches = [launch]()

    init(){
        load()
    }

    func load() {
        let url = URL(string: "https://api.spacexdata.com/v3/launches")!
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode([launch].self, from: d)
                    DispatchQueue.main.async {
                        print(decodedLists)
                        self.launches = decodedLists
                    }
                }else {
                    print("No Data")
                }
            } catch let parsingError {
                print ("Error", parsingError)
            }

        }.resume()
    }
}

struct launch: Codable {
    public var flight_number: Int
    public var mission_name: String
    public var details: String?
}

/// Now conform to Identifiable
extension launch: Identifiable {
    var id: Int { return flight_number }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Lofty
  • 13
  • 5