Swift 优雅的网络请求Moya

Moya

Moya是一个网络抽象层,它在底层将Alamofire进行封装,对外提供更简洁的接口供开发者调用。在Objective-C中,大部分开发者会使用AFNetwork进行网络请求,当业务复杂一些时,会对AFNetwork进行二次封装,编写一个适用于自己项目的网络抽象层。在Objective-C中,有著名的YTKNetwork,它将AFNetworking封装成抽象父类,然后根据每一种不同的网络请求,都编写不同的子类,子类继承父类,来实现请求业务。Moya在项目层次中的地位,有点类似于YTKNetwork。可以看下图对比
网络请求层级
如果单纯把Moya等同于swift版的YTKNetwork,那就是比较错误的想法了。Moya的设计思路和YTKNetwork差距非常大。上面我在介绍YTKNetwork时在强调子类和父类,继承,是因为YTKNetwork是比较经典的利用OOP思想(面向对象)设计的产物。基于swiftMoya虽然也有使用到继承,但是它的整体上是以POP思想(Protocol Oriented Programming,面向协议编程)为主导的。

面向协议

请看我的其中一篇文章有介绍到

Moya的模块组成

Moya层级

  1. Provider:provider是一个提供网络请求服务的提供者。通过一些初始化配置之后,在外部可以直接用provider来发起request。
  2. Request:在使用Moya进行网络请求时,第一步需要进行配置,来生成一个Request。首先按照官方文档,创建一个枚举,遵守TargetType协议,并实现协议所规定的属性。为什么要创建枚举来遵守协议,枚举结合switch语句,使得API管理起来比较方便。
  3. 根据创建了一个遵守TargetType协议的名为Myservice的枚举,我们完成了如下几个变量的设置。
	baseURL
	path
	method
	sampleData
	task
	headers

代码demo

//
//  YShareApI.swift
//  
//
//  Created by bruce yao on 2019/4/10.
//  Copyright © 2019 bruce yao. All rights reserved.
//

import UIKit
import Moya
import RxCocoa
import Result
import SwiftyJSON

//初始rovider
let YShareApiProvider = MoyaProvider<YShareAPI>(plugins: [RequestLoadingPlugin()])

/** 请求的endpoints)**/
//请求分类
enum YShareAPI {
    case shareNavList:
    case shareList(pageSize: Int, pageNum: Int):
}
//请求配置
extension YShareAPI: TargetType {
    //服务器地址
    public var baseURL: URL {
        switch self {
        default:
            return URL(string: HD_Search_Base)!
        }
    }
    
    //各个请求的具体路径
    public var path: String {
        switch self {
        case .shareNavList:
            return "manage/navigation/getNavigationList"
        default:
            return "ddddd/list"
        }
    }
    
    //请求类型
    public var method: Moya.Method {
        switch self {
       
        default:
            return .get
        }
    }
    
    //请求任务事件(这里附带上参数)
    public var task: Task {
        switch self {
        case .shareNavList:
            return .requestPlain
       case .shareList(let pageSize, let pageNum):
            var params: [String: Any] = [:]
            params["pageSize"] = pageSize
            params["pageNum"] = pageNum
            return .requestParameters(parameters: params, encoding: URLEncoding.default)
        }
    }
    //是否执行Alamofire验证
    public var validate: Bool {
        return false
    }
    //这个就是做单元测试模拟的数据,
//    只会在单元测试文件中有作用
    public var sampleData: Data {
        return "{}".data(using: String.Encoding.utf8)!
    }

    //请求头
    public var headers: [String: String]? {
        switch self {
        default:
            return ["Content-type": "application/json"]
        }
    }
}
//
//  RequestLoadingPlugin.swift
//  
//
//  Created by bruce yao on 2019/1/25.
//  Copyright . All rights reserved.
//

import UIKit
import Foundation
import MBProgressHUD
import Moya
import Result

class RequestLoadingPlugin: PluginType {
    
    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        print("prepare")
        var mRequest = request
        mRequest.timeoutInterval = 20
        return mRequest
    }
    func willSend(_ request: RequestType, target: TargetType) {
        print("开始请求")
        if SwiftIsShowHud == true {
            let keyViewController = UIApplication.shared.keyWindow?.rootViewController
            if (keyViewController != nil) {
                MBProgressHUD.showAdded(to: keyViewController!.view, animated: true)
            }
        }
        
    }
    
    func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
        print("结束请求")
        let keyViewController = UIApplication.shared.keyWindow?.rootViewController
        if (keyViewController != nil) {
            MBProgressHUD.hide(for: keyViewController!.view, animated: true)
            //            MBProgressHUD.
        }
        
        
       guard case Result.failure(_) = result
        else {
            let respons = result.value
            let dic: Dictionary<String, Any>? =
                try? JSONSerialization.jsonObject(with: respons!.data, options: .mutableContainers) as! Dictionary<String, Any>
            
            if dic != nil {
                if dic?.keys.contains("status") == true {
                    if dic?["status"] as! Int == 11 || dic?["status"] as! Int == 12 {
                        print("Token 失效")
                    }
                }
                
                if dic?.keys.contains("code") == true {
                    if dic?["code"] as! Int == 11 || dic?["code"] as! Int == 12 {
                        print("Token 失效")
                    }
                }
            }
            return
        }
        let errorReason: String = (result.error?.errorDescription)!
        print("请求失败:\(errorReason)")
        var tip = ""
        if errorReason.contains("The Internet connection appears to be offline") {
            tip = "网络不给力,请检查您的网络"
        }else if errorReason.contains("Could not connect to the server") {
            tip = "无法连接服务器"
        }else {
           tip = "请求失败"
        }
        /// 使用tip文字 进行提示
    }
}

Controller中使用

代码片段

import RxSwift
import RxCocoa
import ObjectMapper

YShareApiProvider
                .rx.request(input.category)
                .mapObject(YBaseModel<T>.self)
                .subscribe(onSuccess: { (baseModel) in
                    print("请求成功 返回数据如下")
                    if baseModel.status != 0 {
                        
                        return
                    }
                    
                }, onError: {error in
                    print("Error:请求错误")
                   
                }).disposed(by: self.disposeBag)
            }, onError: { (error) in
                
        }, onCompleted: {
            
        }) {
            
            }.disposed(by: disposeBag)
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值