在使用Swift开发时,网络请求大多使用Alamofire,但使用时不是很方便,于是就计划封装下网络请求模块.
实现目标:
1.将通用参数和加密规则封装到底层,不要每次都填写;
2.将网络请求的通用处理;如发生请求时显示NetworkActivityIndicator等;
3.网络请求在调用时,尽可能简单,只需要关注需要使用的接口和必要的参数;
4.支持一些可能出现的额外处理.
调用的形式需要实现形如:
UserRouter.user(id:123).request() { result in
print(result)
}
一. Routerable
首先将Request的header,params,url,method以及其他需要接口自定义的内容封装到protocol.由于header是一些固定的属性,所以不加入到协议中
// MARK: - Routerable
public protocol Routerable{
// url, method, param, useCache
var http : (String, RouterMethod, RouterParam, Bool) {get}
}
创建Routerable的extesion,将一些通用的属性设置出来,以下方法方便用于HttpManager调用
// MARK: - 验证相关的信息
extension Routerable {
public var baseURL: String {
return UserDefaults.hostName.value ?? ""
}
/// url
public var url: String {
return "\(baseURL)/\(http.0)"
}
/// method
public var method: HTTPMethod {
return http.1
}
/// 是否使用缓存
public var useCache: Bool {
return http.3
}
/// header
public var headerFields: [String: String]{
var header: [String:String] = [:]
header["version"] = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
header["type"] = "ios"
return header
}
/// parameters
public var parameters: Parameters {
var newParams: Parameters = self.http.2
//添加服务器版本
newParams["version"] = "1.9"
// sign验证=
newParams["sign"] = sign(newParams)
return newParams
}
}
二. HttpManager
创建HttpManager用于管理HTTP请求
import Foundation
import Alamofire
import SwiftyJSON
import SVProgressHUD
// MARK: - HTTP请求管理
public final class HttpManager{
private init(){}
// 创建单例
static let shared = HttpManager()
// 缓存
let urlCache = URLCache.init(memoryCapacity: 4*1024*1024, diskCapacity: 20*1024*1024, diskPath: nil)
lazy var alamofireManager: SessionManager = {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 30
let session = SessionManager(configuration: config)
return session
}()
}
在HttpManager中自定义请求方法
// MARK: - 基本网络请求
extension HttpManager{
/// 通用网络请求
///
/// - Parameters:
/// - router: 路由地址
/// - queue: 线程
/// - completionHandler: 完成的回调,isEnd用于处理带缓存的请求,一般情况会先返回缓存,等请求完成在返回真实数据
public static func requestResult(router: Routerable, queue: DispatchQueue? = DispatchQueue.global(),isShowError: Bool = true, completionHandler: @escaping (Result<JSON>, Bool) -> Void) -> DataRequest{
let request = shared.alamofireManager.request(router.url, method: router.method, parameters: router.parameters, encoding: URLEncoding.default, headers: router.headerFields)
var isEnd = false
if router.useCache {// 如果使用cache
DispatchQueue.after(queue, 0.4, {
if isEnd == false, let dataResponse = request.cacheRequest(router: router) {
LogInfo("*******\(router.url)使用缓存*******")
completionHandler(dataResponse.result, isEnd)
}
})
}
// 将要发送请求
willSend(request)
request.responseDict(router: router, queue: queue, completionHandler: { response in
// 保存结果
response.saveCache(router: router)
// 请求结束
isEnd = true
// 完成请求返回
completionHandler(response.result, isEnd)
// 接收到服务端的结果
didReceive(response, isShowError: isShowError)
})
return request
}
}
// MARK: - Default处理
extension HttpManager {
// MARK: - 网络指示器
private static func NetworkActivityIndicatorVisible(_ isVisible: Bool){
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = isVisible
}
}
fileprivate static func willSend(_ request: DataRequest) {
NetworkActivityIndicatorVisible(true)
}
fileprivate static func didReceive(_ response: DataResponse<JSON>, isShowError: Bool = true){
NetworkActivityIndicatorVisible(false)
LogInfo(response.debugDescription)
if case let .failure(error) = response.result, isShowError{
DispatchQueue.main.async {
SVProgressHUD.showError(withStatus: error.localizedDescription)
}
}
}
}
三.Alamofire+Custom
根据返回数据的格式,处理通用的错误返回,并打印详细的请求相关的数据用于调试.
import Foundation
import Alamofire
import SwiftyJSON
// MARK: - Alamofire扩展
extension DataRequest {
@discardableResult
public func responseDict(
router: Routerable, queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<JSON>) -> Void) -> Self{
response(queue: queue, responseSerializer: DataResponseSerializer { _, response, data, error in
return DataRequest.dealResult(Request.serializeResponseData(response: response, data: data, error: error))
}, completionHandler: { response in
response.result.ifSuccess {
Helper.saveJSON(.http, response.result.value!, router.fileName)
}
completionHandler(response)
})
return self
}
class func dealResult(_ data: Result<Data>) -> Result<JSON> {
var result: Result<JSON>! = nil
switch data{
case .success(let jsonData):
do {
let json = try JSON(data: jsonData)
let errCode = json["error"].intValue
let message = json["message"].stringValue
if errCode == 0 {
result = Result.success(json)
}else{
let error = KFError.init(errorCode: errCode, message: message)
result = Result.failure(error)
}
} catch let error{
result = Result.failure(error)
}
case .failure(let error):
result = Result.failure(error)
}
return result
}
/// 获取缓存
///
/// - Parameter router: 请求路由
/// - Returns: 返回结果
func cacheRequest(router: Routerable) -> DataResponse<JSON>? {
if let request = self.request, request.httpMethod?.lowercased() == "get", let cachedReponse = HttpManager.shared.urlCache.cachedResponse(for: request){
return DataResponse.init(request: request, response: (cachedReponse.response as! HTTPURLResponse), data: cachedReponse.data, result: DataRequest.dealResult(Result.success(cachedReponse.data)))
}
return nil
}
}
extension DataResponse {
/// 保存结果
///
/// - Parameter router: 请求路由
func saveCache(router: Routerable) {
if router.useCache, let res = self.response, let data = self.data, let request = self.request, request.httpMethod?.lowercased() == "get" {// 使用cache的请求才需要缓存
if self.result.isSuccess {
HttpManager.shared.urlCache.storeCachedResponse(CachedURLResponse.init(response: res, data: data), for: request)
}else{
HttpManager.shared.urlCache.removeCachedResponse(for: request)
}
}
}
public var debugDescription: String {
var output: [String] = []
output.append("[Alamofire]")
// Request
output.append("[Request]:\(self.request?.description ?? "(invalid request)")")
if let headers = self.request?.allHTTPHeaderFields {
output.append("[Headers]:\(headers.description)")
}
if let bodyStream = self.request?.httpBodyStream {
output.append("[BodyStream]:\(bodyStream.description)")
}
if let httpMethod = self.request?.httpMethod {
output.append("[Method]:\(httpMethod)")
}
if let body = self.request?.httpBody, let stringOutput = String(data: body, encoding: .utf8) {
output.append("[Body]:\(stringOutput)")
}
output.append("[Data]: \(data?.count ?? 0) bytes")
output.append("[Time]: \(timeline.requestDuration)")
if let data = data {
do {
output.append("[Result]: \(try JSON.init(data: data))")
}catch {}
}else{
output.append("[Result]: \(result.value.debugDescription)")
}
return output.joined(separator: "\n")
}
private func JSONResponseDataFormatter(_ data: Data) -> Data {
do {
let dataAsJSON = try JSONSerialization.jsonObject(with: data)
let prettyData = try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
return prettyData
} catch {
return data // fallback to original data if it can't be serialized.
}
}
}
三.UserRouter
封装关于User的网络请求
// MARK: - 用户请求
public enum UserRouter : Routerable{
///获取用户列表
case userlist
/// 获取user模型, id为用户id
case user(id: Int)
public var http: (String, RouterMethod, RouterParam, Bool){
var path: String!
let method: RouterMethod = .get
var parameters: RouterParam = [:]
var useCache = true
switch self{
case . userlist:
path = "api/list"
case let .user(id):
path = "api/users/\(id)"
parameters[ParamType.ticket_id.rawValue] = id
}
return (path, method, parameters, useCache)
}
}
四.使用方式
HttpManager.requestResult(router: UserRouter.user(id: 123)) {(result, isEnd) in
print(result)
}
或者给Routerable添加网络请求方法
extension Routerable {
public func requestResult(queue: DispatchQueue? = DispatchQueue.global(),isShowError: Bool = true, completionHandler: @escaping (Result<JSON>, Bool) -> Void) -> DataRequest {
return HttpManager.requestResult(router: self, queue: queue, isShowError: isShowError, completionHandler: completionHandler)
}
}
这样也就可以直接通过router请求
UserRouter.user(id: 123).request() { ( result, isEnd) in
print(result)
}
五.添加RxSwift支持
import Foundation
import RxSwift
import SwiftyJSON
import Alamofire
import SVProgressHUD
extension Routerable {
var rx_request: Observable<JSON> {
return HttpManager.rx_requestResult(self)
}
}
// MARK: - HttpManager的Rx扩展
extension HttpManager {
/// Rx版本的网络请求
///
/// - Parameter router: 路由地址
/// - Returns: 回调
static func rx_requestResult(_ router: Routerable, isShowError: Bool = true) -> Observable<JSON>{
return Observable.create({observer -> Disposable in
let request = HttpManager.requestResult(router: router, completionHandler: { result, isEnd in
switch result {
case let .success(value):
observer.onNext(value)
if isEnd {
observer.onCompleted()
}
case let .failure(error):
observer.onError(error)
}
})
return Disposables.create {
request.cancel()
}
})
}
}
// MARK: - 添加新的Observable类型
extension ObservableType {
/// 显示HUD
func showHUD() -> Observable<Self.E> {
return Observable.create { observer in
// showHUD
DispatchQueue.main.async {
SVProgressHUD.show()
}
return self.subscribe({ event in
// hideHUD
DispatchQueue.main.async {
SVProgressHUD.dismiss()
}
observer.on(event)
})
}
}
/// 绑定成功的值
public func bindSuccess(to variable: Variable<E>) -> Disposable {
return subscribe { event in
switch event {
case let .next(element):
variable.value = element
case .error(_):
break
case .completed:
break
}
}
}
/// 当next或error时执行
public func doOnce(on: @escaping ((E?) -> Void)) -> Observable<E> {
return self.do(onNext: { (element) in
on(element)
}, onError: { (_) in
on(nil)
})
}
/// 便利返回数据
public static func next(_ element: E) -> Observable<E> {
return Observable.create({ (observer) -> Disposable in
observer.onNext(element)
observer.onCompleted()
return Disposables.create()
})
}
/// 网络请求
func httpRequest(_ router: Routerable) -> Observable<JSON>{
return Observable.create({ observer -> Disposable in
var request: DataRequest? = nil
let dispose = self.subscribe({ event in
switch event {
case .next(_):
request = HttpManager.requestResult(router: router, completionHandler: { result, isEnd in
switch result {
case let .success(value):
observer.onNext(value)
if isEnd {
observer.onCompleted()
}
case let .failure(error):
observer.onError(error)
}
})
case let .error(error):
observer.onError(error)
default:
break
}
})
return Disposables.create {
dispose.dispose()
request?.cancel()
}
})
}
}
github地址:SwiftHTTPManager