【Xcode/Swift】SwiftyJSONを使ってポケモン検索機能を作ってみる

検索したポケモンをTableViewにリスト表示させるだけのシンプルなアプリです、サンプルなのでアーキテクチャはガン無視して処理を全部VC (ViewController)に書きました。

Contents 非表示

実装

View側

ロジック側

import Foundation
import UIKit

class Pokemon {
    var name: String
    var picture: UIImage?
    var stats: [String: Int]
    var moves: [String]

    init(name: String, picture: UIImage?, stats: [String: Int], moves: [String]) {
        self.name = name
        self.picture = picture
        self.stats = stats
        self.moves = moves
    }
}

今回はnamepictureを使用

import UIKit

class PokemonTableViewCell: UITableViewCell {

    @IBOutlet private weak var pokemonImageView: UIImageView!
    @IBOutlet private weak var pokemonNameLabel: UILabel!

    override func prepareForReuse() {
        super.prepareForReuse()
        pokemonImageView.image = nil
        pokemonNameLabel.text = nil
    }

    func configure(with pokemon: Pokemon) {
        pokemonImageView.image = pokemon.picture
        pokemonNameLabel.text = pokemon.name
    }
    
}
import UIKit
import RxSwift
import RxCocoa
import SwiftyJSON
import PKHUD

class HomeViewController: UIViewController {

    @IBOutlet private weak var pokemonTextField: UITextField!
    @IBOutlet private weak var searchButton: UIButton!
    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var loadingView: UIActivityIndicatorView!

    private var pokemonList = [Pokemon]()
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        loadingView.isHidden = true
        setupTableView()
        bind()
    }

    private func setupTableView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UINib(nibName: "PokemonTableViewCell", bundle: nil), forCellReuseIdentifier: "PokemonTableViewCell")
    }

    private func bind() {
        searchButton.rx.tap.asSignal() // 検索ボタンを押した時
            .emit(onNext: { [weak self] in
                // テキストフィールドが空の場合
                if self?.pokemonTextField.text == "" {
                    HUD.flash(.label("Please enter Pokemon name"), delay: 1.0)
                    return
                }
                DispatchQueue.main.async {
                    self?.showLoadingView()
                }
                // API通信
                guard let searchTerm = self?.pokemonTextField.text?.lowercased(), let url = URL(string: "https://pokeapi.co/api/v2/pokemon/\(searchTerm)") else { return }
                let task = URLSession.shared.dataTask(with: url) { data, response, error in
                    if let data = data {
                        do {
                            let json = try JSON(data: data)
                            let name = json["name"].stringValue
                            let stats = json["stats"].arrayValue
                            let moves = json["moves"].arrayValue
                            let statsDict = stats.reduce(into: [String: Int]()) { dict, stat in
                                dict[stat["stat"]["name"].stringValue] = stat["base_stat"].intValue
                            }
                            let movesList = moves.map { $0["move"]["name"].stringValue }
                            if let sprites = json["sprites"].dictionary,
                               let pictureUrlString = sprites["front_default"]?.stringValue,
                               let pictureUrl = URL(string: pictureUrlString),
                               let pictureData = try? Data(contentsOf: pictureUrl),
                               let picture = UIImage(data: pictureData) {
                                let pokemon = Pokemon(name: name, picture: picture, stats: statsDict, moves: movesList)
                                self?.pokemonList.append(pokemon) // ポケモンリストに追加
                                DispatchQueue.main.async {
                                    self?.tableView.reloadData()
                                    self?.hideLoadingView()
                                    self?.pokemonTextField.text = ""
                                }
                            }
                        } catch {
                            print("DEBUG PRINT: \(error)")
                            DispatchQueue.main.async {
                                HUD.flash(.label("Pokemon was not found"), delay: 1.0)
                                self?.hideLoadingView()
                            }
                        }
                    }
                }
                task.resume()
            })
            .disposed(by: disposeBag)
    }

    private func showLoadingView() {
        loadingView.isHidden = false
        loadingView.startAnimating()
    }

    private func hideLoadingView() {
        loadingView.isHidden = true
        loadingView.stopAnimating()
    }

}

extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pokemonList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "PokemonTableViewCell", for: indexPath) as? PokemonTableViewCell else {
            return UITableViewCell()
        }
        let pokemon = pokemonList[indexPath.row]
        cell.configure(with: pokemon)
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60
    }
}