美文网首页iOS备忘录SwiftiOS学习开发
swift——项目的基础架构之第三方框架-Moya篇

swift——项目的基础架构之第三方框架-Moya篇

作者: Bart_Simpson | 来源:发表于2017-08-15 11:41 被阅读316次

    最近要做新项目,原来的项目框架老了也该升级了。了解了下Moya,发现异常的好用。很好的把接口,接口所需参数,网络请求时的自定义设置等集中到了一起。修改和增加都非常清晰。

    导入就不说了,贴上Github网址——Moya

    Moya的基本思想

    我们希望一些网络抽象层能够直接充分地封装实际调用Alamofire。这应该是简单的,常见的事情是容易的,但足够全面,复杂的事情也很容易。

    如果你使用Alamofire来抽象出来URLSession,为什么不用东西去抽象出URL,参数等等呢?

    Moya的一些很棒的功能:

    • 编译时检查正确的API端点访问。
    • 让您定义具有相关枚举值的不同端点并且清晰的使用。
    • 将测试桩作为一流公民,使单元测试非常简单。

    使用Moya

    首先创建一个枚举来存放你所有的API

    //API 参数列表
    enum bsAPI {
        case getRequest
        case Login(phoneNumber: String, password: String)
        case testNetwork
    }
    

    对你的枚举(bsAPI)进行扩展,并遵循TargetType协议。这里面会把请求的网址,路径参数,基本参数,请求类型等等全部定义好。

    extension bsAPI: TargetType {
        
        //网址
        var baseURL: URL{
            return URL.init(string: Using_URL)!
        }
        
        //内容拼接
        var path:String{
            return ""
        }
        
        var method: Moya.Method{
            switch self {
            case .getRequest:
                return .get
            default:
                return .post
            }
        }
        
        var parameters: [String: Any]?{
            
            switch self {
            case .Login(let phoneNumber, let password):
                let words = EncodeAction(dic: ["phoneNumber":phoneNumber, "password":password], key: FUNC_Login.md5())  /// 这是我的加密方法,自己Demo就随便一个字典就好
                return ["func":"", "words":words]
                
            case .testNetwork:
                let words: String = EncodeAction(dic: Dictionary<String, Any>(), key: FUNC_Test.md5())  /// 这是我的加密方法,自己Demo就随便一个字典就好
                return ["words":words]
                
            default:
                return nil
            }
        }
        
        var parameterEncoding: ParameterEncoding {
            return URLEncoding.default
        }
        
        /// Provides stub data for use in testing.
        var sampleData: Data {
            return "".data(using: .utf8)!
        }
        
        /// The type of HTTP task to be performed.
        var task: Task {
            return .request
        }
        
        /// Whether or not to perform Alamofire validation. Defaults to `false`.
        var validate: Bool {
            return false
        }
    }
    

    协议定义好了之后使用就异常简单, 定义一个类型就可以开始使用了。

    let provider = MoyaProvider<bsAPI>()
     provider.request(.Show) { result in
                // do something with result
     }
    

    但是这是最基础的用法,MoyaProvider是提供很多属性的,下面的写法就为你的网络请求添加了请求时状态栏转圈(状态栏菊花加载)和网络请求页面菊花加载。这样就不用每个请求的时候都带上MBProgressHUD的使用了。

    public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
                    requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
                    stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
                    manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(),
                    plugins: [PluginType] = [],
                    trackInflights: Bool = false) {
    
            self.endpointClosure = endpointClosure
            self.requestClosure = requestClosure
            self.stubClosure = stubClosure
            self.manager = manager
            self.plugins = plugins
            self.trackInflights = trackInflights
        }
    

    这是MoyaProvider的默认初始化方法,我们会对requestClosure和plugins这两个属性作出自定义。

    requestClosure的自定义

    let requestTimeoutClosure = { (endpoint: Endpoint<bsAPI>, done: @escaping MoyaProvider<bsAPI>.RequestResultClosure) in
        
        guard var request = endpoint.urlRequest else { return }
        request.timeoutInterval = 10    //设置请求超时时间
        done(.success(request))
        
    }
    

    plugins的自定义

    let networkPlugin = NetworkActivityPlugin { (type) in
        switch type {
        case .began:
            /// 状态栏转圈
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
            
        case .ended:
            /// 状态栏停止转圈
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
        }
    }
    

    这里用到的是Moya的网络监控插件,NetworkActivityPlugin,继承自PluginType。定义了状态栏的网络加载。下面要自定义一个继承自PluginType,网络请求时页面加载的插件。

    class RequestLoadingPlugin: PluginType {
        
        var HUD:MBProgressHUD = MBProgressHUD.init()
        
        func willSend(_ request: RequestType, target: TargetType) {
            print("开始请求")
            
            if let keyViewController = UIApplication.shared.keyWindow?.rootViewController {
                
                HUD.mode = MBProgressHUDMode.indeterminate
                HUD.bezelView.color = UIColor.lightGray
                HUD.removeFromSuperViewOnHide = true
                HUD.backgroundView.style = .solidColor 
                HUD = MBProgressHUD.showAdded(to: keyViewController.view, animated: true)
                
            }
        }
        
        func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
            print("结束请求")
            HUD.hide(animated: true, afterDelay: 0)
    
            guard case Result.failure(_) = result else {
                return
            }
            /// 请求失败
            let errorReason: String = (result.error?.errorDescription)!
            print("请求失败: \(errorReason)")
            /// 没网 "The Internet connection appears to be offline."
            /// 连接不到服务器 "Could not connect to the server."
            var tip = "请求失败!"
            if errorReason.contains("The Internet connection appears to be offline") {
                tip = "网络不给力,请检查您的网络"
            }
            if errorReason.contains("Could not connect to the server") {
                tip = "无法连接服务器"
            }
            /// 使用tip文字 进行提示
        }
    }
    

    然后初始化MoyaProvider的时候带上自定义的插件

    let bsProvider = MoyaProvider<bsAPI>(requestClosure: requestTimeoutClosure , plugins: [RequestLoadingPlugin(), networkPlugin])
    

    大功告成!!!
    然后去简单使用下

    bsProvider.request(bsAPI.testNetwork) { (result) in
                switch result{
                case let .success(res):
                    print("======\(res)======")
                    let responseDict = JSON(res.data)  ///这里是导入了SwiftyJSON
                    print(responseDict)
                    
                case let .failure(error):
                    print("=====\(error)=========")
                    
                }
            }
    

    我是把API的定义和Manager分开了,在bsNetworkAPI文件里写测试地址/正式地址,接口名。这样子要找接口,改接口一目了然。

    项目截图

    bsNetworkAPI文件代码大致如下

    import UIKit
    
    
    let Dev_URL = "" //测试地址
    let Dis_URL = "" //正式地址
    
    //let Using_URL = Dev_URL /// 当前为测试环境
    let Using_URL = Dis_URL /// 当前为正式环境
    
    /// 登陆
    let FUNC_Login = "login"
    
    /// 测试
    let FUNC_Test = "test"
    

    bsNetworkManager里的代码就是上文里面拆开讲的那部分,给大家一个合集,这样更方便学习。我就比较喜欢全部的代码都给我,然后我在去自己拆解来看。

    import UIKit
    import Moya
    import Result
    import MBProgressHUD
    import SwiftyJSON
    
    //API 参数列表
    enum bsAPI {
        
        case getRequest
        case Login(phoneNumber: String, password: String)
        case testNetwork
        
    }
    
    let networkPlugin = NetworkActivityPlugin { (type) in
        switch type {
        case .began:
            /// 状态栏转圈
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
            
        case .ended:
            /// 状态栏停止转圈
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
        }
    }
    
    let requestTimeoutClosure = { (endpoint: Endpoint<bsAPI>, done: @escaping MoyaProvider<bsAPI>.RequestResultClosure) in
        
        guard var request = endpoint.urlRequest else { return }
        request.timeoutInterval = 10    //设置请求超时时间
        done(.success(request))
        
    }
    
    class RequestLoadingPlugin: PluginType {
        
        var HUD:MBProgressHUD = MBProgressHUD.init()
        
        func willSend(_ request: RequestType, target: TargetType) {
            print("开始请求")
            
            if let keyViewController = UIApplication.shared.keyWindow?.rootViewController {
                
                HUD.mode = MBProgressHUDMode.indeterminate
                HUD.bezelView.color = UIColor.lightGray
                HUD.removeFromSuperViewOnHide = true
                HUD.backgroundView.style = .solidColor
                HUD = MBProgressHUD.showAdded(to: keyViewController.view, animated: true)
                
            }
        }
        
        func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
            print("结束请求")
            HUD.hide(animated: true, afterDelay: 0)
    
            /// 请求失败
            let errorReason: String = (result.error?.errorDescription)!
            print("请求失败: \(errorReason)")
            /// 没网 "The Internet connection appears to be offline."
            /// 连接不到服务器 "Could not connect to the server."
            var tip = "请求失败!"
            if errorReason.contains("The Internet connection appears to be offline") {
                tip = "网络不给力,请检查您的网络"
            }
            if errorReason.contains("Could not connect to the server") {
                tip = "无法连接服务器"
            }
            /// 使用tip文字 进行提示
        }
    }
    
    let bsProvider = MoyaProvider<bsAPI>(requestClosure: requestTimeoutClosure , plugins: [RequestLoadingPlugin(), networkPlugin])
    
    extension bsAPI: TargetType {
        
        //网址
        var baseURL: URL{
            return URL.init(string: Using_URL)!
        }
        
        //内容拼接
        var path:String{
            return FUNC_Test
        }
        
        var method: Moya.Method{
            switch self {
            case .getRequest:
                return .get
            default:
                return .post
            }
        }
        
        var parameters: [String: Any]?{
            
            switch self {
            case .Login(let phoneNumber, let password):
                let words = EncodeAction(dic: ["phoneNumber":phoneNumber, "password":password], key: FUNC_Login.md5())
                return ["func":"", "words":words]
                
            case .testNetwork:
                let words: String = EncodeAction(dic: Dictionary<String, Any>(), key: FUNC_Test.md5())
                return ["words":words]
                
            default:
                return nil
            }
        }
        
        var parameterEncoding: ParameterEncoding {
            return URLEncoding.default
        }
        
        /// Provides stub data for use in testing.
        var sampleData: Data {
            return "".data(using: .utf8)!
        }
        
        /// The type of HTTP task to be performed.
        var task: Task {
            return .request
        }
        
        /// Whether or not to perform Alamofire validation. Defaults to `false`.
        var validate: Bool {
            return false
        }
    }
    

    还有疑问可以去Github看看官方教程或下载官方例子——Moya

    如果有对MBProgressHUD的那部分有疑问的,可以去iOS版 - MBProgressHUD设置背景方框为透明

    转载请注明出处,谢谢。

    相关文章

      网友评论

        本文标题:swift——项目的基础架构之第三方框架-Moya篇

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