LoginSignup
3
5

More than 3 years have passed since last update.

Swiftでapiを使って天気予報を取得しよう

Last updated at Posted at 2020-04-21

はじめに

初心者が学習を兼ねて、作ってみました。
暖かい目でお願いします。
変なところがあったら教えてください!

環境

Swift 5.1.2
Xcode 11.2

使用したお天気api
http://weather.livedoor.com/weather_hacks/webservice

Podをインストールする

Podfile

  pod "Alamofire" 
  pod "SwiftyJSON"

この2つのライブラリをインストールする事で、
簡単にHTTP通信と、JSONを扱うことができます。

Main.storyboardを作る

main.pngresult.png

こんな感じでパーツを配置します。
今回はpickerViewを使用して、地域を選択できるようにします。
あとはラベルとボタンを、いい感じに配置します。

ボタンを押すと、選択した地域の天気予報を取得し、次の画面でラベルに反映するようにします。
とてもシンプルです。

また、今回はNavigation Controllerを使用しています。
タブのEditor -> Embed In -> Navigation Controllerで使用できます。

実装する

まずは、pickerViewに入れるデータをmodelに書いておきます。

CityModel.swift

import Foundation

class CityModel {

    //地域の名前とURL追加するプロパティを持ちます。   
    let code: String
    let name: String

    init(cityCode:String, cityName:String) {
        code = cityCode
        name = cityName
    }
}
CityList.swift

import Foundation

class CityList {

    var list = [CityModel]()

    //Webサイトを見て適当に地域を追加します。
    init() {
        list.append(CityModel(cityCode: "016010", cityName: "札幌"))
        list.append(CityModel(cityCode: "015010", cityName: "室蘭"))
        list.append(CityModel(cityCode: "040010", cityName: "仙台"))
        list.append(CityModel(cityCode: "110010", cityName: "さいたま"))
        list.append(CityModel(cityCode: "130010", cityName: "東京"))
        list.append(CityModel(cityCode: "140010", cityName: "横浜"))
    }
}

次に、最初の画面の処理を書いていきます。
この画面では、選択したデータを次の画面に渡すだけです。

ViewController.swift

import UIKit

//pickerを使うので、DelegateとDataSourceを追加します。
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var pickerView: UIPickerView!
    @IBOutlet weak var resultButton: UIButton!

    //次のVCに渡す変数に初期値を入れておきます。
    var cityData:[String] = ["016010", "札幌"]

    //modelのデータを持ってきます。
    let cityList = CityList()    

    override func viewDidLoad() {
        super.viewDidLoad()

        //pickerを使うので、デリゲートを設定します。
        pickerView.delegate = self
        pickerView.dataSource = self        
    }

    //pickerの列の数を決めます。
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
           return 1
       }

    //pickerの行数を決めます。
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return cityList.list.count
    }

    //pickerに表示するデータを決めます。
    //ここでは取得したmodelのデータのnameを表示します。
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return cityList.list[row].name
    }

    //選択時の挙動を決めます。
    //次の画面に渡すために、取得したmodelのデータから、codeとnameを変数に入れます。
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        cityData = [cityList.list[row].code,cityList.list[row].name]
    }

    //ボタン押した時の処理を書きます。画面遷移です。
    @IBAction func resultButton(_ sender: Any) {
        performSegue(withIdentifier: "result", sender: nil)
    }

    //次の画面にデータを渡す処理を書きます。
    //prepareは、segueが動作するとViewControllerに通知してくれます。
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "result" {
            //resultViewController(次の画面)で作った変数に、pickerで選択した地域の情報を入れます。
            let resultVC = segue.destination as! ResultViewController
            resultVC.cityData = cityData
        }
    }   
}

次の画面を実装します。
この画面では、天気予報の取得とラベルに反映を行います。

resultViewContoller.swift

import UIKit

