小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
前言
这两天在看看自己的项目中Moya的插件可不可以优化一下。
有的时候看见Moya这个库,就会想究竟是怎么的思维迸发才会想到这样编写网络请求库。
我们总是在谈Alamofire,固然0→1是一个无比艰难又伟大的壮举。
不过我觉得Moya就是一个1→100的过程,它让Swift的网络请求前无古人后无来者。
PluginType协议
我们先来看看Moya中的PluginType协议源码:
/// A Moya Plugin receives callbacks to perform side effects wherever a request is sent or received.
///
/// for example, a plugin may be used to
/// - log network requests
/// - hide and show a network activity indicator
/// - inject additional information into a request
public protocol PluginType {
/// Called to modify a request before sending.
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest
/// Called immediately before a request is sent over the network (or stubbed).
func willSend(_ request: RequestType, target: TargetType)
/// Called after a response has been received, but before the MoyaProvider has invoked its completion handler.
func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType)
/// Called to modify a result before completion.
func process(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> Result<Moya.Response, MoyaError>
}
Moya的插件机制实际上就是一个遵守协议的机制,如果自己想写一个Moya插件,那么遵守这个协议,并实现就好了,而且这个协议也做了默认实现,也就是说这么写也是可以的,就是没什么意义:
class LoadingPlugin: PluginType {
}
Moya的内置Plugin——NetworkActivityPlugin和NetworkLoggerPlugin
其实Moya已经很贴心的给我们准备了两个非常好用的插件,NetworkActivityPlugin和NetworkLoggerPlugin。从名字上面我们就可以看出其用途。
NetworkActivityPlugin
这个插件的用途就是用于在网络请求前后,添加loading页面的,我们看看插件的具体代码就可以知道:
/// Called by the provider as soon as the request is about to start
public func willSend(_ request: RequestType, target: TargetType) {
networkActivityClosure(.began, target)
}
/// Called by the provider as soon as a response arrives, even if the request is canceled.
public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
networkActivityClosure(.ended, target)
}
在初始化插件的时候,通过判断began和ended的状态,去增加loading与去掉loading。
可以说是,通过插件的方式,让网络请求的数据层面和UI的页面变化进行了隔离,互不干扰,也便于维护。
NetworkLoggerPlugin
说到网络请求,在App开发中不可避免的就是打印JSON。
在Alamofire中,其内部就有一个打印的方案,是通过Notification实现的,而在Moya中是通过Plugin实现的。
这个功能没什么特别说明的,直接集成就好。
插件使用
activityPlugin:
let activityPlugin = NetworkActivityPlugin { (state, targetType) in
switch state {
case .began:
// 显示loading,自行补全逻辑
case .ended:
// 关闭loading,自行补全逻辑
}
}
loggerPlugin:
let loggerPlugin = NetworkLoggerPlugin()
在provider中集成:
let myProvider = MoyaProvider<MyService>(plugins: [loggerPlugin, activityPlugin])
一个Plugin一个功能
其实从本质上,完全可以把loading的业务和logger的打印放在一个Plugin中完成,那么为何Moya要拆分成为2个。
这里我认为是,Moya更倾向于把功能拆解成为一个个小功能,并且让每个小功能都进行解耦,这样的话,在使用中的取舍中更加灵活。
比如我只想在Debug模式下打印JSON,而Release模式下不打印,那么loggerPlugin我在Release下不使用即可。
记住一个Plugin,一个功能。
PluginType协议与拦截器
看着PluginType协议的函数名称和注释:
-
prepare:
Called modify a request before sending
-
willSend:
Called immediately before a request is sent over the network (or stubbed)
-
didReceive:
Called after a response has been received, but before the MoyaProvider has invoked its completion handler
-
process:
Called to modify a result before completion.
其实描述的就是网络请求的声明周期中,可以搞点事情嘛,这不就是Android、Flutter开发中的网络请求的拦截器嘛!
下面的代码是Dart中的来自Dio:
extension AddPrettyPrint on Dio {
Dio get addPrettyPrint {
this.interceptors.add(
PrettyDioLogger(
requestHeader: false,
requestBody: true,
responseBody: true,
responseHeader: false,
compact: false,
),
);
return this;
}
}
static Dio _dio = Dio(
BaseOptions(
baseUrl: Api.baseUrl,
connectTimeout: timeout,
receiveTimeout: timeout,
headers: {},
),
).addPrettyPrint;
这段代码就是为Dio添加了一个PrettyDioLogger拦截器,然后在网络请求的request和response的时候可以打印相关网络请求的信息,是不是和Moya的NetworkLoggerPlugin非常相似呢?
面向协议编程
Moya是一个面向协议编程非常好的蓝本,而我觉得面向协议的核心在于:
-
只关心这个方法实现了没有,至于怎么实现,不关心
实现变成了Coder自己的事情,灵活性强。
-
用强有力的约束让Coder按照规则做事,不遵守规则,对不起,报错
约束变成了铁律,减少编码错误。
-
协议可以看成是变相的多继承
展平类型与类型的区别,可以通过协议类型去约束泛型与函数类型,使得编程更加简洁与灵活,同时也增加了理解难度。
有的时候,写面向协议编程久了,也会怀念面向对象编程。
为何,因为面向协议编程没有继承。
比如我有一个A业务,有一个B业务,其中B业务就是比A业务多一个方法。
面向对象话class B: A
,而面向协议struct A: Protocol, struct B: Protocol
。
struct
没有办法继承,页面B明明只是在页面A的基础上加一点功能,B只能在Copy一份A代码后,再加功能。
这里我并不想鼓吹面向协议编程或者面向对象编程,存在即合理,灵活运用才是关键。
再说面向协议编程也不是Swift独树一帜,看看隔壁Dart就知道了,extends、implements、with
应有尽有,而我们只有一个:
。
class PAAppBar extends StatelessWidget implements PreferredSizeWidget {
}
参考文档
总结
本文从Moya抽象的PluginType协议聊到具体的两个Plugin:NetworkActivityPlugin和NetworkLoggerPlugin,并讲解了其使用方法。
对比了PluginType协议和Dio拦截器的一些异同。
最后我们又回到了协议本身,说明了面向协议编程的特点。
希望对大家有所帮助。