IOS:Alamofire网络框架

作者: Alisallon | 来源:发表于2019-04-25 08:40 被阅读20次

    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<S>: 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<String> {
        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<S: Mappable>: 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
        }
    }
    
    2.9、创建包装请求结果的类(继承 BaseResponseWrapper,具体数据类型为“泛型数组”)

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

    // ⚠️泛型必须是实现Mappable的类,否则会解析失败
    class ResponseListWrapper<S: Mappable>: 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<S, T: BaseResponseWrapper<S>> {
    
        // 定义请求成功回调
        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<T>(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<String>) {
            let apiErrorType: ApiErrorType = ApiErrorType.valueOf(rawValue: 0)
            // 调用请求失败的回调
            failure?(response.value ?? apiErrorType.msg)
        }
    }
    
    3.4、创建网络服务实现类
    // 通过短信验证码登陆,注意配置的枚举类型,这里是LoginModel,并使用ResponseWrapper
    class LoginBySmsService: BaseService<LoginModel o, ResponseWrapper<LoginModel>> {
        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<String, ResponseStringWrapper> {
        static let instance = LogoutService()
    
        override func requestUrl() -> String {
            // BASE_URL 后面已拼写 “/”,这里就不需要了
            return "logoutPro"
        }
    }
    
    // 获取用户列表,注意配置的枚举类型,这里是User数组,并使用ResponseListWrapper
    class GetUserListService: BaseService<[User], ResponseListWrapper<User>> {
        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网络框架

        本文链接:https://www.haomeiwen.com/subject/yjbogqtx.html