はじめに
SwiftでOpenWeatherAPIを使って天気予報アプリを作ってみたいと思います。
初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。
まず完成形はこちら!
では始めていきます。ぜひ最後までご覧ください。
UIの設計
まずこのアプリでは以下の画像を使ったのでここからダウンロードしてください。
このように配置していきます。
WeatherViewControllerを作り、IBOutlet,IBAction接続します。
class WeatherViewController: UIViewController{
@IBOutlet weak var conditionImageView: UIImageView!
@IBOutlet weak var temparatureLabel: UILabel!
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var searchTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func searchPressed(_ sender: UIButton) {
}
}
全体設計
APIの取得
まず、APIの取得からやっていきたいと思います。
OpenWeatherAPIを使います。
こちらでサインインして、APIKeyを取得します。
今回はCurrentWeatherDataを使います。
こちらでこのようにAPIを叩くと、JSONデータを変換してくれます。
これらのデータをうまく使い今回はアプリを作成していきます。
WeatherManager
今回のAPIにおいてのロジックを管理するWeatherManagerを書いていきます。
import Foundation
//UI更新のためのプロトコル
protocol WeatherManagerDelegate {
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel)
func didFailWithError(error: Error)
}
struct WeatherManager {
//[APIKey]には自分のAPIKeyをかく
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=[APIKey]&units=metric"
var delegate: WeatherManagerDelegate?
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(with: urlString)
}
func performRequest(with urlString: String) {
func performRequest(with urlString:String){
//①URL型に変換
if let url = URL(string: urlString){
//②URLSessionを作る
let session = URLSession(configuration: .default)
//③Session taskを与える
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(safeData){
self.delegate?.didUpdateWeather(self, weather: weather)
}
}
}
//④タスクが始まる
task.resume()
}
}
func parseJSON(_ weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
//JSONを変換
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
//データをアプリで使いやすいようにまとめる
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
}
WeatherModel
データをアプリが使いやすいような形に変換するためのWeatherModelを作成していきます。
import Foundation
struct WeatherModel {
let conditionId:Int
let cityName:String
let temperature:Double
var temperatureString:String {
return String(format: "%.1f",temperature)
}
var conditinName:String{
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud-rain"
case 600...622:
return "cloud-snow"
case 701...781:
return "cloud-fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}
WeatherData
レスポンスしたデータをデコードするためWeatherDataを作ります。
import Foundation
//Codable = Decodable & Encodable
struct WeatherData : Codable{
let name:String
let main:Main
let weather:[Weather]
}
struct Main:Codable {
let temp:Double
}
struct Weather:Codable {
let description:String
let id:Int
}
WeatherViewController
最後に取得したデータをViewに反映させる、またTableViewの操作のためにWeatherViewControllerを作っていきます。
import UIKit
class WeatherViewController: UIViewController {
@IBOutlet weak var conditionImageView: UIImageView!
@IBOutlet weak var temperatureLabel: UILabel!
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var searchTextField: UITextField!
var weatherManager = WeatherManager()
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
searchTextField.delegate = self
}
}
//MARK: - UITextFieldDelegate
extension WeatherViewController: UITextFieldDelegate {
@IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "何か入力してください"
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
weatherManager.fetchWeather(cityName: city)
}
searchTextField.text = ""
}
}
//MARK: - WeatherManagerDelegate
extension WeatherViewController: WeatherManagerDelegate {
func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) {
DispatchQueue.main.async {
//ここでUIの更新
self.temperatureLabel.text = weather.temperatureString
self.conditionImageView.image = UIImage(systemName: weather.conditionName)
self.cityLabel.text = weather.cityName
}
}
func didFailWithError(error: Error) {
print(error)
}
}
UITextFieldの処理はUITextFieldのドキュメントを確認しながら学んでください。