Moya源码解析

Moya是一个基于Swift的高度抽象网络库,通过Target定义网络请求,利用Provider发起请求。本文详细解析了Moya的Target、Provider、EndpointClosure、RequestClosure、StubClosure、Manager和Plugin的实现,展示了其灵活性和易测试性。同时,文章讨论了Moya的优缺点,包括枚举定义Target可能导致的代码碎片化和维护困难等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Moya是一个高度抽象的网络库,他的理念是让你不用关心网络请求的底层的实现细节,只用定义你关心的业务。且Moya采用桥接和组合来进行封装(默认桥接了Alamofire),使得Moya非常好扩展,让你不用修改Moya源码就可以轻易定制。官方给出几个Moya主要优点:

  • 编译时检查API endpoint权限
  • 让你使用枚举定义各种不同Target, endpoints
  • 把stubs当做一等公民对待,因此测试超级简单。

Target

开始Moya之旅的第一步便是,建立一个Enum的Target,这个Target便是你网络请求相关行为的定义。Target必须实现TargetType协议。

public protocol TargetType {
    var baseURL: NSURL { get }
    var path: String { get }
    var method: Moya.Method { get }
    var parameters: [String: AnyObject]? { get }
    var sampleData: NSData { get }
} 

例如有一个AccountAPI模块,模块实现注册登录的功能。所以第一件事情,我们需要定义一个Target

enum AccountAPI {
    case Login(userName: String, passwd: String)
    case Register(userName: String, passwd: String)
}

extension AccountAPI: TargetType {
    var baseURL: NSURL {
        return NSURL(string: "https://www.myapp.com")!
    }

    var path: String {
        switch self {
        case .Login:
            return "/login"
        case .Register:
            return "/register"
        }
    }

    var method: Moya.Method {
        return .GET
    }

    var parameters: [String: AnyObject]? {
        switch self {
        case .Login:
            return nil
        case .Register(let userName, let passwd):
            return ["username": userName, "password": passwd]
        }
    }

    var sampleData: NSData {
        switch self {
        case .Login:
            return "{'code': 1,6'Token':'123455'}".dataUsingEncoding(NSUTF8StringEncoding)!
        case .Register(let userName, let passwd):
            return "找不到数据"
        }
    }
}

主要是实现了TargetType协议,里面的网址和内容,是随便写的,可能不make sence(不合理), 但 仅仅是做一个例子而已。

Providers

Providers是Moya中的核心,Moya中所有的API请求都是通过Provider来发起的。因此大多数时候,你的代码请求像这样:

let provider = MoyaProvider<AccountAPI>()
provider.request(.Login) { result in
    // `result` is either .Success(response) or .Failure(error)
}

我们初始化了一个AccountAPI的Provider,并且调用了Login请求。怎么样?干净简单吧!

从Provider的构造函数说起

Provider真正做的事情可以用一个流来表示:Target -> Endpoint -> Request 。在这个例子中,它将AccountAPI转换成Endpoint, 再将其转换成为NSRURLRequest。最后将这个NSRURLRequest交给Alamofire去进行网络请求。

我们从Provider的构造函数开始切入,一步一步地扒开它。

//Moya.swift

public init(endpointClosure: EndpointClosure = MoyaProvider.DefaultEndpointMapping,
        requestClosure: RequestClosure = MoyaProvider.DefaultRequestMapping,   
        stubClosure: StubClosure = MoyaProvider.NeverStub,
        manager: Manager = MoyaProvider<Target>.DefaultAlamofireManager(),
        plugins: [PluginType] = []) 
  1. 首先我们发现的是3个Closure:endpointClosure、requestClosure、stubClosure。这3个Closure是让我们定制请求和进行测试时用的。非常有用,后面细说。

  2. 然后是一个Manager,Manager是真正用来网络请求的类,Moya自己并不提供Manager类,Moya只是对其他网络请求类进行了简单的桥接。这么做是为了让调用方可以轻易地定制、更换网络请求的库。比如你不想用Alamofire,可以十分简单的换成其他库

  3. 最后是一个类型为PluginType的数组。Moya提供了一个插件机制,使我们可以建立自己的插件类来做一些额外的事情。比如写Log,显示“菊花”等。抽离出Plugin层的目的,就是让Provider职责单一,满足开闭原则。把和自己网络无关的行为抽离。避免各种业务揉在一起不利于扩展。

先来看看第一个EndpointClosure

EndpointClosure

//Moya.swift

public typealias EndpointClosure = Target -> Endpoint<Target>

EndpointClosure这个闭包,输入是一个Target,返回Endpoint。这就是我们前面说的Target -> Endpoint的转换,那么Endpoint是个什么鬼?
Endpoint 是Moya最终进行网络请求前的一种数据结构,它保存了这些数据:

  • URL
  • HTTP请求方式 (GET, POST, etc).
  • 本次请求的参数
  • 参数的编码方式 (URL, JSON, custom, etc).
  • stub数据的 response(测试用的)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值