Home > Software engineering >  Stuck with figuring out how to use api response to make a call to retrieve a response from a differe
Stuck with figuring out how to use api response to make a call to retrieve a response from a differe

Time:08-02

I'm currently having trouble with utilizing the PokeApi. I have code that allows me to view a pokemon's name and URL to the other JSON for a pokemon, but I'm not quite sure how I would retrieve that data. Here is what I have so far. And here is the link to the api! let pokeList = https://pokeapi.co/api/v2/pokemon?limit=100000&offset=0

import Foundation

// MARK: - Pokemon
struct PokemonList: Codable {
    let count: Int
    let results: [Result]
}

// MARK: - Result
struct Result: Codable, Identifiable {
    let id = UUID()
    let name: String
    let url: String
    enum CodingKeys: String, CodingKey {
        case name, url
    }
}

// MARK: - Pokemon
struct Pokemon: Codable, Identifiable {
    let abilities: [Ability]
    let baseExperience: Int
    let forms: [Species]
    let gameIndices: [GameIndex]
    let height: Int
    let id: Int
    let isDefault: Bool
    let locationAreaEncounters: String
    let moves: [Move]
    let name: String
    let order: Int
    let species: Species
    let sprites: Sprites
    let stats: [Stat]
    let types: [TypeElement]
    let weight: Int
}

// MARK: - Ability
struct Ability:  Codable {
    let ability: Species
    let isHidden: Bool
    let slot: Int
}

// MARK: - Species
struct Species:  Codable  {
    let name: String
    let url: String
}

// MARK: - GameIndex
struct GameIndex:  Codable{
    let gameIndex: Int
    let version: Species
}

// MARK: - Move
struct Move: Codable  {
    let move: Species
}

// MARK: - Sprites
struct Sprites: Codable {
    let backDefault: String
    let backFemale: String
    let backShiny: String
    let backShinyFemale: String
    let frontDefault: String
    let frontFemale: String
    let frontShiny: String
    let frontShinyFemale: String
}


// MARK: - Home
struct Home: Codable {
    let frontDefault: String
    let frontFemale: String
    let frontShiny: String
    let frontShinyFemale: String
}

// MARK: - Other
struct Other: Codable {
    let home: Home
    let officialArtwork: OfficialArtwork
}

// MARK: - OfficialArtwork
struct OfficialArtwork: Codable {
    let frontDefault: String
}

// MARK: - Stat
struct Stat: Codable {
    let baseStat: Int
    let effort: Int
    let stat: Species
}

// MARK: - TypeElement
struct TypeElement: Codable {
    let slot: Int
    let type: Species
}

Code for Webservice to retrieve API Call import Foundation

    class PokeWebService: ObservableObject{
        
        @Published var pokeList: PokemonList?
        @Published var pokemonIndvl: Pokemon?
            
        
        func getPokemonList() async throws {
            let (data, _) = try await URLSession.shared.data(from: Constants.url.pokeList)
                Task{@MainActor in
                    self.pokeList = try JSONDecoder().decode(PokemonList.self, from: data)
                }
        }
        
        func getPokemonFromPokemonList(invlURL: String) async throws{
            var plURL: URL = URL(string: invlURL)!
            let (data, _) = try await URLSession.shared.data(from: plURL)
                Task{@MainActor in
                    self.pokemonIndvl = try JSONDecoder().decode(Pokemon.self, from: data)
                    
                }
        }
        
    }
    
     

My Content View

import SwiftUI

struct PokeListView: View {
    @EnvironmentObject var pokeWebService: PokeWebService
    var body: some View {
        List(pokeWebService.pokeList?.results ?? []){ pokemon in  // <-- here
            Text(pokemon.name)
            Text(pokemon.url)
            
        }
        .task {
            do{
                try await pokeWebService.getPokemonList()
                
            } catch{
                print("---> task error: \(error)")
            }
        }
    }
}

