APIから取得したJsonをParse
以前ハッカソンですごく苦しんだJsonのParseを克服したのでメモ
APIからデータを取得する
まずAPIからデータを取得します。今回はOpenWetherMapからデータを取得します。
ハッカソンの時はjsonのParseではなくこの取得の部分ですごく苦しみました。
できるのが当然であんまり詳しく解説しているサイトが見つけられませんでした…
APIへのアクセスコード
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=個人の取得したキー")!
let request = URLRequest(url: url)
var task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else{return}
do{
let jData = try JSONDecoder().decode(Root.self, from: data)
print(jData.coord.lat)
} catch let e {
print(e)
}
}
task.resume()
どかんと乗っけられたこのコードをそのまま理解できれば良いのですが、私は理解できませんでした。
まず、URLを指定して、APIにリクエストする定数を作っておきます。
let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=個人の取得したキー")!
let request = URLRequest(url: url)
クロージャ部分をわかりやすく分解して解説します。
taskはクロージャで内部でAPIとの処理を行います。引数で先ほど指定したURLの定数を渡しと、データ・レスポンス・エラーが帰ってきます。
データがない場合はガード節で弾いています。
var task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else{return}
}
あとはデータを取得しているdo-catchの中です。
なぜdo-catchがついているかなのですが、.decodeメソッドにthrousがくっついているからです。詳しくは下記の記事がわかりやすいです
猿がついに理解できたSwiftのthrow・do・try・catchの意味
JSONDecoderのdecode関数を使ってデータを取得しているわけですが、JSONSerializationなどの関数でもデータを保持できるようでこの辺がつまりました。
英語弱いのではじめdecodeがよくわかってませんでしたが、リファレンスを読むと、Jsonオブジェクトからデータに変換するメソッドだということがわかりまた。
引数には、Jsonデータを格納するための構造体と取得したデータを渡してあげます。構造体は下の方で解説します。
do{
let jData = try JSONDecoder().decode(Root.self, from: data)
print(jData.coord.lat)
} catch let e {
print(e)
}
これで、取れるだろうと思ってたかをくくっていたのですが、何度ビルドしても取得できずにハマっていました。
公式リファレンスを読んでいると、初期状態はサスペンドモード(省エネモード?)で動いていないので、動かす時はresumeを呼べと書いてありました。
なので、最後にresumeを呼び出して実行してあげています。
task.resume()
CodableでJsonのパース
Codableについては検索すればたくさん情報が出てくるので詳しく説明をする必要はないと思いますが、構造体にCodableプロパティを準拠させることでJsonを打ち込めます。
OpenWetherMapのURLをブラウザで検索すると画像のようなJsonが表示されます。
OpenWetherMapのデータの詳細はここから
これだと階層がよくわからないので、Jsonの構造を綺麗にしてくれるフォーマッターをかけてあげるとこうなります。
{
"coord": {
"lon": 139.32,
"lat": 35.66
},
"weather": [{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04n"
}],
"base": "stations",
"main": {
"temp": 291.32,
"pressure": 1019,
"humidity": 88,
"temp_min": 290.15,
"temp_max": 292.15
},
"visibility": 16093,
"wind": {
"speed": 1.16,
"deg": 326.002
},
"clouds": {
"all": 75
},
"dt": 1539608160,
"sys": {
"type": 1,
"id": 7622,
"message": 0.0069,
"country": "JP",
"sunrise": 1539550150,
"sunset": 1539590833
},
"id": 1863440,
"name": "Hachioji",
"cod": 200
}
あとは階層ごとにCodableを準拠した構造体をつくっていくだけです。
実際に値を取得する変数名がkeyと異なっていたり、型が違っているとえらーになってしまうので注意が必要です。
出来上がった構造体がこちらのコードになります。大元の一番大きな構造体に好きな名前をつけてあとは中にあるデータの名前と型を正確に作っていく簡単なお仕事です!
struct Root: Codable{
var coord: Coord
var weather: [Weather]
var base: String
var main: Main
var visibility: Int
var wind: Wind
var clouds: Clouds
var dt: Int
var sys: Sys
var id: Int
var name: String
var cod: Int
}
struct Coord: Codable {
var lon:Double
var lat:Double
}
struct Weather: Codable {
var id:Int
var main:String
var description:String
var icon:String
}
struct Main: Codable{
var temp: Double
var pressure: Int
var humidity: Int
var temp_min: Double
var temp_max: Double
}
struct Wind: Codable {
var speed: Double
var deg: Double
}
struct Clouds: Codable {
var all: Int
}
struct Sys: Codable {
var type: Int
var id: Int
var message: Double
var sunrise: Int
var sunset: Int
}
必要ないとは思いますが一応プロジェクトをGithabに乗っけておきました。
よければ動かしてデータが取得できていることを確認してみてください