はじめに
AlamofireやAFNetworking、SwiftyJSONなどサードパーティ製のライブラリを一切使わずにAPIクライアントを実装してみました。
そのようなライブラリは便利な反面、サイズが大きくなってパフォーマンスが低下したり、アップデートが手間などのデメリットもあります。
決して 使い方がよくわからなかったから採用しなかったわけではありません 。
環境
- OS:macOS High Sierra 10.13.1
- Xcode:9.2
- Swift:4.0.3
APIクライアントの実装
GETメソッドのみ実装しています。
自分で必要になったらPOSTメソッドなども実装しようと思います。
import UIKit
/// HTTPクライアント
class HTTPClient: NSObject {
// MARK: Enumerations
/// HTTPメソッド
///
/// - get: GETメソッド
/// - post: POSTメソッド
/// - put: PUTメソッド
/// - delete: DELETEメソッド
private enum HTTPMethod: String {
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}
/// ヘッダー
///
/// - contentType: Content-Type
private enum HeaderField: String {
case contentType = "Content-Type"
}
/// Content-Type
///
/// - json: JSON
private enum ContentType: String {
case json = "application/json; charset=UTF-8"
}
// MARK: Properties
// MARK: Public Methods
/// GETメソッドを実行する
///
/// - Parameters:
/// - urlString: URL文字列
/// - queriesDictionary: クエリディクショナリ
/// - Returns: レスポンスボディ
static public func get(urlString: String, queriesDictionary: [String: String]?) -> Any {
let url = createUrl(urlString: urlString, queriesDictionary: queriesDictionary)
return runHTTPMethod(requestBody: nil, url: url, method: .get, contentType: .json)
}
// MARK: Private Methods
/// HTTPメソッドを実行する
///
/// - Parameters:
/// - requestBody: リクエストボディ
/// - url: URL
/// - method: HTTPメソッド
/// - contentType: Content-Type
/// - Returns: レスポンスボディ
static private func runHTTPMethod(requestBody: [String: Any]?, url: URL, method: HTTPMethod, contentType: ContentType) -> Any {
let request = createRequest(requestBody: requestBody, url: url, method: method, contentType: contentType)
// HTTPメソッドを実行する
var responseBody: Any! = nil
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
// レスポンスボディをJSONからデシリアライズして返す
responseBody = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
} catch let error {
print(error.localizedDescription)
fatalError()
}
}
task.resume()
// レスポンスを待つ
while responseBody == nil {
}
return responseBody!
}
/// リクエストを作成する
///
/// - Parameters:
/// - requestBody: リクエストボディ
/// - url: URL
/// - method: HTTPメソッド
/// - contentType: Content-Type
/// - Returns: リクエスト
static private func createRequest(requestBody: [String: Any]?, url: URL, method: HTTPMethod, contentType: ContentType) -> URLRequest {
var request = URLRequest(url: url)
request.httpMethod = method.rawValue
request.setValue(contentType.rawValue, forHTTPHeaderField:HeaderField.contentType.rawValue)
// リクエストボディをディクショナリからJSONへシリアライズする
if let _requestBody = requestBody {
do {
request.httpBody = try JSONSerialization.data(withJSONObject: _requestBody, options: [])
} catch let error {
print(error.localizedDescription)
fatalError()
}
}
return request
}
/// URLを作成する
///
/// - Parameters:
/// - urlString: URL文字列
/// - queriesDictionary: クエリディクショナリ
/// - Returns: URL
static private func createUrl(urlString: String, queriesDictionary: [String: String]?) -> URL {
var components = URLComponents(string: urlString)
if let _queriesDictionary = queriesDictionary {
components?.queryItems = createQueries(queriesDictionary: _queriesDictionary)
}
return (components?.url)!
}
/// クエリを作成する
///
/// - Parameter queriesDictionary: クエリディクショナリ
/// - Returns: クエリ配列
static private func createQueries(queriesDictionary: [String: String]) -> [URLQueryItem] {
var queries = [URLQueryItem]()
for query in queriesDictionary {
queries.append(URLQueryItem(name: query.key, value: query.value))
}
return queries
}
}
使い方
例
郵便番号検索APIを実行し、ディズ○ーランドの住所を取得する
import UIKit
/// ZipCloud APIクライアント
class ZipCloudAPIClient: NSObject {
// MARK: Properties
/// REST Endpoint URL
/// 参考:http://zipcloud.ibsnet.co.jp/doc/api
static private let restEndpointUrlString = "http://zipcloud.ibsnet.co.jp/api/search"
// MARK: Public Methods
static public func searchDisneylandAddress() {
let disneylandAddress: [String: Any] = getAddress(zipcode: "279-0031")
let results = disneylandAddress["results"] as! [[String: Any]]
let result = results[0]
print(result)
}
// MARK: Private Methods
static private func getAddress(zipcode: String) -> [String: Any] {
let queries = ["zipcode": zipcode]
return HTTPClient.get(urlString: restEndpointUrlString, queriesDictionary: queries) as! [String: Any]
}
}
["kana2": ウラヤスシ, "address1": 千葉県, "address3": 舞浜, "address2": 浦安市, "prefcode": 12, "kana1": チバケン, "zipcode": 2790031, "kana3": マイハマ]
ちなみにこのAPIを実行できるようになるまでに以下の2記事が書き上がりました。。
おわりに
Swiftに慣れていないため、適切でない実装箇所があると思います。(特に例外処理や enum
の使い方)
お気づきになりましたら、遠慮なくコメントや編集リクエストなどを頂けると嬉しいです。
あとすでにお気づきでしょうが、冒頭に伝えた言葉の一部に嘘があります。
Alamofireなどの使い方がよくわからなかったから自作しました 。
でも 学習コストがかかる のもライブラリ導入のデメリットではあるので、あながち間違った方向性ではないと思います。
「Alamofireを使えばもっとよく書ける!」という意見も大歓迎です。