準備
JSONファイルの作成
まずはローカルのJSONファイルを作ってプロジェクトに入れておきましょう
{
"pokemon": [
{
"id": 1,
"name": "Bulbasaur",
"types": ["Grass", "Poison"],
"description": "A strange seed was planted on its back at birth. The plant sprouts and grows with this Pokémon.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png"
},
{
"id": 2,
"name": "Charmander",
"types": ["Fire"],
"description": "Obviously prefers hot places. When it rains, steam is said to spout from the tip of its tail.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/4.png"
},
{
"id": 3,
"name": "Squirtle",
"types": ["Water"],
"description": "After birth, its back swells and hardens into a shell. Powerfully sprays foam from its mouth.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/7.png"
},
{
"id": 4,
"name": "Pikachu",
"types": ["Electric"],
"description": "When several of these Pokémon gather, their electricity could build and cause lightning storms.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png"
},
{
"id": 5,
"name": "Eevee",
"types": ["Normal"],
"description": "Its genetic code is irregular. It may mutate if it is exposed to radiation from element Stones.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/133.png"
},
{
"id": 6,
"name": "Mew",
"types": ["Psychic"],
"description": "So rare that it is still said to be a mirage by many experts. Only a few people have seen it worldwide.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/151.png"
},
{
"id": 7,
"name": "Gengar",
"types": ["Ghost", "Poison"],
"description": "Under a full moon, this Pokémon likes to mimic the shadows of people and laugh at their fright.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/94.png"
},
{
"id": 8,
"name": "Dragonite",
"types": ["Dragon", "Flying"],
"description": "This Pokémon is so strong, it can easily hold aloft a child while flying.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/149.png"
},
{
"id": 9,
"name": "Mewtwo",
"types": ["Psychic"],
"description": "It was created by a scientist after years of horrific gene-splicing and DNA-engineering experiments.",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/150.png"
},
{
"id": 10,
"name": "Sylveon",
"types": ["Fairy"],
"description": "It sends a soothing",
"image_url": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/700.png"
}
]
}
MVVMファイルの作成
以下3つのファイルに分けておく
Pokemon.swift [Model]
PokemonView.swift [View]
PokemonViewModel.swift [ViewModel]
コーディング
そのままコピペでOK
import Foundation
struct PokemonResponse: Codable {
let pokemon: [Pokemon]
}
struct Pokemon: Codable, Hashable {
let id: Int
let name: String
let types: [String]
let description: String
let imageUrl: URL
enum CodingKeys: String, CodingKey {
case id
case name
case types
case description
case imageUrl = "image_url"
}
}
import SwiftUI
struct PokemonView: View {
@ObservedObject private var viewModel = PokemonViewModel()
var body: some View {
List(viewModel.pokemon, id: \.self) { pokemon in
pokemonDataList(pokemon)
}
.onAppear {
viewModel.fetchPokemon()
}
}
}
@ViewBuilder
private func pokemonDataList(_ pokemon: Pokemon) -> some View {
HStack {
ZStack {
Circle()
.fill(Color.gray.opacity(0.4))
.frame(width: 80, height: 80)
AsyncImage(url: pokemon.imageUrl) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.frame(width: 80, height: 80)
case .failure:
Text("Failed to load image")
@unknown default:
Text("Unknown error")
}
}
}
VStack(alignment: .leading) {
HStack {
Text(pokemon.name)
.font(.system(size: 24, weight: .light))
Spacer()
Text(pokemon.types.joined(separator: ", "))
.font(.system(size: 16, weight: .light))
.foregroundColor(.black.opacity(0.8))
}
Spacer().frame(height: 4)
Text(pokemon.description)
.foregroundColor(.gray.opacity(0.8))
.font(.system(size: 12, weight: .light))
}
}
}
struct PokemonView_Previews: PreviewProvider {
static var previews: some View {
PokemonView()
}
}
import Foundation
class PokemonViewModel: ObservableObject {
@Published var pokemon: [Pokemon] = []
func fetchPokemon() {
guard let url = Bundle.main.url(forResource: "pokemon", withExtension: "json") else {
print("File not Found")
return
}
let task = URLSession.shared.dataTask(with: url) { data, _, error in
if let data = data {
if let decodedData = try? JSONDecoder().decode(PokemonResponse.self, from: data) {
DispatchQueue.main.async {
self.pokemon = decodedData.pokemon
}
}
}
}
task.resume()
}
}