RxNetworks
RxNetworks 是一个用于 Swift 的声明式和响应式网络库。为 Swift 5 开发,旨在利用最新的语言特性。该框架的最终目标是实现易于使用的网络交互功能,使其易于编写可维护的代码。
🧚. RxSwift + Moya + HandyJSON + 插件👒👒👒
英语 | 简体中文
这是一组基于 RxSwift + Moya
建立的基础设施
特性
目前,RxNetworks 最重要的特性可以总结如下
- 支持与 RxSwift 结合使用的响应式网络请求。
- 支持面向对象和网络请求,也支持 POP 网络请求。
- 支持使用 HandyJSON 进行数据解析。
- 支持配置通用请求、路径等。
- 支持对 Moya 的各种网络插件进行简单自定义。
- 支持注入默认插件。
- 提供了 6 个插件供您使用。
MoyaNetwork
此模块基于Moya封装的网络API架构。
- 主要分为8部分
- 网络配置:在程序开始时设置配置信息。
- addDebugging:是否默认引入调试模式插件。
- baseURL:根路径地址到基本URL。
- baseParameters:默认基本参数,例如:userID,token等。
- baseMethod:默认请求方法类型。
- updateBaseParametersWithValue:更新默认基本参数值。
- RxMoyaProvider:增加网络请求的响应性,返回
Single
序列。 - 网络工具:网络相关函数
- defaultPlugin:添加默认插件。
- transformAPIObservableJSON:转换一个
Observable
序列JSON对象。 - handyConfigurationPlugin:处理配置插件。
- PluginSubType:继承并替换Moya插件协议,便于后续扩展。
- configuration:设置网络配置信息后,此方法可用于诸如本地缓存存在时直接抛出数据且不执行后续网络请求的场景。
- lastNever:当最后一次网络响应返回时,此方法可用于诸如键失败重新获取键然后自动重新请求网络的场景。
- 网络API:添加协议属性,基于TargetType封装基本网络请求。
- ip:根路径地址到基本URL。
- parameters:请求参数。
- plugins:设置网络插件。
- stubBehavior:是否获取测试数据。
- retry:网络请求失败重试。
- request:网络请求方法,并返回Single序列对象。
- NetworkAPI+Ext:协议默认实现方案。
- NetworkAPIOO:面向对象转换器,MVP到OOP,方便习惯OC思考的朋友。
- cdy_ip:根路径地址到基本URL。
- cdy_path:请求路径。
- cdy_parameters:请求参数。
- cdy_plugins:设置网络插件。
- cdy_testJSON:网络测试JSON字符串。
- cdy_testTime:网络测试数据返回时间,默认为半秒。
- cdy_HTTPRequest:网络请求方法,并返回Single序列对象。
- NetworkX:扩展函数等方法。
- toJSON:转换为JSON字符串。
- toDictionary:JSON字符串转换为字典。
- +=:字典拼接。
- 网络配置:在程序开始时设置配置信息。
MoyaPlugins
此模块主要基于moya包的网络相关插件
🏠 - 简单易用,在API协议中实现协议方法,然后将其添加到插件中
var plugins: APIPlugins {
let cache = NetworkCachePlugin(cacheType: .networkElseCache)
let loading = NetworkLoadingPlugin.init(delay: 0.5)
let warning = NetworkWarningPlugin.init()
warning.changeHud = { (hud) in
hud.detailsLabel.textColor = UIColor.yellow
}
return [loading, cache, warning]
}
HandyJSON
本模块基于 HandyJSON
包进行网络数据解析
- 大致分为以下3个部分
- HandyDataModel:网络外部数据模型
- HandyJSONError:解析错误相关
- RxHandyJSON:HandyJSON数据解析,目前提供两种解析方案
- 方案1:将
HandyDataModel
模型结合来解析数据。 - 方案2:根据
keyPath
解析指定键的数据,前提是json数据源必须是字典形式。
- 方案1:将
🎷 - 网络部分的联合使用示例
func request(_ count: Int) -> Driver<[CacheModel]> {
CacheAPI.cache(count).request()
.asObservable()
.mapHandyJSON(HandyDataModel<[CacheModel]>.self)
.compactMap { $0.data }
.observe(on: MainScheduler.instance)
.delay(.seconds(1), scheduler: MainScheduler.instance)
.asDriver(onErrorJustReturn: [])
}
使用示例
提供一些测试用例供参考。🚁
- OO示例1
class OOViewModel: NSObject {
struct Input {
let retry: Int
}
struct Output {
let items: Observable<String>
}
func transform(input: Input) -> Output {
return Output(items: input.request())
}
}
extension OOViewModel.Input {
func request() -> Observable<String> {
var api = NetworkAPIOO.init()
api.cdy_ip = NetworkConfig.baseURL
api.cdy_path = "/ip"
api.cdy_method = APIMethod.get
api.cdy_plugins = [NetworkLoadingPlugin()]
api.cdy_retry = self.retry
return api.cdy_HTTPRequest()
.asObservable()
.compactMap{ (($0 as! NSDictionary)["origin"] as? String) }
.catchAndReturn("")
.observe(on: MainScheduler.instance)
}
}
- MVP示例2
enum LoadingAPI {
case test2(String)
}
extension LoadingAPI: NetworkAPI {
var ip: APIHost {
return NetworkConfig.baseURL
}
var path: String {
return "/post"
}
var parameters: APIParameters? {
switch self {
case .test2(let string): return ["key": string]
}
}
}
class LoadingViewModel: NSObject {
let disposeBag = DisposeBag()
let data = PublishRelay<NSDictionary>()
/// Configure the loading animation plugin
let APIProvider: MoyaProvider<MultiTarget> = {
let configuration = URLSessionConfiguration.default
configuration.headers = .default
configuration.timeoutIntervalForRequest = 30
let session = Moya.Session(configuration: configuration, startRequestsImmediately: false)
let loading = NetworkLoadingPlugin.init()
return MoyaProvider<MultiTarget>(session: session, plugins: [loading])
}()
func loadData() {
APIProvider.rx.request(api: LoadingAPI.test2("666"))
.asObservable()
.subscribe { [weak self] (event) in
if let dict = event.element as? NSDictionary {
self?.data.accept(dict)
}
}.disposed(by: disposeBag)
}
}
- MVVM示例3
class CacheViewModel: NSObject {
struct Input {
let count: Int
}
struct Output {
let items: Observable<[CacheModel]>
}
func transform(input: Input) -> Output {
let items = request(input.count).asObservable()
return Output(items: items)
}
}
extension CacheViewModel {
func request(_ count: Int) -> Observable<[CacheModel]> {
CacheAPI.cache(count).request()
.mapHandyJSON(HandyDataModel<[CacheModel]>.self)
.compactMap { $0.data }
.observe(on: MainScheduler.instance) // the result is returned on the main thread
.catchAndReturn([]) // return null on error
}
}
- 链式调用示例4
class ChainViewModel: NSObject {
let disposeBag = DisposeBag()
let data = PublishRelay<NSDictionary>()
func chainLoad() {
requestIP()
.flatMapLatest(requestData)
.subscribe(onNext: { [weak self] data in
self?.data.accept(data)
}, onError: {
print("Network Failed: \($0)")
}).disposed(by: disposeBag)
}
}
extension ChainViewModel {
func requestIP() -> Observable<String> {
return ChainAPI.test.request()
.asObservable()
.map { ($0 as! NSDictionary)["origin"] as! String }
.catchAndReturn("") // Exception thrown
}
func requestData(_ ip: String) -> Observable<NSDictionary> {
return ChainAPI.test2(ip).request()
.asObservable()
.map { ($0 as! NSDictionary) }
.catchAndReturn(["data": "nil"])
}
}
- 批处理示例5
class BatchViewModel: NSObject {
let disposeBag = DisposeBag()
let data = PublishRelay<NSDictionary>()
/// Configure loading animation plugin
let APIProvider: MoyaProvider<MultiTarget> = {
let configuration = URLSessionConfiguration.default
configuration.headers = .default
configuration.timeoutIntervalForRequest = 30
let session = Moya.Session(configuration: configuration, startRequestsImmediately: false)
let loading = NetworkLoadingPlugin.init()
return MoyaProvider<MultiTarget>(session: session, plugins: [loading])
}()
func batchLoad() {
Observable.zip(
APIProvider.rx.request(api: BatchAPI.test).asObservable(),
APIProvider.rx.request(api: BatchAPI.test2("666")).asObservable(),
APIProvider.rx.request(api: BatchAPI.test3).asObservable()
).subscribe(onNext: { [weak self] in
guard var data1 = $0 as? Dictionary<String, Any>,
let data2 = $1 as? Dictionary<String, Any>,
let data3 = $2 as? Dictionary<String, Any> else {
return
}
data1 += data2
data1 += data3
self?.data.accept(data1)
}, onError: {
print("Network Failed: \($0)")
}).disposed(by: disposeBag)
}
}
CocoaPods
CocoaPods 是一个依赖管理器。有关使用和安装说明,请访问其网站。要使用 CocoaPods 进行集成,请在上面的 Podfile 中指定它
pod 'RxNetworks'
您应明确定义最低部署目标
platform :ios, '10.0'
如果您只想导入加载动画插件
pod 'RxNetworks/MoyaPlugins/Loading'
备注
总体过程几乎是这样的,演示也写得非常详细,您可以自己看看。🎷
提示:如果您觉得有帮助,请帮助我加个星。如果您有任何问题或需求,也可以提交。
谢谢。🎇
关于作者
- 🎷 电子邮件地址:[邮箱地址1 esports.com] 🎷
- 🎸 GitHub地址:yangKJ 🎸
许可
RxNetworks在MIT许可证下可用。更多信息请参阅LICENSE文件。