開發手機程式時,我們常常需要串接後端的 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
的請求方式。