お天気APIを叩こう！(Swift・OpenWeatherMap)

#はじめに
今回は、Swiftで[OpenWeatherMap](https://openweathermap.org/)というAPIを叩いて指定した緯度経度の天気情報を取得し、天気情報を表示させようと思います。
この記事を読むことで操作を理解し、APIに関する理解を深められたら幸いです。

#用いるAPIの紹介
今回使うAPIは、[OpenWeatherMap](https://openweathermap.org/)という無料で世界中の天気予報などの情報を提供してくれる物を使います。
※一部課金が必要な機能もあります。

#完成予想図
今回は、サンプルとしてOpenWeatherMapを用いて取得した天気情報を表示させます。
![result2](https://user-images.githubusercontent.com/64241438/102870114-ec3eb500-447f-11eb-9258-8c151a7e2784.gif)




#初めてOpenWeatherMapを利用する場合
API Keyの取得にはアカウントの作成が必要です。
まだアカウントを作成していない方は、こちらの記事を参考にしてアカウントを作成し、自身のAPIキーを取得してください。
[無料天気予報APIのOpenWeatherMapを使ってみる](https://qiita.com/nownabe/items/aeac1ce0977be963a740)


#APIのやりとりの仕組み
さらっとAPIのやり取りについての仕組みを説明します。
APIの定義はないですが、HTTPプロトコルを用いてネットワーク越しに呼び出すアプリケーション間、システム間のインターフェースのことを指すことが多いです。(下図参照)
<img src="https://user-images.githubusercontent.com/64241438/113119346-50c19b00-924b-11eb-9bf8-8c2ad98476d1.png" width="">

特に、欲しいデータを取得するレスポンスと呼ばれるものは主にJSON型で返ってきます。
<img src="https://user-images.githubusercontent.com/64241438/113119901-ebba7500-924b-11eb-86a0-ce66db3bf803.png" >

このJSON型で返ってきたデータから欲しい情報を抜き出して表示させたりすることができます。
今回はさらっとですが詳しく知りたい方は[Web APIとは何なのか](https://qiita.com/NagaokaKenichi/items/df4c8455ab527aeacf02)を読んでみてください！

#JSONを見てみよう
では、レスポンスのJSON形式で表示されるデータを見てみましょう！
その前に、まずはリクエスト用のクエリを発行する必要があります。

`pro.openweathermap.org/data/2.5/forecast/hourly?lat={lat}&lon={lon}&appid={API key}`

このURLの`appid=`というところに、自分のAPI Keyを入力し、`lat=`と`lon=`のところに天気の情報が欲しい地区の緯度経度を入力します。
そしてクエリの入力をしたURLをブラウザなどに入れてみると、以下のように現在の天気の情報が表示されると思います。
<img src="https://user-images.githubusercontent.com/64241438/113122641-c9762680-924e-11eb-8c5c-63de3cf00fe8.png">
あれ？なんか思ってたのと違いますね汗
ごちゃごちゃしててよくわからないですが実はこれちゃんと取得できています。

これを綺麗にして可視化できるようにGoogle Chromeの[JSON View Awesome](https://chrome.google.com/webstore/detail/json-viewer/efknglbfhoddmmfabeihlemgekhhnabb)という拡張機能を使ってみようと思います！
<img src="https://user-images.githubusercontent.com/64241438/113123494-9c764380-924f-11eb-84c7-5f3132d71797.png">
するとこんな感じに綺麗に表示させることができました！
さらに整理してみると、、？
<img src="https://user-images.githubusercontent.com/64241438/113123839-e2330c00-924f-11eb-9ef1-a1147d9f2bb7.png">
39個の天気データがあることがわかりました！！
これでJSON型のデータを見ることができました。

#今回使うライブラリの紹介
SwiftでAPIを利用しようとすると操作の意図がわかりにくい複雑なコードが多く、とても大変です。
そこで今回は2つのライブラリを用いることでコードをできるだけシンプルにわかりやすく書いていこうと思います。
今回用いるライブラリは、
[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
`少ない記述量で JSONデータを取得でき、また直感的にJSON内 の欲しい値をゲットすることができるライブラリ`

[Alamofire](https://github.com/Alamofire/Alamofire)
`ネットワーク通信(http~)をシンプルに記述することができるライブラリ`

の二つになります。
cocoapodsでインストールしましょう。

#実際にコードを書いてみよう！！

####①リクエスト(http~)を発行しよう！！
まず最初に、データを取得するためのリクエストURLを作ります。

```swift
let text = "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&units=metric&appid=自分の発行したAPIKey"
```

`https://api.openweathermap.org/data/2.5/weather?` ・・・発行元が決めてるから変えられない
`?lat=\(latitude)&lon=\(longitude)&units=metric` ・・・今回は緯度経度をパラメーターとする

今回は、蔵王温泉スキー場の天気を取得しようと思うので`latitude=38.1705275`、`longitude=140.417219`とします。

このtextは、ただのString型であるのでリクエストとして使える型に変換する必要があります。
そこで、

```swift
let url = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
```
と書くことで、先ほど定義したtextが新たにurlとしてリクエストに使える型に再定義されました。

####②APIコール処理
①で作成したurlを使って実際にAPIのコール処理を実装します。
ここで、先ほどインポートしたライブラリであるAlamofireを用います。

```swift
AF.request(url!, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON { (response) in
            switch response.result {
            case .success:
            case .failure(let error):
            }
        }
```
Alamofireを用いることでAPIコール処理を簡潔に実装できます。
標準と比較した記事は[こちら](https://qiita.com/uhooi/items/c0e083f916dc516175bd)

####③レスポンス後の処理
まずコードを提示します。

```swift
 switch response.result {
            case .success:
                let json = JSON(response.data as Any)
                self.descriptionWeather = json["weather"][0]["main"].string!
                self.max.text = "\(Int(json["main"]["temp_max"].number!).description)℃"
                self.min.text = "\(Int(json["main"]["temp_min"].number!).description)℃"
                self.taikan.text = "\(Int(json["main"]["temp"].number!).description)℃"                
                self.wind.text = "\(Int(json["wind"]["speed"].number!).description)m/s"

            case .failure(let error):
                print(error)
            }
```
まず、天気情報結果を代入している値response.dataをJSONとしてjsonを作成します。
次に解析を行います。
ここで、インポートしたもう一つのライブラリであるSwiftyJSONが役立ちます。
天気だけの情報が欲しい時は`json["weather"][0]["main"].string!`のように指定することで値を取得でします。ほんとシンプルでかわかりやすいですよね？
<img src="https://user-images.githubusercontent.com/64241438/116104388-5e076380-a6eb-11eb-98f9-9a96539e3fd1.png" width="">
こんな感じでマトリョーシカみたいなイメージで欲しいデータだけを引っ張れます。(この画像は別のプロダクトで撮った写真ですが、、)

####④表示させる
あとは、抽出したデータを煮るなり焼くなりするだけなんでサンプルコードを提示して終わりにしますね！！

```swift
import UIKit
import Alamofire
import SwiftyJSON
import KRProgressHUD

class WeatherViewController: UIViewController, UISearchBarDelegate {
    
    @IBOutlet var tenkiImageView: UIImageView!
    @IBOutlet var max: UILabel!
    @IBOutlet var min: UILabel!
    @IBOutlet var taikan: UILabel!
    @IBOutlet var humidity: UILabel!
    @IBOutlet var wind: UILabel!
    @IBOutlet var backImageView : UIImageView!
    
    
    var descriptionWeather: String?
    var cityData : [String] = []
    var selectedCity : String!
    var longitude : Double!
    var latitude : Double!
    
  
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.addBackground(name: "snow-mountain.jpg")
        

      backImageView.layer.cornerRadius = backImageView.frame.size.width * 0.1
      backImageView.clipsToBounds = true
        tenkiImageView.layer.cornerRadius = tenkiImageView.frame.size.width * 0.1
        tenkiImageView.clipsToBounds = true
        
        yoho()
    }

    func yoho() {
        let latitude = String(self.latitude)
        let longitude = String(self.longitude)
        let text = "https://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)&units=metric&appid=55b317379a06a94f5198e9c297ff0b0e"

        
        let url = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
        AF.request(url!, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON { (response) in
            switch response.result {
            case .success:

                let json = JSON(response.data as Any)

                self.descriptionWeather = json["weather"][0]["main"].string!

                if self.descriptionWeather == "Clouds" {
                    self.tenkiImageView.image = UIImage(named: "kumori")
                }else if self.descriptionWeather == "Rain" {
                    self.tenkiImageView.image = UIImage(named: "ame")
                }else if self.descriptionWeather == "Snow"{
                    self.tenkiImageView.image = UIImage(named: "yuki.gif")
                }else {
                    self.tenkiImageView.image = UIImage(named: "hare")
                }

                self.max.text = "\(Int(json["main"]["temp_max"].number!).description)℃"
                self.min.text = "\(Int(json["main"]["temp_min"].number!).description)℃"
                self.taikan.text = "\(Int(json["main"]["temp"].number!).description)℃"
                self.wind.text = "\(Int(json["wind"]["speed"].number!).description)m/s"
                
            case .failure(let error):
                print(error)
            }
        }
    }
    
}
```

#終わりに
以上で終わりとなります。
もしかしたら間違いなどがあるかもしれないのであったらご指摘ください。
今回の記事を作成するにあたって
[SwiftでAPIを使う！　自分がつまずいたところを詳しく調べてみた](https://qiita.com/groot/items/c1f871ecca1ea35a5886)
を参考にさせて頂きました。是非こちらもご覧ください。
是非みなさんもお天気表示してみてくださいね！