Search code examples
jsonapiswiftuiswift5

Stuck with figuring out how to use api response to make a call to retrieve a response from a different link


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!


Solution

  • 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)
            }
        }
    }