IOS:Alamofire网络框架

1、导入依赖

  • 导入Alamofire网络框架
  • 导入ObjectMapper是为了把Json解析成Object
project 'xxxxxxxx.xcodeproj'

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'

target 'xxxxxxxx' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  # use_frameworks!
  # Pods for xxxxxxxx
  # 导入Alamofire网络框架
  pod 'Alamofire'
  # 导入ObjectMapper是为了解析json到Object
  pod 'ObjectMapper'
end

2、创建数据Model

注意:需要注意ObjectMapper的使用。

  • Model类必须实现 ObjectMapperMappable 协议:
    • 必须实现 required init?(map: Map) 方法(Mappable 自己的协议)
    • 必须实现 func mapping(map: Map) 方法(Mappable 继承自 BaseMappable 的协议)
  • 子类也必须重写 required init?(map: Map) 方法,并调用 super.init(map: map)
  • 子类也必须重写 func mapping(map: Map) 方法,并调用 super.mapping(map: map)
2.1、导入ObjectMapper
import ObjectMapper
2.2、创建基类
class BaseModel: Mappable {
    required init?(map: Map) {
    }

    func mapping(map: Map) {
    }
}
2.3、创建响应错误类(继承 BaseModel)
class ApiErrorModel: BaseModel {
    var code: Int?
    var msg: String?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        code <- map["code"]
        msg <- map["msg"]
    }
}
2.4、创建登录信息类(继承 BaseModel)
class LoginModel: BaseModel {
    var token: String?
    var user: User?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        token <- map["token"]
        user <- map["user"]
    }
}
2.5、创建用户信息类(继承 BaseModel)
class User: BaseModel {
    var id: Int?
    var name: String?
    var status: Int?
    var createTime: Int?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        id <- map["id"]
        name <- map["name"]
        status <- map["status"]
        createTime <- map["createTime"]
    }
}
2.6、创建包装请求结果的基类(继承 BaseModel,具体数据类型为“泛型”)
class BaseResponseWrapper: BaseModel {
    var reqResult: ApiErrorModel?

    func getData() -> S? {
        return nil
    }

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        reqResult <- map["reqResult"]
    }
}
2.7、创建包装请求结果的类(继承 BaseResponseWrapper,具体数据类型为 String)
class ResponseStringWrapper: BaseResponseWrapper {
    var data: String?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        data <- map["data"]
    }

    override func getData() -> String? {
        return data
    }
}
2.8、创建包装请求结果的类(继承 BaseResponseWrapper,具体数据类型为“泛型”)

注意:泛型必须是实现 Mappable 的类,否则会解析失败

// ⚠️泛型必须是实现Mappable的类,否则会解析失败
class ResponseWrapper: BaseResponseWrapper {
    var data: S?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        data <- map["data"]
    }

    override func getData() -> S? {
        return data
    }
}
2.9、创建包装请求结果的类(继承 BaseResponseWrapper,具体数据类型为“泛型数组”)

注意:泛型必须是实现 Mappable 的类,否则会解析失败

// ⚠️泛型必须是实现Mappable的类,否则会解析失败
class ResponseListWrapper: BaseResponseWrapper<[S]> {
    var data: [S]?

    required init?(map: Map) {
        super.init(map: map)
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        data <- map["data"]
    }

    override func getData() -> [S]? {
        return data
    }
}
3.0、创建请求错误的枚举(包括错误代码和错误消息)
enum ApiErrorType {
    case INTERNAL_SERVER_ERROR
    case BAD_GATEWAY
    case NOT_FOUND
    case CONNECTION_TIMEOUT
    case NETWORK_NOT_CONNECT
    case UNEXPECTED_ERROR

    var rawValue: Int {
        get {
            switch self {
            case .INTERNAL_SERVER_ERROR: return 500
            case .BAD_GATEWAY: return 502
            case .NOT_FOUND: return 404
            case .CONNECTION_TIMEOUT: return 408
            case .NETWORK_NOT_CONNECT: return 499
            case .UNEXPECTED_ERROR: return 700
            }
        }
    }

