開發手機程式時,我們常常需要串接後端的 REST APIs。目前常用的是 URLSessionDataTask 和 Alamofire。URLSession 是 iOS SDK 內建的;Alamofire 則是非常熱門的第三方套件。本章會講解如何透過它們串接 REST API 並取得 JSON 資料。
Table of Contents
URLSessionDataTask
URLSession 是 iOS SDK 中內建的類別,而它的好處就是不用再安裝第三方套件。
GET Request
使用 URLSession 時,我們要先準備好一個 URLRequest。把 URLRequest 中的 .httpMethod 設為 GET,而它是不區分大小寫的。然後,將要傳送的資料,直接設定在 ? 後面的 Query String。
guard let url = URL(string: "https://postman-echo.com/get?foo1=bar1&foo2=bar2") else {
print("Error: can not create URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "get"接下來,呼叫 URLSession.dataTask(with:) 來建立 URLSessionDataTask,並傳入 URLRequest 和 Response Handler 為參數。最後,記得要呼叫 URLSessionDataTask.resume() 來啟動這個 Task。
當它呼叫 Response Handler 時,它會給三個參數。如果第三個參數 error 不是 nil 的話,那就表示有錯誤發生。
第一個參數 data 是 RESTful API 回傳的 HTTP Body,它是 Data 型態的 JSON 字串。利用 JSONSerialization.jsonObject(with:) 把 data 轉換成 Dictionary 型態。
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print(error)
return
}
guard let data = data else {
print("Did not receive data")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let args = json["args"] as! [String: String]
print(args["foo1"]!)
print(args["foo2"]!)
} catch {
print("Error: can not convert data to JSON")
return
}
}
task.resume()POST Request
如同 GET Request,先準備好 URLRequest,並且把 .httpMethod 設為 POST。但是要傳送的資料,則要設定在 .httpBody。
因為我們要傳送的是 JSON 字串,所以在 URLRequest 的 Header 裡,要將 Content-Type 設為 application/json,後端才知道 HTTP Body 裡的是 JSON 字串 。 我們先把資料準備好成 Dictionary 型態,再用 JSONSerialization.data(withJSONObject:) 轉成 Data 型態的 JSON 字串後,才能給 .httpBody。
guard let url = URL(string: "https://postman-echo.com/post") else {
print("Error: can not create URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let data = [
"foo1": "bar1",
"foo2": "bar2",
]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: data)
} catch {
print(error)
return
}之後和 GET Request 一樣,呼叫 URLSession.dataTask(with:) 來傳送資料。Handler 裡也是相同的方式來處理資料。
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print(error)
return
}
guard let data = data else {
print("Did not receive data")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let args = json["data"] as! [String: String]
print(args["foo1"]!)
print(args["foo2"]!)
} catch {
print("Error: can not convert data to JSON")
return
}
}
task.resume()Alamofire
URLSessionDataTask 使用起來並不是很順手。例如,GET 的資料要自己手動串在 Query String。POST 的資料更麻煩,要把資料弄成 JSON 字串,再轉換成 Data 型態。此外,在 Handler 中資料的轉換處理也是都要自己來。
Alamofire 主要就是幫助我們處理這些資料格式的轉換。讓我們來看看它是怎麼運作的。
首先要先安裝 Alamofire 套件,如果你是使用 CocoaPods 的話,將這行放入你的 Podfile 中。
pod 'Alamofire', '~>5.2'
如果你不熟悉 CocoaPods 操作的話,可以參考這篇。
GET Request
我們把要傳送的資料準備好成 Dictionary 型態。呼叫 AF.request(),然後在 .responseJSON() 中傳入 Response Handler。
.responseJSON() 會先將回來的 JSON 字串轉成 Dictionary 型態,然後我們直接使用就可以了。
import Alamofire
let parameters = [
"foo1": "bar1",
"foo2": "bar2",
]
AF.request("https://postman-echo.com/get", method: .get, parameters: parameters)
.responseJSON { response in
switch response.result {
case .success(_):
let json = response.value as! [String: Any]
let args = json["args"]! as! [String: String]
print(args["foo1"]!)
print(args["foo2"]!)
case .failure(let error):
print(error)
}
}POST Request
發送 POST 的方式和 GET 是幾乎是一樣的。我們也要把 Content-Type 設為 application/json,以告訴後端是 JSON 字串。
另外,我們還要傳入 JSONEncoding.default。這是要告訴 Alamofire 將 parameters 轉成 JSON 字串。
let headers: HTTPHeaders = [
"Content-Type": "application/json"
]
let parameters = [
"foo1": "bar1",
"foo2": "bar2",
]
AF.request("https://postman-echo.com/post",
method: .post,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers)
.responseJSON { response in
switch response.result {
case .success(_):
let json = response.value as! [String: Any]
let args = json["data"]! as! [String: String]
print(args["foo1"]!)
print(args["foo2"]!)
case .failure(let error):
print(error)
}
}Alamofire 進階使用
剛剛介紹的 Alamofire 使用方式,已經相當地簡單化了 RESTful API 的請求。但是,問題是我們還是在用 Dictionary 型態來處理資料,而不是用物件 (Object)。讓我們修改剛剛的程式。
宣告 struct PostRequest 來表示要傳送的資料,並實作 Encodable。此外,我們還要為 Encodable 加入一個 Computed Properties – var dictionary: [String: Any]。它會把物件轉換成 Dictionary。
extension Encodable {
var dictionary: [String: Any] {
(try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))) as? [String: Any] ?? [:]
}
}
struct PostRequest: Encodable {
let foo1: String
let foo2: String
}宣告 struct PostResponse 和 struct PostResponseData 來表示回傳的資料,並實作 Decodable。
struct PostResponse: Decodable {
let data: PostResponseData
}
struct PostResponseData: Decodable {
let foo1: String
let foo2: String
}然後改用 .responseDecoablde(of:) 來處理回傳的資料,並告訴 Alamofire 將回傳的資料自動轉成 PostResponse 物件。最後的程式如下:
let headers: HTTPHeaders = [
"Content-Type": "application/json"
]
let parameters = PostRequest(foo1: "bar1", foo2: "bar2")
AF.request("https://postman-echo.com/post",
method: .post,
parameters: parameters.dictionary,
encoding: JSONEncoding.default,
headers: headers)
.responseDecodable(of: PostResponse.self) { response in
switch response.result {
case .success(let value):
print(value.data.foo1)
print(value.data.foo2)
case .failure(let error):
print(error)
}
}修改後的程式是不是更加地物件導向呢!
結語
REST API 是前後端溝通最流行的方式。在 iOS 開發時,我們必須要處理大量的 REST API。比起 URLSessionDataTask,Alamofire 使用起來更為便利。它不但幫我們轉換回傳的資料,還統一了 GET 和 POST 的請求方式。