struct PokeListView_Previews: PreviewProvider {
    static var previews: some View {
        PokeListView()
            .environmentObject(PokeWebService())
    }
}

Since I'm still fairly new to working with APIs in Swift, how would I retrieve the data from the Pokemon.url?

Thank you!

CodePudding user response:

try something like this approach (note the different model structs). You will need to find from the server docs, which fields are optional and adjust the various structs:

import Foundation
import SwiftUI


struct ContentView: View {
    @StateObject var pokeWebService = PokeWebService()
    
    var body: some View {
        PokeListView().environmentObject(pokeWebService)
    }
}

struct PokeListView: View {
    @EnvironmentObject var pokeWebService: PokeWebService
    
    var body: some View {
        NavigationStack {
            List(pokeWebService.pokeList?.results ?? []){ pokemon in
                NavigationLink(pokemon.name, value: pokemon.url)
            }
            .navigationDestination(for: String.self) { urlString in
                PokeDetailsView(urlString: urlString)
            }
        }
        .environmentObject(pokeWebService)
        .task {
            do {
                try await pokeWebService.getPokemonList()
            } catch{
                print("---> PokeListView error: \(error)")
            }
        }
    }
}

struct PokeDetailsView: View {
    @EnvironmentObject var pokeWebService: PokeWebService
    @State var urlString: String
    
    var body: some View {
        VStack {
            Text(pokeWebService.pokemonIndvl?.name ?? "no name")
            Text("height: \(pokeWebService.pokemonIndvl?.height ?? 0)")
            // ... other info
        }
        .task {
            do {
                try await pokeWebService.getPokemon(from: urlString)
            } catch{
                print("---> PokeDetailsView error: \(error)")
            }
        }
    }
}

class PokeWebService: ObservableObject{
    
    @Published var pokeList: PokemonList?
    @Published var pokemonIndvl: Pokemon?
    
    func getPokemonList() async throws {
        let (data, _) = try await URLSession.shared.data(from: Constants.url.pokeList)
        Task{@MainActor in
            self.pokeList = try JSONDecoder().decode(PokemonList.self, from: data)
        }
    }
    
    func getPokemon(from urlString: String) async throws {
        if let url = URL(string: urlString) {
            let (data, _) = try await URLSession.shared.data(from: url)
            Task{@MainActor in
                self.pokemonIndvl = try JSONDecoder().decode(Pokemon.self, from: data)
            }
        }
    }
    
}

// using https://app.quicktype.io/

// MARK: - PokemonList
struct PokemonList: Codable {
    let count: Int
    let results: [ListItem]  // <-- don't use the word Result
}

// MARK: - ListItem
struct ListItem: Codable, Identifiable {
    let id = UUID()
    let name: String
    let url: String
    enum CodingKeys: String, CodingKey {
        case name, url
    }
}

struct HeldItem: Codable {
    let item: Species
    let versionDetails: [VersionDetail]

    enum CodingKeys: String, CodingKey {
        case item
        case versionDetails = "version_details"
    }
}