    var msg: String {
        get {
            switch self {
            case .INTERNAL_SERVER_ERROR: return "请求发生异常"
            case .BAD_GATEWAY: return "请求发生异常"
            case .NOT_FOUND: return "未知请求"
            case .CONNECTION_TIMEOUT: return "请求超时"
            case .NETWORK_NOT_CONNECT: return "网络错误"
            case .UNEXPECTED_ERROR: return "未知错误"
            }
        }
    }

    public static func valueOf(rawValue: Int) -> ApiErrorType {
        switch rawValue {
        case ApiErrorType.INTERNAL_SERVER_ERROR.rawValue: return ApiErrorType.INTERNAL_SERVER_ERROR
        case ApiErrorType.BAD_GATEWAY.rawValue: return ApiErrorType.BAD_GATEWAY
        case ApiErrorType.NOT_FOUND.rawValue: return ApiErrorType.NOT_FOUND
        case ApiErrorType.CONNECTION_TIMEOUT.rawValue: return ApiErrorType.CONNECTION_TIMEOUT
        case ApiErrorType.NETWORK_NOT_CONNECT.rawValue: return ApiErrorType.NETWORK_NOT_CONNECT
        case ApiErrorType.UNEXPECTED_ERROR.rawValue: return ApiErrorType.UNEXPECTED_ERROR
        default:return ApiErrorType.UNEXPECTED_ERROR
        }
    }
}

3、创建网络服务

