网络请求封装主要利用 Swift 的 protocol 和 enum 特性

定义一个protocol, 用于规范网络请求接口

/// 接口类型
protocol TargetType {
  /// 接口地址
  var serviceAddress: String { get }
  /// 参数信息
  var parameters: [String: Any]? { get }
  /// 请求类型
  var method: HTTPMethod { get }
}

HTTPMethod由Alamofire定义如下:

public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

以订单列表和订单详情请求做声明举例

以一个 enum 实现 TargetType protocol,巧妙运用 swift 可以在 enum 自生做 switch case 操作

enum OrderTarget: TargetType {
  /// 订单列表接口
  case orders(uid: Int)
  /// 订单详情接口
  case detail(no: String)

  // MARK: - 参数说明
  var serviceAddress: String {
    switch self {
    case .orders:
      return "orders"
    case let .detail(no):
      return "orders/\(no)"
    }
  }

  var parameters: [String : Any]? {
    switch self {
    case let .orders(uid):
      return ["uid": uid]
    default:
      return nil
    }
  }

  var method: HTTPMethod {
    switch self {
    default:
      return .get
    }
  }
}

网络请求封装

网络请求采用了 Alamofire、AlamofireObjectMapper、ObjectMapper、SwiftyJSON

import Foundation
import AlamofireObjectMapper
import ObjectMapper

// 请求地址前缀
private let kBaseURL = "***"

/// 超时时长
private let kTimeoutIntervalForRequest: TimeInterval = 15

/// 服务器返回的对象封装,根据实际情况定义
struct Response: Mappable, CustomStringConvertible {
  var status: Int?
  var code: Int?
  var message: String?
  var result: [String: Any]?
  var reason: String?
  var stack: String?

  init?(map: Map) {}

  mutating func mapping(map: Map) {
    status <- map["status"]
    code <- map["code"]
    message <- map["message"]
    result <- map["result"]
    reason <- map["reason"]
    stack <- map["stack"]
  }

  public var description: String {
    return "status: \(String(describing: status))\n message: \(String(describing: message))\n result: \(String(describing: result))"
  }
}

/// 错误类型
///
/// - netError: 网络异常
/// - serverError: 服务器异常 500
/// - sessionError: 用户未授权(签名错误或无权限)
/// - nodataError: 没有数据
/// - otherError: 其他错误
enum NetworkError: CustomStringConvertible {
  case netError(String)
  case serverError(String)
  case sessionError(String)
  case nodataError(String)
  case otherError(String)

  public var description: String {
    switch self {
    case let .netError(text):
      return text
    case let .serverError(text):
      return text
    case let .nodataError(text):
      return text
    case let .sessionError(text):
      return text
    case let .otherError(text):
      return text
    }
  }
}

/// 封装返回结果
///
/// - success: 成功结果
/// - error: 错误结果
enum NetworkResult {
  case success(Response)
  case error(NetworkError)
}

/// 网络请求
struct Network: RequestAdapter {
  static let `default`: Network = {
    let network = Network()
    network.sessionManager.adapter = network
    return network
  }()

  private let sessionManager: SessionManager = {
    let config = URLSessionConfiguration.default
    config.timeoutIntervalForRequest = kTimeoutIntervalForRequest
    let sessionManager = SessionManager(configuration: config)
    return sessionManager
  }()

  public func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
    return urlRequest
  }

  /// 发出请求
  ///
  ///   - target: 请求目标,包含方法信息和参数信息
  ///   - completionHandler: 结果回调
  ///   - Returns: DataRequest 实例
  func request(target: TargetType,
               completionHandler: @escaping (NetworkResult) -> Void) -> Request {
    let url = kBaseURL + target.serviceAddress

    var headers = SessionManager.defaultHTTPHeaders
    // 请求头处理...

    let request = self.sessionManager.request(url,
                                              method: target.method,
                                              parameters: target.parameters,
                                              encoding: URLEncoding.default,
                                              headers: headers)

    return request.responseJSON { (originalResponse) in
      self.dealResponse(originalResponse: originalResponse, completionHandler: completionHandler)
    }
  }

  /// 统一处理响应结果
  ///
  /// - Parameters:
  ///   - originalResponse: 原始请求
  ///   - completionHandler: 成功回调
  private func dealResponse(originalResponse: DataResponse<Any>,
                            completionHandler: @escaping (NetworkResult) -> Void) {
    if let url = originalResponse.response?.url?.absoluteString {
      print("请求地址:\(url)")
    }
    let statusCode = originalResponse.response?.statusCode ?? -1

    // 根据自己的逻辑对结果判断
    switch originalResponse.result {
    case let .success(value):
      let json = JSON(value)
      print("\(statusCode) 返回结果:\(json)")

      var resp = Response(JSON: json.dictionaryObject!)!
      if statusCode == 401 {
        let msg = resp.message ?? "登录信息失效"

        if resp.code == 40006 {
          completionHandler(.error(.otherError(msg)))
        } else {
            // 此处可以做全局通用的登录失效提示如:
            /*
            DispatchQueue.main.async {
              HUD.showError(text: resultError.description)
            }
            */
        }
        completionHandler(.error(.sessionError(msg)))
        return
      } else if statusCode == 422 {
        completionHandler(.error(.otherError(resp.msg ?? "请求参数错误")))
        return
      } else if statusCode == 400 || statusCode == 403 || statusCode == 404 {
        completionHandler(.error(.otherError(resp.msg ?? "请求失败")))
        return
      } else if statusCode == 500 {
        completionHandler(.error(.otherError(resp.msg ?? "服务器异常")))
        return
      } else {
        completionHandler(.success(resp))
      }
      break

    case let .failure(error):
      let description = error.localizedDescription
      print("\(statusCode) 错误信息:\(description)")

      if description == "已取消" {
        return
      }

      let resultError: NetworkError = .netError(description)
      completionHandler(.error(resultError))
      // 此处可以做全局通用的网络异常提示,如:
      /*
      DispatchQueue.main.async {
        HUD.showError(text: resultError.description) // 弹出全局通用错误提示
      }
      */
      break
    }
  }
}

网络请求调用示例

let request = Network.default.request(target: OrderTarget.detail(no: "1111"))
    { [unowned self] (result) in
      switch result {
      case let .success(resp):
        // 成功处理...
        break
      case let .error(error):
        // 错误处理...
        switch error {
        case let .otherError(text):
        // CRHUD.showToast(text)
          break
        default:
          break
        }
        break
      }
    }