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