随着Alamofire5.0的发布,各位想要的抑或不想要的功能,各位会调用抑或不会调用的功能它都有了.
都有的同时,也使得它4.0的版本相比实在大了不少.
如果只是写个Demo,做个测试,这货实在是太大了,于是乎想着用分类写一个简单点的网络请求,只支持GET与POST
// MARK: - 封装网络请求方式
extension URLRequest {
/// 直接从Alamofire5.0中拿的
/// 请求方式
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
/// `GET` method.
public static let get = HTTPMethod(rawValue: "GET")
/// `POST` method.
public static let post = HTTPMethod(rawValue: "POST")
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
/// 请求方式
public var requestMethod: HTTPMethod? {
get { return httpMethod.flatMap(HTTPMethod.init) }
set { httpMethod = newValue?.rawValue }
}
}
// MARK: - 封装网络请求中的错误
extension URLRequest {
/// 详细错误
///
/// - urlInitFailed: URL初始化失败,返回值为nil
/// - notSupportRequestType: 不支持的请求方式(目前仅支持get与post)
/// - urlComponentsInitFailed: URLComponents初始化失败,返回值为nil
/// - getComponentsUrlFailed: 获取URLComponents对象的url失败,返回值为nil
/// - addingPercentEncodingError: 字符串进行addingPercentEncoding编码方法失败,返回为nil
/// - stringTransformDataError: 字符串通过utf8编码为Data类型失败,返回为nil
/// - noData: 请求返回的数据为空
/// - jsonDecodingError: 请求返回的数据反序列化失败
/// - urlSessionDataTaskError: 请求时出现了错误
public enum DetailError: Error {
case urlInitFailed
case notSupportRequestType
case urlComponentsInitFailed
case getComponentsUrlFailed
case addingPercentEncodingError
case stringTransformDataError
case noData(response: URLResponse?)
case jsonDecodingError(error: Error)
case urlSessionDataTaskError(error: Error)
}
}
extension URLRequest.DetailError: CustomStringConvertible {
public var description: String {
let message: String
switch self {
case .urlInitFailed:
message = "URL初始化失败,返回值为nil"
case .notSupportRequestType:
message = "不支持的请求方式(目前仅支持get与post)"
case .urlComponentsInitFailed:
message = "URLComponents初始化失败,返回值为nil"
case .getComponentsUrlFailed:
message = "获取URLComponents对象的url失败,返回值为nil"
case .addingPercentEncodingError:
message = "字符串进行addingPercentEncoding编码方法失败,返回为nil"
case .stringTransformDataError:
message = "字符串通过utf8编码为Data类型失败,返回为nil"
case .noData(_):
message = "请求返回的数据为空"
case .jsonDecodingError(_):
message = "请求返回的数据反序列化失败"
case .urlSessionDataTaskError(_):
message = "请求时出现了错误"
}
return message
}
}
// MARK: - 封装网络请求
extension URLRequest {
static let defaultSession = URLSession(configuration: .default)
static var task: URLSessionDataTask?
/// GET请求
///
/// - Parameters:
/// - baseUrl: 基本网址
/// - api: Api
/// - HTTPHeaders: 请求头
/// - params: 请求参数
/// - completionHandler: 完成回调
public static func get<T: Codable>(baseUrl: String,
api: ApiProtocol? = nil,
HTTPHeaders: [String: String]? = nil,
params: [String: String]? = nil,
completionHandler: @escaping (Swift.Result<(T, HTTPURLResponse?), URLRequest.DetailError>) -> Void) {
request(baseUrl: baseUrl, requestMethod: .get, api: api, HTTPHeaders: HTTPHeaders, completionHandler: completionHandler)
}
/// POST请求
///
/// - Parameters:
/// - baseUrl: 基本网址
/// - api: Api
/// - HTTPHeaders: 请求头
/// - params: 请求参数
/// - completionHandler: 完成回调
public static func post<T: Codable>(baseUrl: String,
api: ApiProtocol? = nil,
HTTPHeaders: [String: String]? = nil,
params: [String: String]? = nil,
completionHandler: @escaping (Swift.Result<(T, HTTPURLResponse?), URLRequest.DetailError>) -> Void) {
request(baseUrl: baseUrl, requestMethod: .post, api: api, HTTPHeaders: HTTPHeaders, completionHandler: completionHandler)
}
/// 通用请求
///
/// - Parameters:
/// - baseUrl: 基本网址
/// - requestMethod: 请求方式 (目前仅支持get与post)
/// - api: Api
/// - HTTPHeaders: 请求头
/// - params: 请求参数
/// - completionHandler: 完成回调 注意是一个Result类型 Success是(T, HTTPURLResponse?)的元组, Failure是URLRequest.DetailError 自定义的Error枚举
private static func request<T: Codable>(baseUrl: String,
requestMethod: URLRequest.HTTPMethod,
api: ApiProtocol? = nil,
HTTPHeaders: [String: String]? = nil,
params: [String: String]? = nil,
completionHandler: @escaping (Swift.Result<(T, HTTPURLResponse?), URLRequest.DetailError>) -> Void) {
/// 网址字符串拼接
let queryURLString = baseUrl + (api?.api ?? "")
/// 网址URL生成
guard let queryURL = URL(string: queryURLString) else {
completionHandler(.failure(.urlInitFailed))
return
}
switch requestMethod {
case .get:
/// get请求的参数序列化
guard var components = URLComponents(url: queryURL, resolvingAgainstBaseURL: true) else {
completionHandler(.failure(.urlComponentsInitFailed))
return
}
if let params = params {
// 对于一个字典进行enumerated(), 第一个其实元素所在的序列顺序的个数,而后面会将字典的[key: value]改为元组形式的(key: String, value: String)这样方便进行.出来
for (_, tuple) in params.enumerated() {
components.queryItems?.append(URLQueryItem(name: tuple.key, value: tuple.value))
}
}
guard let url = components.url else {
completionHandler(.failure(.getComponentsUrlFailed))
return
}
var request = URLRequest(url: url)
request.httpMethod = requestMethod.rawValue
if let headers = HTTPHeaders {
for (_, tuple) in headers.enumerated() {
request.setValue(tuple.value, forHTTPHeaderField: tuple.key)
}
}
urlSessionRequest(request, completionHandler: completionHandler)
case .post:
/// post请求的参数序列化
var request = URLRequest(url: queryURL)
request.httpMethod = requestMethod.rawValue
if let headers = HTTPHeaders {
for (_, tuple) in headers.enumerated() {
request.setValue(tuple.value, forHTTPHeaderField: tuple.key)
}
}
if let params = params {
let queryString = params.compactMap { "\($0) = \($1)" }.joined(separator: "&")
let generalDelimitersToEncode = ":#[]@"
let subDelimitersToEncode = "!$&'()*+,;="
var allowedCharacterSet = CharacterSet.urlQueryAllowed
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
guard let query = queryString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) else {
completionHandler(.failure(.addingPercentEncodingError))
return
}
guard let data = query.data(using: .utf8) else {
completionHandler(.failure(.stringTransformDataError))
return
}
request.httpBody = data
}
urlSessionRequest(request, completionHandler: completionHandler)
default:
completionHandler(.failure(.notSupportRequestType))
}
}
/// URLSession单例的dataTask请求
///
/// - Parameters:
/// - request: URLRequest
/// - completionHandler: 完成回调
private static func urlSessionRequest<T: Codable>(_ request: URLRequest,
completionHandler: @escaping (Swift.Result<(T, HTTPURLResponse?), URLRequest.DetailError>) -> Void) {
task?.cancel()
task = defaultSession.dataTask(with: request) { (data, response, error) in
defer {
self.task = nil
}
guard let data = data else {
DispatchQueue.main.async {
completionHandler(.failure(.noData(response: response)))
}
return
}
guard error == nil else {
DispatchQueue.main.async {
completionHandler(.failure(.urlSessionDataTaskError(error: error!)))
}
return
}
do {
let object = try JSONDecoder().decode(T.self, from: data)
DispatchQueue.main.async {
completionHandler(.success((object, response as? HTTPURLResponse)))
}
} catch {
DispatchQueue.main.async {
#if DEBUG
print("JSON Decoding Error: \(error)")
#endif
completionHandler(.failure(.jsonDecodingError(error: error)))
}
}
}
task?.resume()
}
}
/// Api协议
public protocol ApiProtocol: CustomStringConvertible {
var api: String { get }
}
extension ApiProtocol {
public var api: String {
return description
}
}
使用的时候如下
/// 定义模型
struct TopicItem: Codable {
let topicImageUrl: String?
let topicOrder: Int?
let id: Int?
let upTime: String?
let topicStatus: Int?
let topicTittle: String?
let topicDesc: String?
}
struct TopicModel: Codable {
let code: Int?
let list: [TopicItem]
}
请求
/// 注意返回的result类型,有点复杂,但是目前只能这样
URLRequest.post(baseUrl: "http://sun.topray-media.cn/tz_inf/api/topics") { (result: Swift.Result<(TopicModel, HTTPURLResponse?), URLRequest.DetailError>) in
switch result {
case .success(let model, let response):
print("model:\(model)")
print("status code: \(response?.statusCode)")
case .failure(let error):
print("error:\(error)")
}
}
网友评论