Last active
January 14, 2017 04:41
-
-
Save mosluce/a2783b9aa62a870a65c87a0f5fa8fae2 to your computer and use it in GitHub Desktop.
對於常用的 WebAPI 呼叫方式進行擴充 URLSession
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 範例中使用了 SVProgressHUD, SwiftyJSON | |
/// 另外 UIAlertController 有 extension | |
func request(_ path: String, method: HttpMethod = .get, parameters: [String: Any]? = nil, localizedStatus: String? = NSLocalizedString("資料處理中", comment: "呼叫API讀取狀態文字"), _ completion: @escaping (_ json: JSON) -> Void) { | |
// 顯示 ActivityIndicator | |
if let status = localizedStatus { SVProgressHUD.show(withStatus: status) } | |
// 待會使用 GCD 版本所以要 Dispatch 到其他 thread | |
DispatchQueue.global().async { | |
// 基本的 header | |
var headers = ["Content-Type": "application/json"] | |
// token | |
if let token = UserDefaults.standard.string(forKey: udkAuthToken) { | |
headers["Authorization"] = "JWT \(token)" | |
} | |
do { | |
// 使用 URLSession extension 功能 | |
let data = try URLSession.ephemeral().request(self.baseUrl.appending(path), method: method, headers: headers, parameters: parameters) | |
DispatchQueue.main.async { | |
// 完成 | |
completion(JSON(data: data)) | |
} | |
} catch let error as HttpError { | |
// 處理 Http Errors | |
switch error { | |
case .unAuthorized: | |
// 實作驗證失效邏輯 -> 顯示登入畫面 | |
NotificationCenter.default.post(name: .didAuthFailure, object: nil) | |
break | |
default: | |
// 實作其他錯誤的對應邏輯 -> 顯示錯誤訊息 (每個狀態都有對應錯誤訊息) | |
NotificationCenter.default.post(name: .presentNotifyStatus, object: nil, userInfo: [kGUNotificationUserInfoMessageKey: error.localizedDescription, kGUNotificationUserInfoStatusKey: NotifyStatus.error]) | |
break | |
} | |
} catch { | |
// 非 Http Error 類 | |
UIAlertController(error: error).present() | |
} | |
DispatchQueue.main.async { | |
// 隱藏 ActivityIndicator | |
SVProgressHUD.dismiss() | |
} | |
} | |
// let config = URLSessionConfiguration.ephemeral | |
// let session = URLSession(configuration: config) | |
// | |
// var req = URLRequest(url: URL(string: baseUrl.appending(path))!) | |
// | |
// req.httpMethod = method.rawValue | |
// req.addValue("application/json", forHTTPHeaderField: "Content-Type") | |
// | |
// if let token = UserDefaults.standard.string(forKey: udkAuthToken) { | |
// req.addValue("JWT \(token)", forHTTPHeaderField: "Authorization") | |
// } | |
// | |
// if let parameters = parameters, let data = try? JSONSerialization.data(withJSONObject: parameters, options: [.prettyPrinted]) { | |
// req.httpBody = data | |
// } | |
// | |
// | |
// | |
// if let status = localizedStatus { SVProgressHUD.show(withStatus: status) } | |
// session.dataTask(with: req) {(data, res, error) in | |
// SVProgressHUD.dismiss() | |
// | |
// var json: JSON? | |
// | |
// if let data = data { | |
// json = JSON(data: data) | |
// } | |
// | |
// DispatchQueue.main.async { | |
// if error != nil { | |
// let message = NSLocalizedString("發生錯誤 (0)", comment: "客戶端端發生錯誤") | |
// NotificationCenter.default.post(name: .presentNotifyStatus, object: nil, userInfo: [kGUNotificationUserInfoMessageKey: message, kGUNotificationUserInfoStatusKey: NotifyStatus.error]) | |
// return completion(nil) | |
// } | |
// | |
// if let res = res as? HTTPURLResponse { | |
// if res.statusCode >= 400 { | |
// switch res.statusCode { | |
// case 400: | |
// if let json = json { | |
// let message = json["message"].stringValue | |
// NotificationCenter.default.post(name: .presentNotifyStatus, object: nil, userInfo: [ | |
// // 來自伺服器的訊息 | |
// kGUNotificationUserInfoMessageKey: NSLocalizedString(message, comment: ""), | |
// kGUNotificationUserInfoStatusKey: NotifyStatus.error | |
// ]) | |
// return completion(nil) | |
// } else { | |
// NotificationCenter.default.post(name: .presentNotifyStatus, object: nil, userInfo: [ | |
// kGUNotificationUserInfoMessageKey: NSLocalizedString("傳送資料格式錯誤", comment: "傳送資料格式錯誤"), | |
// kGUNotificationUserInfoStatusKey: NotifyStatus.error | |
// ]) | |
// return completion(nil) | |
// } | |
// case 401: | |
// NotificationCenter.default.post(name: .didAuthFailure, object: nil) | |
// return completion(nil) | |
// default: | |
// print(res.statusCode) | |
// NotificationCenter.default.post(name: .presentNotifyStatus, object: nil, userInfo: [ | |
// kGUNotificationUserInfoMessageKey: NSLocalizedString("伺服器正在維修中", comment: "伺服器無法連線"), | |
// kGUNotificationUserInfoStatusKey: NotifyStatus.error | |
// ]) | |
// return completion(nil) | |
// } | |
// } | |
// } | |
// | |
// completion(json) | |
// } | |
// }.resume() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// URLSessionExtension.swift | |
// | |
// 簡化 URLSession 使用時初始化的工作量 | |
// | |
// Created by 默司 on 2017/1/12. | |
// Copyright © 2016年 默司. All rights reserved. | |
// | |
enum HttpMethod: String { | |
case get = "GET" | |
case post = "POST" | |
case put = "PUT" | |
case delete = "DELETE" | |
} | |
enum HttpError: Error { | |
case badRequest(String) | |
case unAuthorized | |
case forbidden | |
case notFound | |
case internalServerError | |
case badGateway | |
case serviceUnavailable | |
case userNetworking | |
var localizedDescription: String { | |
switch self { | |
case .badRequest(let reason): | |
return NSLocalizedString(reason, comment: "") | |
case .unAuthorized: | |
return NSLocalizedString("授權無效", comment: "") | |
case .forbidden: | |
return NSLocalizedString("禁止訪問", comment: "") | |
case .notFound: | |
return NSLocalizedString("服務端點不存在", comment: "") | |
case .internalServerError: | |
return NSLocalizedString("伺服器異常", comment: "") | |
case .badGateway: | |
return NSLocalizedString("伺服器閘道異常", comment: "") | |
case .serviceUnavailable: | |
return NSLocalizedString("無法連線到伺服器", comment: "") | |
default: | |
return NSLocalizedString("網路連線錯誤", comment: "") | |
} | |
} | |
} | |
extension URLSession { | |
/// 建立新的 URLSession 實體,不使用快取 | |
/// | |
/// - Returns: | |
static func ephemeral() -> URLSession { return URLSession(configuration: URLSessionConfiguration.ephemeral) } | |
/// 建立新的 URLSession 實體,使用快取 | |
/// | |
/// - Returns: | |
static func `default`() -> URLSession { return URLSession(configuration: URLSessionConfiguration.default) } | |
/// 通用請求 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - method: | |
/// - headers: | |
/// - parameters: 參數 | |
/// - completion: | |
/// - Returns: | |
@discardableResult func request(_ urlString: String, method: HttpMethod, headers: [String: String]? = nil, parameters: [String: Any]? = nil, _ completion: @escaping (_ data: Data?, _ res: HTTPURLResponse?, _ error: Error?) -> Void) -> URLSession { | |
var req = URLRequest(url: URL(string: urlString)!) | |
req.httpMethod = method.rawValue | |
if let parameters = parameters, let data = try? JSONSerialization.data(withJSONObject: parameters, options: [.prettyPrinted]) { | |
req.httpBody = data | |
} | |
if let headers = headers { | |
Array(headers.keys).forEach({ (key) in | |
req.setValue(headers[key], forHTTPHeaderField: key) | |
}) | |
} | |
self.dataTask(with: req) { completion($0, $1 as? HTTPURLResponse, $2) }.resume() | |
return self | |
} | |
/// GET 請求 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - completion: | |
/// - Returns: | |
@discardableResult func get(_ urlString: String, headers: [String : String]? = nil, _ completion: @escaping (_ data: Data?, _ res: HTTPURLResponse?, _ error: Error?) -> Void) -> URLSession { | |
return self.request(urlString, method: .get, headers: headers, completion) | |
} | |
/// POST 請求 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - parameters: 參數 | |
/// - completion: | |
/// - Returns: | |
@discardableResult func post(_ urlString: String, headers: [String : String]? = nil, parameters: [String: Any]? = nil, _ completion: @escaping (_ data: Data?, _ res: HTTPURLResponse?, _ error: Error?) -> Void) -> URLSession { | |
return self.request(urlString, method: .post, headers: headers, parameters: parameters, completion) | |
} | |
/// PUT 請求 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - parameters: 參數 | |
/// - completion: | |
/// - Returns: | |
@discardableResult func put(_ urlString: String, headers: [String : String]? = nil, parameters: [String: Any]? = nil, _ completion: @escaping (_ data: Data?, _ res: HTTPURLResponse?, _ error: Error?) -> Void) -> URLSession { | |
return self.request(urlString, method: .put, headers: headers, parameters: parameters, completion) | |
} | |
/// DELETE 請求 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - completion: | |
/// - Returns: | |
@discardableResult func delete(_ urlString: String, headers: [String : String]? = nil, _ completion: @escaping (_ data: Data?, _ res: HTTPURLResponse?, _ error: Error?) -> Void) -> URLSession { | |
return self.request(urlString, method: .delete, headers: headers, completion) | |
} | |
/// 通用請求,GCD 同步版 | |
/// | |
/// 目前支援JSON格式、String格式的錯誤訊息 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - parameters: 參數 | |
/// - completion: | |
/// - Returns: | |
/// - Throws: HTTP ERROR | |
func request(_ urlString: String, method: HttpMethod, headers: [String: String]? = nil, parameters: [String: Any]? = nil) throws -> Data { | |
let semaphore = DispatchSemaphore(value: 0) | |
var statusCode: Int = 0 | |
var data: Data? | |
var error: Error? | |
self.request(urlString, method: method, headers: headers, parameters: parameters) { (d, r, e) in | |
statusCode = r?.statusCode ?? 0 | |
data = d | |
error = e | |
semaphore.signal() | |
} | |
semaphore.wait() | |
if let error = error { throw error } | |
if statusCode >= 400 { | |
switch statusCode { | |
case 400: | |
let unknown = NSLocalizedString("輸入參數錯誤", comment: "") | |
guard let data = data else { | |
throw HttpError.badRequest(unknown) | |
} | |
// JSON 格式支援 | |
let json = (try? JSONSerialization.jsonObject(with: data, options: .mutableContainers)) as? [String: Any] | |
var message = json?["message"] as? String | |
if message == nil { | |
// 文字格式支援 | |
message = String.init(data: data, encoding: .utf8) | |
} | |
let reason = NSLocalizedString(message ?? "輸入參數錯誤" , comment: "") | |
throw HttpError.badRequest(reason) | |
case 401: | |
throw HttpError.unAuthorized | |
case 403: | |
throw HttpError.forbidden | |
case 404: | |
throw HttpError.notFound | |
case 500: | |
throw HttpError.internalServerError | |
case 502: | |
throw HttpError.badGateway | |
case 503: | |
throw HttpError.serviceUnavailable | |
default: | |
throw HttpError.userNetworking | |
} | |
} | |
return data! | |
} | |
/// GET 請求,GCD 同步版 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - Returns: | |
/// - Throws: HTTP ERROR | |
func get(_ urlString: String, headers: [String : String]? = nil) throws -> Data { | |
return try self.request(urlString, method: .get, headers: headers) | |
} | |
/// POST 請求,GCD 同步版 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - parameters: 參數 | |
/// - Returns: | |
/// - Throws: HTTP ERROR | |
func post(_ urlString: String, headers: [String : String]? = nil, parameters: [String: Any]? = nil) throws -> Data { | |
return try self.request(urlString, method: .post, headers: headers, parameters: parameters) | |
} | |
/// PUT 請求,GCD 同步版 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - parameters: 參數 | |
/// - Returns: | |
/// - Throws: HTTP ERROR | |
func put(_ urlString: String, headers: [String : String]? = nil, parameters: [String: Any]? = nil) throws -> Data { | |
return try self.request(urlString, method: .put, headers: headers, parameters: parameters) | |
} | |
/// DELETE 請求,GCD 同步版 | |
/// | |
/// - Parameters: | |
/// - urlString: 網址 | |
/// - headers: | |
/// - Returns: | |
/// - Throws: HTTP ERROR | |
func delete(_ urlString: String, headers: [String : String]? = nil) throws -> Data { | |
return try self.request(urlString, method: .delete, headers: headers) | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment