【Xcode/SwiftUI】OpenWeatherのAPIを使ってお天気情報をリストで表示してみる

事前準備

APIキーの取得

事前にOpenWeatherのページで個人のAPIキーを取得しておきましょう (無料)

ライブラリのインストール

今回は、Alamofire, SwiftyJSONを使用します。(CoreLocationはXcodeに元から入っているのでインストール不要)

pod 'Alamofire'
pod 'SwiftyJSON', '~> 4.0'

コード

Model (モデル)

import Foundation

struct WeatherData {
    var temperature: Double // 気温
    var description: String // 説明
    var locationName: String // 場所

    init(temperature: Double, description: String, locationName: String) {
        self.temperature = temperature
        self.description = description
        self.locationName = locationName
    }

}

View (ビュー)

import SwiftUI

struct HomeView: View {
    @ObservedObject private var viewModel = HomeViewModel()

    var body: some View {
        NavigationView {
            VStack {
                List(viewModel.weatherData, id: \.temperature) { data in
                    VStack(alignment: .leading) {
                        let formattedTemperature = String(format: "%.1f", data.temperature) // 小数点第二位以降切り捨て
                        Text("\(data.locationName)")
                            .font(.title)
                        Text("\(formattedTemperature)℃")
                            .font(.title)
                        Text(data.description)
                            .font(.headline)
                    }
                }
                Button {
                    viewModel.weatherData.removeAll()
                    viewModel.getWeatherData()
                } label: {
                    Text("Refresh")
                        .font(.largeTitle)
                }
            }
            .navigationTitle("Weather")
            .onAppear {
                viewModel.getWeatherData()
            }
        }
    }
}

ViewModel (ビューモデル)

import SwiftUI
import CoreLocation
import Alamofire
import SwiftyJSON

class HomeViewModel: ObservableObject {
    @Published var weatherData: [WeatherData] = []
    private let location = CLLocation()
    private let geocoder = CLGeocoder()

    func getWeatherData() {
        let apiKey = "<#発行したAPIキー#>"
        let baseUrl = "https://api.openweathermap.org/data/2.5/weather?"
        let units = "metric"
        let count = 10

        for _ in 0..<count {
            let latitude = Double.random(in: -90...90) // ランダムな緯度
            let longitude = Double.random(in: -180...180) // ランダムな経度
            let url = "\(baseUrl)lat=\(latitude)&lon=\(longitude)&appid=\(apiKey)&units=\(units)"

            // APIリクエストを行う
            AF.request(url).responseJSON { response in
                switch response.result {
                case .success(let value):
                    // SwiftyJSONを使ってJSONをパースする
                    let json = JSON(value)
                    let temperature = json["main"]["temp"].doubleValue
                    let description = json["weather"][0]["description"].stringValue

                    // 位置情報から地名を取得する
                    self.geocoder.reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemarks, error in
                        if let placemark = placemarks?.first {
                            let locationName = placemark.locality ?? placemark.name ?? "Unknown Location"
                            let data = WeatherData(temperature: temperature, description: description, locationName: locationName)
                            self.weatherData.append(data)
                        } else {
                            let data = WeatherData(temperature: temperature, description: description, locationName: "Unknown Location")
                            self.weatherData.append(data)
                        }
                    }
                    
                case .failure(let error):
                    print(error)
                }
            }
        }
    }
}