Skip to content

Instantly share code, notes, and snippets.

@mosluce
Last active January 14, 2017 04:41
Show Gist options
  • Save mosluce/a2783b9aa62a870a65c87a0f5fa8fae2 to your computer and use it in GitHub Desktop.
Save mosluce/a2783b9aa62a870a65c87a0f5fa8fae2 to your computer and use it in GitHub Desktop.
對於常用的 WebAPI 呼叫方式進行擴充 URLSession
/// 範例中使用了 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()
}
//
// 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