3.1、导入 Alamofire 与 ObjectMapper
import Alamofire
import ObjectMapper
3.2、配置 Alamofire 的 Https 证书
func setAlamofireHttps() {
    SessionManager.default.delegate.sessionDidReceiveChallenge = { (session: URLSession, challenge: URLAuthenticationChallenge) in
        let method = challenge.protectionSpace.authenticationMethod
        if method == NSURLAuthenticationMethodServerTrust {
            //验证服务器,直接信任或者验证证书二选一,推荐验证证书,更安全
            return self.trustServerWithCer(challenge: challenge)
            // return self.trustServer(challenge: challenge)
        } else if method == NSURLAuthenticationMethodClientCertificate {
            //认证客户端证书
            return self.sendClientCer()
        } else {
            //其他情况,不通过验证
            return (.cancelAuthenticationChallenge, nil)
        }
    }
}
//不做任何验证,直接信任服务器
private func trustServer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
    let disposition = URLSession.AuthChallengeDisposition.useCredential
    let credential = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
    return (disposition, credential)
}
//验证服务器证书
private func trustServerWithCer(challenge: URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?) {
    var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
    var credential: URLCredential?
    //获取服务器发送过来的证书
    let serverTrust: SecTrust = challenge.protectionSpace.serverTrust!
    let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)!
    let remoteCertificateData = CFBridgingRetain(SecCertificateCopyData(certificate))!
    //加载本地CA证书
    let cerPath = Bundle.main.path(forResource: "server", ofType: "der")!
    let cerUrl = URL(fileURLWithPath: cerPath)
    let localCertificateData = try! Data(contentsOf: cerUrl)
    if (remoteCertificateData.isEqual(localCertificateData) == true) {
        //服务器证书验证通过
        disposition = URLSession.AuthChallengeDisposition.useCredential
        credential = URLCredential(trust: serverTrust)
    } else {
        //服务器证书验证失败
        disposition = URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge
    }
    return (disposition, credential)
}
//发送客户端证书交由服务器验证
private func sendClientCer() -> (URLSession.AuthChallengeDisposition, URLCredential?) {
    let disposition = URLSession.AuthChallengeDisposition.useCredential
    var credential: URLCredential?
    //获取项目中P12证书文件的路径
    let path: String = Bundle.main.path(forResource: "你本地的p12证书文件名", ofType: "p12")!
    let PKCS12Data = NSData(contentsOfFile: path)!
    let key: NSString = kSecImportExportPassphrase as NSString
    let options: NSDictionary = [key: "p12证书的密码"] //客户端证书密码
    var items: CFArray?
    let error = SecPKCS12Import(PKCS12Data, options, &items)
    if error == errSecSuccess {
        let itemArr = items! as Array
        let item = itemArr.first!
        let identityPointer = item["identity"];
        let secIdentityRef = identityPointer as! SecIdentity
        let chainPointer = item["chain"]
        let chainRef = chainPointer as? [Any]
        credential = URLCredential.init(identity: secIdentityRef, certificates: chainRef, persistence: URLCredential.Persistence.forSession)
    }
    return (disposition, credential)
3.3、创建网络服务基类

注意:该基类接收的是“泛型”数据

  • “泛型” S,即服务器返回的具体数据(“泛型” T 的 data 字段),不包括 ApiErrorModel
  • ApiErrorModel 数据位于“泛型” T内(T 继承了 BaseResponseWrapper)
class BaseService> {

    // 定义请求成功回调
    typealias Success = (_ data: S?) -> Void
    // 定义请求失败回调
    typealias Failure = (_ msg: String) -> Void

    // 定义 DataRequest,方便子类操控
    var request: DataRequest? = nil
    // 防止重复请求
    var isRunning = false
    // 请求参数
    var parameters: [String: Any]? = nil
    // 保存请求成功回调
    var success: Success? = nil
    // 保存请求失败回调
    var failure: Failure? = nil

    // 初始化
    init() {
        // 配置 https 证书
        setAlamofireHttps()
    }

    // 执行网络请求(有请求参数)
    // parameters:请求参数
    // success:请求成功回调
    // failure:请求失败回调(默认在主线程显示一个 Toast 提示)
    func start(parameters: [String: Any]?, success: Success?, failure: Failure? = { (msg: String) -> Void in
        DispatchQueue.main.async {
            UIApplication.shared.windows.last?.makeToast(msg, duration: 1.0)
        }
    }) {
        // 设置当前请求的参数
        self.parameters = parameters
        // 调用执行网络请求(无请求参数)
        start(success: success, failure: failure)
    }

    // 执行网络请求(无请求参数)
    // success:请求成功回调
    // failure:请求失败回调(默认在主线程显示一个 Toast 提示)
    func start(success: Success?, failure: Failure? = { (msg: String) -> Void in
        DispatchQueue.main.async {
            UIApplication.shared.windows.last?.makeToast(msg, duration: 1.0)
        }
    }) {
        if isRunning {
            // 正在请求中,禁止再次请求
            return
        }
        // 设置参数:在已传进来的参数的基础上,设置一些共用参数或者网络服务子类特有的参数
        setParameters()
        isRunning = true
        self.success = success
        self.failure = failure
        // 正式发送网络请求
        request = Alamofire.request(
                        BASE_URL + requestUrl(), // 设置请求url:host + 网络服务子类自己的请求路径
                        method: requestMethod(), // 设置请求方法:网络服务子类自己的请求方法
                        parameters: parameters, // 设置请求参数:[String: Any]? 类型
                        encoding: URLEncoding.httpBody, // 设置URL编码,即把请求参数拼接在url后面,还是放在Body里面
                        headers: requestHeader()) // 设置请求Header:设置一些共用Header或者网络服务子类特有的Header
                // 设置请求结果回调,在回调里解析服务器的返回信息
                .responseString { response in
                    debugPrint("url:", BASE_URL + self.requestUrl())
                    debugPrint("parameters:", self.parameters?.description ?? "")
                    debugPrint("headers:", self.requestHeader()?.description ?? "")
                    debugPrint("response:", response.description)
                    debugPrint()
                    // 重置请求参数,防止下次请求被再次使用
                    self.parameters = nil
                    // 关键地方:把服务器返回的Json字符串解析为指定的泛型类(继承 BaseResponseWrapper)
                    let data = Mapper(context: nil, shouldIncludeNilValues: true).map(JSONString: response.value ?? "")
                    if response.result.isSuccess {
                        // 网络请求处理成功
                        if let data1 = data {
                            // 服务器有返回信息,调用请求完成的方法
                            self.requestFinished(data1)
                            self.isRunning = false
                        } else {
                            // 服务器无返回信息,调用请求失败的方法
                            failure?(ApiErrorType.INTERNAL_SERVER_ERROR.msg)
                            self.isRunning = false
                        }
                    } else {
                        // 网络请求处理失败,调用请求失败的方法
                        self.requestFailed(response)
                        self.isRunning = false
                    }
                }
    }

    // 请求路径:这里需要网络服务子类自己重写,自己配置自己对应的路径,比如“/login”
    func requestUrl() -> String {
        return ""
    }

    // 请求方法:默认POST,但网络服务子类可以自己重写,自己配置
   func requestMethod() -> HTTPMethod {
        return HTTPMethod.post
    }

    // 请求Header:可以在这里配置所有请求共通的Header,但网络服务子类可以自己重写,自己配置自己特有的Header
   func requestHeader() -> [String: String]? {
        return [
            "token": getUserData()?.token ?? "",
            "timestamp": String(Int(NSDate().timeIntervalSince1970 * 1000))
        ]
    }

    // 请求参数:其中parameters可以通过start方法配置,然后在此基础上,再给parameters配置一些所有请求共通的参数
    fileprivate func setParameters() {
        if parameters == nil {
            parameters = [String: Any]()
        }
        // 给parameters再配置一些所有请求共通的参数
        parameters?.merge([
            "channelId": "3"
        ], uniquingKeysWith: { (current, new) -> Any in
            return new
        })
    }

    // 请求完成的方法
    // 参数 data:T 即对服务器返回的Json数据进行解析后的BaseResponseWrapper的实现类
    private func requestFinished(_ data: T) {
        // 判断BaseResponseWrapper的ApiErrorModel的错误代码,这里“1”表示服务器处理正常
        if (1 == data.reqResult?.code) {
            // 调用请求成功的回调
            success?(data.getData())
            return
        }
        if (-9001 == data.reqResult?.code || -9002 == data.reqResult?.code || -9002 == data.reqResult?.code) {
            // 需要重新登陆
            clearUserData()
        }
        // 调用请求失败的回调
        failure?(data.reqResult?.msg ?? ApiErrorType.UNEXPECTED_ERROR.msg)
    }

    // 请求失败的方法
    private func requestFailed(_ response: DataResponse) {
        let apiErrorType: ApiErrorType = ApiErrorType.valueOf(rawValue: 0)
        // 调用请求失败的回调
        failure?(response.value ?? apiErrorType.msg)
    }
}
3.4、创建网络服务实现类
// 通过短信验证码登陆,注意配置的枚举类型,这里是LoginModel,并使用ResponseWrapper
class LoginBySmsService: BaseService> {
    static let instance = LoginBySmsService()

    func start(telNo: String, verfCode: String, success: Success?) {
        super.start(parameters: ["telNo": telNo, "verfCode": verfCode], success: success)
    }

    override func requestUrl() -> String {
        // BASE_URL 后面已拼写 “/”,这里就不需要了
        return "loginBySms"
    }
}

// 退出登陆,注意配置的枚举类型,这里是String,并使用ResponseStringWrapper
class LogoutService: BaseService {
    static let instance = LogoutService()

    override func requestUrl() -> String {
        // BASE_URL 后面已拼写 “/”,这里就不需要了
        return "logoutPro"
    }
}

// 获取用户列表,注意配置的枚举类型,这里是User数组,并使用ResponseListWrapper
class GetUserListService: BaseService<[User], ResponseListWrapper> {
    static let instance = GetUserListService()

    override func requestUrl() -> String {
        // BASE_URL 后面已拼写 “/”,这里就不需要了
        return "getUserList"
    }
}
3.5、使用网络服务类发送Http请求
// 登陆
LoginBySmsService.instance.start(telNo: telNo, verfCode: verfCode,
        success: { (data: LoginModel?) -> Void in
            saveUserData(data: data.user)
            DispatchQueue.main.async {
                UIApplication.shared.windows.last?.makeToast("登录成功:用户" + (data?.user?.name ?? "") + ",欢迎您的登录", duration: 2.0)
            }
        })

// 获取用户列表
GetUserListService.instance.start(
        success: { (data: [User]?) -> Void in
            self.userList = data ?? [User]()
            DispatchQueue.main.async {
                self.tableView?.reloadData()
            }
        })

// 退出登陆
LogoutService.instance.start(
        success: { (data: String?) -> Void in
            clearUserData()
            DispatchQueue.main.async {
                UIApplication.shared.windows.last?.makeToast("退出成功", duration: 1.0)
                self.pop()
            }
        })

你可能感兴趣的:(IOS:Alamofire网络框架)