swift——项目的基础架构之网络篇—Moya

最近要做新项目,原来的项目框架老了也该升级了。了解了下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(), 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()
 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.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, done: @escaping MoyaProvider.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, 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(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文件里写测试地址/正式地址,接口名。这样子要找接口,改接口一目了然。

swift——项目的基础架构之网络篇—Moya_第1张图片
项目截图

bsNetworkAPI文件代码大致如下

import UIKit

#if DEBUG
let Using_URL = "" //测试地址
#else
let Using_URL = "" //正式地址
#endif

/// 登陆
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, done: @escaping MoyaProvider.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, 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(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(), 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)