Swift — 來接個 API 吧( 串接 API & 解析 JSON )

使用Swift 中的 Decodable 協議方式,來串接 API 並解析其 JSON。

Jeremy Xue
9 min readAug 28, 2018

前言:

前面兩篇文章有介紹到 Swift 中的 Codable 協議以及簡單介紹了 Open Data 的 API 基本介紹,以及其說明文件。若是還沒看過的可以先翻閱前兩篇文章再來開始實作。

  • Swift — 說說 Codable ( Decodable & Encodable )
  • Swift — 來接個 API 吧( Open data )

實作

1. 定義 Decodable 的結構

基本上我們看到我們的 JSON 檔之後,就可以開始來定義我們的 struct 要有哪些屬性值,基本上使用 Codable 方式定義時,我們的 key 值跟其類型必須與 JSON 上相同,而且資料只能相同或是更少,或者可以透過 Optional 來處理這種不確定資料是否存在的情況。

並且要看清楚 {} 以及 [],這兩者的定義方式是不同的,前者是結構 Struct,後者是陣列 Array,所以在定義的時候需要注意!

接著我們就來定義我們的 struct ,首先先看我們第一層的結構:

接著我們來定義資料結構構,並使其遵循 Decodable 或是 Codable 的協議這邊可以依照個人需求串接想要的資料即可,記得屬性名稱及類型必須與 JSON 文件上相同。

struct CurrentWeatherData: Decodable {
var name: String
var id: Int
var dt: TimeInterval
// dt 時間格式是 TimeInterval。
// 在這邊雖然類型使用 Int 也能取到值,但意思上還是有些差距。
}

屬性只需要其名稱與類型宣告一致即可,接著我們需要定義其中其它的結構。這邊我們先以座標位置做示範,因此我們會定義一個 coord 的結構,並且在 CurrentWeatherData 的結構中新增一個類型為 Coord 的屬性。

// 定義座標結構,別忘了也要遵循 Decodable 協議
struct Coord: Decodable {
var lon: Double
var lat: Double
}

接著我們還需要將它加到我們 Current Weather Data 的結構中:

struct CurrentWeatherData: Decodable {
var name: String
var id: Int
var dt: TimeInterval
// 新增的座標結構
var coord: Coord
}

其他結構定義方式也是如上,這邊我們再加上 main 的結構,就不再多說明一次。當然你也可以省略其中的某個屬性:

struct Main: Decodable {
var temp: Double
var humidity: Int
var temp_min: Double
var temp_max: Double
//這邊我們省略了 pressure 屬性
}

接著你會發現 JSON 中的 weather 類型可能讓你不知道怎麼定義它:

但簡單來說它就是一個特定類型的陣列,而資料前的數字 0 ,就表示它是此陣列的第一筆資料,因為我們陣列資料只有一筆,所以可能無法實際體會。接著我們一樣來定義我們 weather 的結構:

struct Weather: Decodable {
var icon: String
var main: String
var description: String
}

接著,因為其類型是陣列,前面也有提到就是一個特定類型的陣列,所以我們放到 Current Weather Data 如下:

struct CurrentWeatherData: Decodable {
var name: String
var id: Int
var dt: TimeInterval
var coord: Coord
var main: Main
var weather: [Weather]
}

來看一下我們 Current Weather Data 的整體定義結構吧:

這些定義的結構不一定要與我相同,依照個人的需求定義即可,接下來我們要來實際印出我們的資料試試看。

2. 解析 JSON 中的資料

定義好結構後,接下來我們就要開始串接我們的 API 了。這邊我們使用城市名稱( city name )來調用我們的 API。因此這邊我們會定義一個帶有城市名稱 getWeatherData( city:) 的方法。

func getWeatherData(city:String) {
// Get weather data
}

接下來我們要組合我們的 URL,我們的 URL 的組合如下:

https://api.openweathermap.org/data/2.5/weather?q={城市名稱}&appid={你的 API Key}https://api.openweathermap.org/data/2.5/weather?q=Taipei&appid={你的 API Key}

當組合好的時候可以貼到網址列測試,如果成功表示這條 URL 是有效的:

取得 Taipei 的天氣資訊

接下來我們就可以使用 URLSession 來取得 API 的數據:

這時我們可以來測試能不能取得我們特定城市( Taipei )的天氣資訊,我們在 ViewDidLoad 呼叫它:

getWeatherData(city: "Taipei")
印出的解析後的天氣數據

可以看出印出的結果就是我們所定義遵循 Decodable 的結構,我們也能個別取出其中的數據來運用,就像是取用 Model 中的屬性值而已。

print("城市名稱: \(weatherData.name)")
print("經緯度: (\(weatherData.coord.lon),\(weatherData.coord.lat))")
print("溫度: \(weatherData.main.temp)°K")
print("描述: \(weatherData.weather[0].description)")

額外補充:

上面接下來的數據,可能某些部分格式可能不是我們想要的:例如它的溫度單位不是我們想要的格式,或是描述的語言是英文的。在這個 API 中也能透過一些參數來改變其資料格式。

溫度單位格式:

其中有提到:

  • 想要換算成 Fahrenheit,加上 units=imperial
  • 想要換算成 Celsius,加上 units=imperial
  • 溫度的預設單位格式為 Kelvin,不需加上 units 參數來調用
Examples of API calls:standard: api.openweathermap.org/data/2.5/find?q=London
metric: api.openweathermap.org/data/2.5/find?q=London&units=metric
imperial: api.openweathermap.org/data/2.5/find?q=London&units=imperial

多國語言支持:

你可以從文件中的描述來看見它支持了哪些國家的語系,如果描述中沒有該國家的語系則無法進行轉換。假如我們要轉換成繁體中文,就加上一個參數 lang=zh_tw,即可轉換成繁體中文。

Examples of API calls: http://api.openweathermap.org/data/2.5/forecast/daily?id=524901&lang=zh_cn

所以我們假如要換成攝氏溫度,並且將語系換成繁體中文,那麼我們的網址列如下:

// 別忘了參數之間都要加上一個 "&" 符號
https://api.openweathermap.org/data/2.5/weather?q=Taipei&units=metric&lang=zh_tw&appid={你的API Key}

接著再使用我們上面的 getWeatherData(city:) 方式來調用它,查看結果:

轉換後的數據資料

後記:

希望透過這次的教學能讓大家能夠了解 API ,且使用 Swift 中 Codable 方式來串接 API 中的 JSON 檔,並且將串接到的數據應用在自己的 APP 中。

附上本次實作 github 連結:

記得去申請帳號獲取你的 API key。

記得去申請帳號獲取你的 API Key 啊!不要問我為什麼都是 Invalid URL 啊~~

本次實作只有印出數據,未來有時間再實作在應用上,最近被案子塞滿滿發文率降低了,請多包涵 😏

--

--

Jeremy Xue

Hi, I’m Jeremy. [好想工作室 — iOS Developer]