//Podからインスールしたライブラリを使うのでインポートします。
import Alamofire
import SwiftyJSON

//componentsを使用するのでインポートします。
import Foundation

class ResultViewController: UIViewController {

    @IBOutlet weak var cityNameLabel: UILabel!
    @IBOutlet weak var detailsTextView: UITextView!

    @IBOutlet weak var todayDateLabel: UILabel!
    @IBOutlet weak var todayWeatherLabel: UILabel!
    @IBOutlet weak var todayHighLabel: UILabel!
    @IBOutlet weak var todayLowLabel: UILabel!

    @IBOutlet weak var tomorrowDateLabel: UILabel!
    @IBOutlet weak var tomorrowWeatherLabel: UILabel!
    @IBOutlet weak var tomorrowHighLabel: UILabel!
    @IBOutlet weak var tomorrowLowLabel: UILabel!

    let highString = "最高気温:"
    let lowString = "最低気温:"

    //取得した天気予報のデータを入れます。
    var date:String = ""
    var weather:String = ""
    var high:String = ""
    var low:String = ""
    var details:[String] = []

    //ViewController(前の画面)に渡し、選択した地域のデータを入れます。
    var cityData:[String] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        getWeatherData(row: 0)
        getWeatherData(row: 1)
    }

    //天気情報を取得する。
    private func getWeatherData(row: Int) {

        //前の画面から渡されたデータのcodeを、urlに組み込みます。
        let url = "http://weather.livedoor.com/forecast/webservice/json/v1?city=\(cityData[0])"

        //Alamofireで通信します。データを取得します。
        AF.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON {(response) in

            switch response.result {
            case .success:

                //jsonを取得します。
                let json:JSON = JSON(response.data as Any)

                //取得したjsonから、必要なデータを取り出します。
                let date = json["forecasts"][row]["date"].string
                let weather = json["forecasts"][row]["telop"].string
                let high = json["forecasts"][row]["temperature"]["max"]["celsius"].string
                let low = json["forecasts"][row]["temperature"]["min"]["celsius"].string
                let details = json["description"]["text"].string

                //取り出したデータを、それぞれの変数に入れます。
                //データがない場合もあるので、その時は"No Data"と入れておきます。
                if date != nil {
                    self.date = String(date!.suffix(5))  //2025-04-10 -> 04-10 
                } else {
                    self.date = "No Data"
                }

                if weather != nil {
                    self.weather = weather!
                } else {
                    self.weather = "No Data"
                }

                if high != nil {
                    self.high = "\(high!)°"
                } else {
                    self.high = "No Data"
                }

                if low != nil {
                    self.low = "\(low!)°"
                } else {
                    self.low = "No Data"
                }

                if details != nil {
                    self.details = (details?.components(separatedBy: .newlines))!  //改行で分割します。
                } else {
                    self.details = ["No Details"]
                }

                //ラベルに反映させます。
                self.setWeatherData(row: row)

            case .failure(let error):
                print("-------- エラー ------")
                print(error)
            }
        }
    }

    //ラベルに反映させる処理です。
    //引数を与えて、todayとtomorrowで分岐させます。
    private func setWeatherData(row: Int) {

        if row == 0 {

            todayWeatherLabel.text = weather
            todayDateLabel.text = date
            todayHighLabel.text = highString + high
            todayLowLabel.text = lowString + low

            //分割時に配列になっているので、1つ目の要素だけ表示します。
            detailsTextView.text = details[0]

        } else if row == 1 {

            tomorrowWeatherLabel.text = weather
            tomorrowDateLabel.text = date
            tomorrowHighLabel.text = highString + high
            tomorrowLowLabel.text = lowString + low
        }
    }
}

完成

main2.pngresult2.png

コードの内容はさておき、天気予報のデータの取得に成功しました。

ViewDidLoad内でフォントサイズボタンの枠線を変更していますが、
長くなるので割愛しました。お好みでどうぞ。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5