Home > Software design >  How to parse data from two APIs
How to parse data from two APIs

Time:04-20

I'm making an app where I need to parse data from two APIs (enter image description here

Each launch has an ID that matches the ID of one of the four rockets in the first API. In my application there are four tabs with information about each rocket and a button that leads to another View where information about all the launches of this rocket (name, date and whether the launch was successful) should be displayed.

  1. I do not know how to update my model so that I can parse information from two APIs at once.
  2. I also can't figure out how I can display information about all launches of a particular rocket on a separate View, having first checked that their IDs match to particular rocket ID from the first API.

CodePudding user response:

EDIT-1:

The best thing to do, is to restructure your code and use a ObservableObject class to do all fetching, processing and publishing.

Here is some example code using an ObservableObject class.

struct ContentView: View {
    @StateObject var spacex = SpacexModel() // <-- here
    
    var body: some View {
        NavigationView {
            if spacex.loadingRockets || spacex.loadingLaunches {  // <-- here
                ProgressView()
            } else {
                TabView {
                    ForEach(spacex.rockets) { rocket in  // <-- here
                        ScrollView(.vertical, showsIndicators: false) {
                            VStack {
                                //MARK: - HEADER IMAGE
                                Image(systemName: "globe") // <-- for testing
                                    .renderingMode(.original)
                                    .resizable()
                                    .scaledToFill()
                                    .frame(width: 190, height: 190, alignment: .center)
                                    .padding(.bottom, 32)
                                
                                //MARK: - INFO
                                VStack(spacing: 40) {
                                    HStack {
                                        Text(rocket.name).font(.title)
                                        Spacer()
                                    }
                                    HStack {
                                        Text("First flight")
                                        Spacer()
                                        Text(rocket.first_flight)
                                    }
                                    HStack {
                                        Text("Country")
                                        Spacer()
                                        Text(rocket.country)
                                    }
                                    HStack {
                                        Text("Cost per launch")
                                        Spacer()
                                        Text("$\(rocket.cost_per_launch / 1000000)M")
                                    }
                                } //: VSTACK
                                .padding(.horizontal, 32)
                                
                                //MARK: - LAUNCHES BUTTON
                                NavigationLink {
                                    LaunchDetailView(rocket: rocket)  // <-- here
                                } label: {
                                    Text("Launches".uppercased())
                                        .font(.headline)
                                        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 56, maxHeight: 56, alignment: .center)
                                        .background(
                                            Color(UIColor.secondarySystemFill)
                                                .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
                                        )
                                        .foregroundColor(Color.green)
                                        .padding(32)
                                }
                            } //: VSTACK
                        } //: SCROLL
                    } //: LOOP
                } //: TAB
                .tabViewStyle(.page)
                .navigationBarTitleDisplayMode(.inline)
                .navigationBarHidden(true)
                .edgesIgnoringSafeArea(.vertical)
            }
        } //: NAVIGATION
        .navigationViewStyle(.stack)
        .environmentObject(spacex) // <-- here
        .edgesIgnoringSafeArea(.vertical)
    }
}

struct LaunchDetailView: View {
    @EnvironmentObject var spacex: SpacexModel  // <-- here
    let rocket: RocketInfo    // <-- here
    
    var body: some View {
        VStack {
            Text("launches for \(rocket.name)").foregroundColor(.blue)
            List {
                ForEach(spacex.launchesFor(rocket)) { launch in
                    VStack {
                        Text(launch.name).foregroundColor(.green)
                        Text("launch \(launch.details ?? "no details")")
                    }
                }
            }
        }
    }
}

class SpacexModel: ObservableObject {
    
    @Published var rockets = [RocketInfo]()
    @Published var launches = [LaunchInfo]()
    
    @Published var loadingRockets = false
    @Published var loadingLaunches = false
    
    init() {
        getRockets()
        getLaunches()
    }
    
    func getRockets() {
        self.loadingRockets = true
        guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else {
            return
        }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            // todo deal with errors
            guard let data = data, error == nil else { return }
            DispatchQueue.main.async {
                do {
                    self.rockets = try JSONDecoder().decode([RocketInfo].self, from: data)
                    self.loadingRockets = false
                } catch {
                    print(error)
                }
            }
        }.resume()
    }
    
    func getLaunches() {
        self.loadingLaunches = true
        guard let url = URL(string: "https://api.spacexdata.com/v4/launches") else {
            return
        }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            // todo deal with errors
            guard let data = data, error == nil else { return }
            DispatchQueue.main.async {
                do {
                    self.launches = try JSONDecoder().decode([LaunchInfo].self, from: data)
                    self.loadingLaunches = false
                } catch {
                    print(error)
                }
            }
        }.resume()
    }
    
    func launchesFor(_ rocket: RocketInfo) -> [LaunchInfo] {
        return launches.filter{ $0.rocket == rocket.id }
    }
    
}




struct RocketInfo: Codable, Identifiable {
    let id: String  // <-- here
    let name: String
    let country: String
    let first_flight: String
    let cost_per_launch: Int
    // ...
}

struct LaunchInfo: Identifiable, Codable {
    let id: String
    let rocket: String  // <-- here
    let details: String?
    let crew, ships, capsules, payloads: [String]
    let name: String
    // ...
}
  • Related