SWNetRequest.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // SWNetRequest.swift
  3. // LiteraryHeaven
  4. //
  5. // Created by SanW on 2017/8/2.
  6. // Copyright © 2017年 ONON. All rights reserved.
  7. //
  8. import Alamofire
  9. import UIKit
  10. // 默认超时时间
  11. public let bf_timeoutInterval: TimeInterval = 30
  12. // MARK: - 错误
  13. /// 错误
  14. public struct PQError: Error {
  15. public var msg: String? // 提示信息
  16. public var code: Int // 错误吗
  17. public init(msg: String?, code: Int = 0) {
  18. self.msg = msg
  19. self.code = code
  20. }
  21. public var localizedDescription: String {
  22. return msg ?? ""
  23. }
  24. }
  25. // MARK: - 网络请求
  26. /// 网络请求
  27. public class SWNetRequest: NSObject {
  28. // static let share = SWNetRequest()
  29. /// 回调方法
  30. public typealias completeHander = (_ jsonObject: Any?, _ error: PQError?, _ duration: TimeInterval?) -> Void
  31. static let sessionManager: Session? = {
  32. let configuration = URLSessionConfiguration.default
  33. configuration.timeoutIntervalForRequest = bf_timeoutInterval
  34. return Session(configuration: configuration, delegate: SessionDelegate(), rootQueue: DispatchQueue(label: "org.alamofire.session.rootQueue"), startRequestsImmediately: true, requestQueue: nil, serializationQueue: nil, interceptor: CMRequestRetrier(), serverTrustManager: nil, redirectHandler: nil, cachedResponseHandler: nil, eventMonitors: [])
  35. }()
  36. static let reTrySessionManager: Session? = {
  37. let configuration = URLSessionConfiguration.default
  38. configuration.timeoutIntervalForRequest = 5
  39. return Session(configuration: configuration, delegate: SessionDelegate(), serverTrustManager: nil)
  40. }()
  41. /// get请求
  42. public class func getRequestData(url: String, parames: [String: Any]?, encoding: ParameterEncoding = URLEncoding.default, timeoutInterval: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  43. requestData(method: .get, encoding: encoding, url: url, parames: parames, timeoutInterval: timeoutInterval) { responseObject, error, timeline in
  44. response(responseObject, error, timeline)
  45. }
  46. }
  47. /// post请求
  48. public class func postRequestData(url: String, parames: [String: Any]?, encoding: ParameterEncoding = URLEncoding.default, timeoutInterval: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  49. requestData(method: .post, encoding: encoding, url: url, parames: parames, timeoutInterval: timeoutInterval) { responseObject, error, timeline in
  50. response(responseObject, error, timeline)
  51. }
  52. }
  53. // put请求
  54. public class func putRequestData(url: String, parames: [String: Any]?, encoding: ParameterEncoding = URLEncoding.default, timeoutInterval: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  55. requestData(method: .put, encoding: encoding, url: url, parames: parames, timeoutInterval: timeoutInterval) { responseObject, error, timeline in
  56. response(responseObject, error, timeline)
  57. }
  58. }
  59. /// delete请求
  60. public class func deleteRequestData(url: String, parames: [String: Any]?, encoding: ParameterEncoding = URLEncoding.default, timeoutInterval: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  61. requestData(method: .delete, encoding: encoding, url: url, parames: parames, timeoutInterval: timeoutInterval) { responseObject, error, timeline in
  62. response(responseObject, error, timeline)
  63. }
  64. }
  65. /// head请求
  66. public class func headRequestData(url: String, parames: [String: Any]?, encoding: ParameterEncoding = URLEncoding.default, timeoutInterval: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  67. requestData(method: .head, encoding: encoding, url: url, parames: parames, timeoutInterval: timeoutInterval) { responseObject, error, timeline in
  68. response(responseObject, error, timeline)
  69. }
  70. }
  71. /// 网络请求
  72. fileprivate class func requestData(method: HTTPMethod, encoding: ParameterEncoding, url: String, parames: [String: Any]?, timeoutInterval _: TimeInterval = bf_timeoutInterval, response: @escaping completeHander) {
  73. if !bf_validURL(url: url) {
  74. response(nil, PQError(msg: "非法地址", code: 0), nil)
  75. return
  76. }
  77. NTLog(message: "网络请求-发起:url = \(url),parames = \(parames ?? [:])")
  78. sessionManager?.request(url, method: method, parameters: parames, encoding: encoding, headers: nil, requestModifier: { request in
  79. request.timeoutInterval = bf_timeoutInterval
  80. }).responseJSON { jsonResponse in
  81. switch jsonResponse.result {
  82. case .success:
  83. if jsonResponse.data != nil {
  84. let jsonData: Any = try! JSONSerialization.jsonObject(with: jsonResponse.data!, options: JSONSerialization.ReadingOptions.mutableContainers)
  85. NTLog(message: "网络请求-成功:url = \(jsonResponse.request?.url?.absoluteString ?? ""),jsonData = \(jsonData)")
  86. response(jsonData, nil, jsonResponse.metrics?.taskInterval.duration)
  87. } else {
  88. response(nil, PQError(msg: "数据为空", code: 1), jsonResponse.metrics?.taskInterval.duration)
  89. }
  90. case let .failure(error):
  91. let errorResult = dealWithError(error: error)
  92. response(nil, PQError(msg: errorResult.msg, code: errorResult.code), jsonResponse.metrics?.taskInterval.duration)
  93. NTLog(message: "网络请求-失败:url = \(jsonResponse.request?.url?.absoluteString ?? ""),error = \(String(describing: jsonResponse.error?.localizedDescription))")
  94. }
  95. }
  96. }
  97. /// 取消网络请求
  98. /// - Parameter url: 某一个地址,空则取消所有
  99. /// - Returns: <#description#>
  100. public class func cancelTask(url: String?) {
  101. sessionManager?.session.getAllTasks(completionHandler: { tasks in
  102. tasks.forEach { task in
  103. if task.currentRequest?.url?.absoluteString == url {
  104. task.cancel()
  105. }
  106. }
  107. })
  108. }
  109. public class func bf_validURL(url: String?) -> Bool {
  110. if url == nil || (url?.count ?? 0) <= 4 || (!(url?.hasPrefix("http") ?? false) && !(url?.hasPrefix("https") ?? false)) {
  111. return false
  112. }
  113. return true
  114. }
  115. /// 获取网络状态
  116. /// - Returns: <#description#>
  117. public class func networkStatusDescription() -> String {
  118. let status = NetworkReachabilityManager(host: "www.baidu.com")?.status
  119. var statusStr: String!
  120. switch status {
  121. case .unknown:
  122. statusStr = "NETWORK_UNKNOWN"
  123. case .notReachable:
  124. statusStr = "NETWORK_NO"
  125. case .reachable(.cellular):
  126. statusStr = "4G/5G"
  127. case .reachable(.ethernetOrWiFi):
  128. statusStr = "Wi-Fi"
  129. default:
  130. statusStr = "NETWORK_UNKNOWN"
  131. }
  132. return statusStr
  133. }
  134. /// 判断是否有网
  135. /// - Returns: <#description#>
  136. public class func isNetReachabled() -> Bool {
  137. return NetworkReachabilityManager(host: "www.baidu.com")?.status == .reachable(.cellular) || NetworkReachabilityManager(host: "www.baidu.com")?.status == .reachable(.ethernetOrWiFi)
  138. }
  139. /// 获取ip地址
  140. /// - Returns: <#description#>
  141. public class func ipAddressDescription() -> String {
  142. var addresses = [String]()
  143. var ifaddr: UnsafeMutablePointer<ifaddrs>?
  144. if getifaddrs(&ifaddr) == 0 {
  145. var ptr = ifaddr
  146. while ptr != nil {
  147. let flags = Int32(ptr!.pointee.ifa_flags)
  148. var addr = ptr!.pointee.ifa_addr.pointee
  149. if (flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING) {
  150. if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
  151. var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
  152. if getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
  153. if let address = String(validatingUTF8: hostname) {
  154. addresses.append(address)
  155. }
  156. }
  157. }
  158. }
  159. ptr = ptr!.pointee.ifa_next
  160. }
  161. freeifaddrs(ifaddr)
  162. }
  163. return addresses.first ?? "0.0.0.0"
  164. }
  165. // 将原始的url编码为合法的url
  166. public class func bf_urlEncoded(url: String) -> String {
  167. let encodeUrlString = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
  168. return encodeUrlString ?? ""
  169. }
  170. // 将编码后的url转换回原始的url
  171. public class func bf_urlDecoded(url: String) -> String {
  172. return url.removingPercentEncoding ?? ""
  173. }
  174. public class func dealWithError(error: Error?) -> (code: Int, msg: String?) {
  175. var code: errorCode = .nomal
  176. var msg: String? = error?.localizedDescription
  177. if let error = error as? AFError {
  178. switch error {
  179. case let .invalidURL(url):
  180. NTLog(message: "网球请求-处理错误:Invalid URL: \(url) - \(error.localizedDescription)")
  181. code = .valideUrl
  182. msg = "请求地址检验失败"
  183. case let .parameterEncodingFailed(reason):
  184. NTLog(message: "网球请求-处理错误:parameterEncodingFailed \(error.localizedDescription),Reason: \(reason)")
  185. case let .multipartEncodingFailed(reason):
  186. NTLog(message: "网球请求-处理错误:multipartEncodingFailed: \(error.localizedDescription),\(reason)")
  187. case let .responseValidationFailed(reason):
  188. NTLog(message: "网球请求-处理错误:responseValidationFailed: \(error.localizedDescription),Reason: \(reason)")
  189. switch reason {
  190. case .dataFileNil, .dataFileReadFailed:
  191. NTLog(message: "网球请求-处理错误:Downloaded file could not be read")
  192. case let .missingContentType(acceptableContentTypes):
  193. NTLog(message: "网球请求-处理错误:Content Type Missing: \(acceptableContentTypes)")
  194. case let .unacceptableContentType(acceptableContentTypes, responseContentType):
  195. NTLog(message: "网球请求-处理错误:Response content type: \(responseContentType) was unacceptable: \(acceptableContentTypes)")
  196. case let .unacceptableStatusCode(code):
  197. NTLog(message: "网球请求-处理错误:Response status code was unacceptable: \(code)")
  198. case let .customValidationFailed(error: error):
  199. NTLog(message: "网球请求-处理错误:customValidationFailed: \(error)")
  200. }
  201. case let .responseSerializationFailed(reason):
  202. NTLog(message: "网球请求-处理错误:Response serialization failed: \(error.localizedDescription)")
  203. NTLog(message: "Failure Reason: \(reason)")
  204. case let .createUploadableFailed(error: error):
  205. NTLog(message: "网球请求-处理错误:createUploadableFailed: \(error.localizedDescription)")
  206. case let .createURLRequestFailed(error: error):
  207. NTLog(message: "createURLRequestFailed: \(error.localizedDescription)")
  208. case let .downloadedFileMoveFailed(error: error, source: source, destination: destination):
  209. NTLog(message: "网球请求-处理错误:downloadedFileMoveFailed: \(error.localizedDescription),\(source),\(destination)")
  210. case .explicitlyCancelled:
  211. NTLog(message: "网球请求-处理错误:explicitlyCancelled: \(error.localizedDescription)")
  212. case let .parameterEncoderFailed(reason: reason):
  213. NTLog(message: "网球请求-处理错误:parameterEncoderFailed: \(error.localizedDescription),\(reason)")
  214. case let .requestAdaptationFailed(error: error):
  215. NTLog(message: "网球请求-处理错误:requestAdaptationFailed: \(error.localizedDescription)")
  216. case let .requestRetryFailed(retryError: retryError, originalError: originalError):
  217. NTLog(message: "网球请求-处理错误:requestRetryFailed: retryError = \(retryError),originalError = \(originalError)\(error.localizedDescription)")
  218. case let .serverTrustEvaluationFailed(reason: reason):
  219. NTLog(message: "网球请求-处理错误:serverTrustEvaluationFailed: - \(error.localizedDescription),\(reason)")
  220. case .sessionDeinitialized:
  221. NTLog(message: "网球请求-处理错误:sessionDeinitialized: \(error.localizedDescription)")
  222. case let .sessionInvalidated(error: error):
  223. NTLog(message: "网球请求-处理错误:sessionInvalidated: \(String(describing: error?.localizedDescription))")
  224. case let .sessionTaskFailed(error: failError):
  225. NTLog(message: "网球请求-处理错误:sessionTaskFailed:error = \(error) \(error.localizedDescription)")
  226. code = errorCode.init(rawValue: (failError as? NSError)?.code ?? 1) ?? .nomal
  227. if code == .netLost || code == .tomeOut {
  228. msg = "网络不给力"
  229. }
  230. case let .urlRequestValidationFailed(reason: reason):
  231. NTLog(message: "网球请求-处理错误:urlRequestValidationFailed: \(error.localizedDescription),\(reason)")
  232. }
  233. NTLog(message: "网球请求-处理错误:Underlying error: \(String(describing: error.underlyingError))")
  234. } else {
  235. NTLog(message: "网球请求-处理错误:Unknown error: \(String(describing: error))")
  236. }
  237. return (code.rawValue, msg)
  238. }
  239. }
  240. /** 打印 */
  241. func NTLog<T>(message: T, fileName: String = #file, methodName: String = #function, lineNumber: Int = #line) {
  242. #if DEBUG
  243. print("👉func: \((fileName.components(separatedBy: "/").last)!).\(methodName)\n time: \(Int64(CFAbsoluteTimeGetCurrent() * 1000))\n line: \(lineNumber)\n message: \(message)👈")
  244. #else
  245. #endif
  246. }
  247. class CMRequestRetrier: RequestInterceptor {
  248. func retry(_: Request, for _: Session, dueTo _: Error, completion: @escaping (RetryResult) -> Void) {
  249. completion(.doNotRetry)
  250. }
  251. }
  252. enum errorCode: Int {
  253. case nomal = 1
  254. case valideUrl = 2
  255. case tomeOut = -1001
  256. case netLost = -1009
  257. }