struct VersionDetail: Codable {
    let rarity: Int
    let version: Species
}

 // MARK: - Pokemon
 struct Pokemon: Codable, Identifiable {
     let abilities: [Ability]
     let baseExperience: Int
     let forms: [Species]
     let gameIndices: [GameIndex]
     let height: Int
     let heldItems: [HeldItem]
     let id: Int
     let isDefault: Bool
     let locationAreaEncounters: String
     let moves: [Move]
     let name: String
     let order: Int
     let pastTypes: [String]
     let species: Species
     let sprites: Sprites
     let stats: [Stat]
     let types: [TypeElement]
     let weight: Int

     enum CodingKeys: String, CodingKey {
         case abilities
         case baseExperience = "base_experience"
         case forms
         case gameIndices = "game_indices"
         case height
         case heldItems = "held_items"
         case id
         case isDefault = "is_default"
         case locationAreaEncounters = "location_area_encounters"
         case moves, name, order
         case pastTypes = "past_types"
         case species, sprites, stats, types, weight
     }
 }

 // MARK: - Ability
 struct Ability: Codable {
     let ability: Species
     let isHidden: Bool
     let slot: Int

     enum CodingKeys: String, CodingKey {
         case ability
         case isHidden = "is_hidden"
         case slot
     }
 }

 // MARK: - Species
 struct Species: Codable {
     let name: String
     let url: String
 }

 // MARK: - GameIndex
 struct GameIndex: Codable {
     let gameIndex: Int
     let version: Species

     enum CodingKeys: String, CodingKey {
         case gameIndex = "game_index"
         case version
     }
 }

 // MARK: - Move
 struct Move: Codable {
     let move: Species
     let versionGroupDetails: [VersionGroupDetail]

     enum CodingKeys: String, CodingKey {
         case move
         case versionGroupDetails = "version_group_details"
     }
 }

 // MARK: - VersionGroupDetail
 struct VersionGroupDetail: Codable {
     let levelLearnedAt: Int
     let moveLearnMethod, versionGroup: Species

     enum CodingKeys: String, CodingKey {
         case levelLearnedAt = "level_learned_at"
         case moveLearnMethod = "move_learn_method"
         case versionGroup = "version_group"
     }
 }

 // MARK: - GenerationV
 struct GenerationV: Codable {
     let blackWhite: Sprites

     enum CodingKeys: String, CodingKey {
         case blackWhite = "black-white"
     }
 }

 // MARK: - GenerationIv
 struct GenerationIv: Codable {
     let diamondPearl, heartgoldSoulsilver, platinum: Sprites

     enum CodingKeys: String, CodingKey {
         case diamondPearl = "diamond-pearl"
         case heartgoldSoulsilver = "heartgold-soulsilver"
         case platinum
     }
 }

 // MARK: - Versions
 struct Versions: Codable {
     let generationI: GenerationI
     let generationIi: GenerationIi
     let generationIii: GenerationIii
     let generationIv: GenerationIv
     let generationV: GenerationV
     let generationVi: [String: Home]
     let generationVii: GenerationVii
     let generationViii: GenerationViii

     enum CodingKeys: String, CodingKey {
         case generationI = "generation-i"
         case generationIi = "generation-ii"
         case generationIii = "generation-iii"
         case generationIv = "generation-iv"
         case generationV = "generation-v"
         case generationVi = "generation-vi"
         case generationVii = "generation-vii"
         case generationViii = "generation-viii"
     }
 }

 // MARK: - Sprites
 class Sprites: Codable {
     let backDefault: String
     let backFemale: String?
     let backShiny: String
     let backShinyFemale: String?
     let frontDefault: String
     let frontFemale: String?
     let frontShiny: String
     let frontShinyFemale: String?
     let other: Other?
     let versions: Versions?
     let animated: Sprites?

     enum CodingKeys: String, CodingKey {
         case backDefault = "back_default"
         case backFemale = "back_female"
         case backShiny = "back_shiny"
         case backShinyFemale = "back_shiny_female"
         case frontDefault = "front_default"
         case frontFemale = "front_female"
         case frontShiny = "front_shiny"
         case frontShinyFemale = "front_shiny_female"
         case other, versions, animated
     }

 }

 // MARK: - GenerationI
 struct GenerationI: Codable {
     let redBlue, yellow: RedBlue

     enum CodingKeys: String, CodingKey {
         case redBlue = "red-blue"
         case yellow
     }
 }

 // MARK: - RedBlue
 struct RedBlue: Codable {
     let backDefault, backGray, backTransparent, frontDefault: String
     let frontGray, frontTransparent: String

     enum CodingKeys: String, CodingKey {
         case backDefault = "back_default"
         case backGray = "back_gray"
         case backTransparent = "back_transparent"
         case frontDefault = "front_default"
         case frontGray = "front_gray"
         case frontTransparent = "front_transparent"
     }
 }

 // MARK: - GenerationIi
 struct GenerationIi: Codable {
     let crystal: Crystal
     let gold, silver: Gold
 }

 // MARK: - Crystal
 struct Crystal: Codable {
     let backDefault, backShiny, backShinyTransparent, backTransparent: String
     let frontDefault, frontShiny, frontShinyTransparent, frontTransparent: String

     enum CodingKeys: String, CodingKey {
         case backDefault = "back_default"
         case backShiny = "back_shiny"
         case backShinyTransparent = "back_shiny_transparent"
         case backTransparent = "back_transparent"
         case frontDefault = "front_default"
         case frontShiny = "front_shiny"
         case frontShinyTransparent = "front_shiny_transparent"
         case frontTransparent = "front_transparent"
     }
 }

 // MARK: - Gold
 struct Gold: Codable {
     let backDefault, backShiny, frontDefault, frontShiny: String
     let frontTransparent: String?

     enum CodingKeys: String, CodingKey {
         case backDefault = "back_default"
         case backShiny = "back_shiny"
         case frontDefault = "front_default"
         case frontShiny = "front_shiny"
         case frontTransparent = "front_transparent"
     }
 }

 // MARK: - GenerationIii
 struct GenerationIii: Codable {
     let emerald: Emerald
     let fireredLeafgreen, rubySapphire: Gold

     enum CodingKeys: String, CodingKey {
         case emerald
         case fireredLeafgreen = "firered-leafgreen"
         case rubySapphire = "ruby-sapphire"
     }
 }

 // MARK: - Emerald
 struct Emerald: Codable {
     let frontDefault, frontShiny: String

     enum CodingKeys: String, CodingKey {
         case frontDefault = "front_default"
         case frontShiny = "front_shiny"
     }
 }

 // MARK: - Home
 struct Home: Codable {
     let frontDefault: String
     let frontFemale: String?
     let frontShiny: String
     let frontShinyFemale: String?

     enum CodingKeys: String, CodingKey {
         case frontDefault = "front_default"
         case frontFemale = "front_female"
         case frontShiny = "front_shiny"
         case frontShinyFemale = "front_shiny_female"
     }
 }

 // MARK: - GenerationVii
 struct GenerationVii: Codable {
     let icons: DreamWorld
     let ultraSunUltraMoon: Home

     enum CodingKeys: String, CodingKey {
         case icons
         case ultraSunUltraMoon = "ultra-sun-ultra-moon"
     }
 }

 // MARK: - DreamWorld
 struct DreamWorld: Codable {
     let frontDefault: String
     let frontFemale: String?

     enum CodingKeys: String, CodingKey {
         case frontDefault = "front_default"
         case frontFemale = "front_female"
     }
 }

 // MARK: - GenerationViii
 struct GenerationViii: Codable {
     let icons: DreamWorld
 }

 // MARK: - Other
 struct Other: Codable {
     let dreamWorld: DreamWorld
     let home: Home
     let officialArtwork: OfficialArtwork

     enum CodingKeys: String, CodingKey {
         case dreamWorld = "dream_world"
         case home
         case officialArtwork = "official-artwork"
     }
 }

 // MARK: - OfficialArtwork
 struct OfficialArtwork: Codable {
     let frontDefault: String

     enum CodingKeys: String, CodingKey {
         case frontDefault = "front_default"
     }
 }

 // MARK: - Stat
 struct Stat: Codable {
     let baseStat, effort: Int
     let stat: Species

     enum CodingKeys: String, CodingKey {
         case baseStat = "base_stat"
         case effort, stat
     }
 }

 // MARK: - TypeElement
 struct TypeElement: Codable {
     let slot: Int
     let type: Species
 }

EDIT-1:

if you have problems with the NavigationStack, use this NavigationView instead.

NavigationView {
    List(pokeWebService.pokeList?.results ?? []){ pokemon in
        NavigationLink(destination: PokeDetailsView(urlString: pokemon.url)) {
            Text(pokemon.name)
        }
    }
}
  • Related