2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > iOS开发之Moya网络请求使用与封装

iOS开发之Moya网络请求使用与封装

时间:2021-07-08 01:54:53

相关推荐

iOS开发之Moya网络请求使用与封装

一、使用

let provider = MoyaProvider<RequestApi>()provider.rx.request(.ads(position: VALUE10)).asObservable().mapString().mapObject(BaseListResponse<Ads>.self).subscribe {event inswitch event {case .next(let data):// 接口调用成功print(data.data.data![0].title)case .error(let error):// 接口报错print("error \(error)")case .completed:print("completed")}}.disposed(by: rx.disposeBag)

二、封装

1、配置网络请求

import Foundationimport Moyaenum RequestApi {// 广告列表case ads(position:Int)// 单曲列表case songscase songDetail(data:String)// 登录注册case register(data:User)case login(data:User)}// MARK: - 实现TargetType协议extension RequestApi:TargetType{var baseURL: URL {return URL(string: RequestConfig.BASE_URL)!}// 返回每个请求的路径var path: String {switch self{case .ads(_):return "v1/ads"case .songs:return "v1/songs"case .songDetail(let data):return "v1/songs/\(data)"case .register(_):return "v1/users"case .login:return "v1/sessions"default:fatalError("Request path is null")}}var method: Moya.Method {switch self{case .login:return .postdefault:return.get}}var task: Moya.Task {switch self{case .ads(let position):return .requestParameters(parameters: ["position":position], encoding: URLEncoding.default)case .login(let user):return .requestData((user.toJSONString()?.data(using: .utf8))!)case .register(let data):return .requestData(data.toJSONString()!.data(using: .utf8)!)default://不传递任何参数return .requestPlain}}/// 请求头var headers: [String : String]? {var headers:Dictionary<String,String> = [:]//内容的类型headers["Content-Type"]="application/json"//判断是否登录了if PreferenceUtil.isLogin() {//获取tokenlet session = PreferenceUtil.getSession()print("user session \(session)")//将token设置到请求头headers["Authorization"]=session}return headers}}

2、定义网络请求方法

import Foundationimport Moyaimport RxSwiftimport HandyJSONclass DoRequest{static let shared = DoRequest()private var provider:MoyaProvider<RequestApi>!// MARK: 获取列表数据func getAdsList() ->Observable<BaseListResponse<Ads>> {return provider.rx.request(.ads(position: 0)).asObservable().mapString().mapObject(BaseListResponse<Ads>.self)}// MARK: 获取详情func getSongDetail(_ data: String) -> Observable<BaseDetailResponse<Song>> {return provider.rx.request(.songDetail(data: data)).asObservable().mapString().mapObject(BaseDetailResponse<Song>.self)}// MARK: 登录func doLogin(_ data: User) -> Observable<BaseDetailResponse<BaseModel>> {return provider.rx.request(.login(data: data))// 过滤出'statusCode'在200 - 299范围内的响应 成功响应的.filterSuccessfulStatusCodes().asObservable().mapString().mapObject(BaseDetailResponse<BaseModel>.self)}/// 私有构造方法 主要为了打印后台请求到的数据 便于查看private init() {//插件列表var plugins:[PluginType] = []if Config.DEBUG {//表示当前是调试模式//添加网络请求日志插件plugins.append(NetworkLoggerPlugin(configuration: NetworkLoggerPlugin.Configuration(logOptions: .verbose)))}//网络请求加载对话框let networkActivityPlugin = NetworkActivityPlugin {change, target in//changeType类型是NetworkActivityChangeType//通过它能监听到开始请求和结束请求 //targetType类型是TargetType//就是我们这里的service //通过它能判断是那个请求if change == .began {//开始请求let targetType = target as! RequestApiswitch targetType {case .sheetDetail:DispatchQueue.main.async {//切换到主线程 才可以操作viewToastPopup.showLoading()}default:break}} else {//结束请求DispatchQueue.main.async {ToastPopup.hideLoading()}}}plugins.append(networkActivityPlugin)provider = MoyaProvider<RequestApi>(plugins: plugins)}}

3、使用

DoRequest.shared.getAdsList().subscribeSuccess {[weak self] data in// [weak self]弱引用 防止内存泄露print(data)}.disposed(by: rx.disposeBag)

相关联方法及扩展

网络请求响应通用模型

import Foundationimport HandyJSON// 通用模型class BaseModel: HandyJSON {required init() {}func mapping(mapper: HelpingMapper) {}}// 通用网络请求模型class BaseResponse: BaseModel{// 状态码var status:Int = 0// 发生网络请求错误的时候var message:String?}

通用响应数据接收

import UIKitimport HandyJSONclass BaseCommon: BaseModel {/// Idvar id:String!/// 创建时间var createdAt:String!/// 更新时间var updatedAt:String!override func mapping(mapper: HelpingMapper) {super.mapping(mapper: mapper)mapper <<< self.createdAt <-- "created_at"mapper <<< self.updatedAt <-- "updated_at"}}

列表类请求解析类,带有分页

import Foundationimport HandyJSONclass Meta<T:HandyJSON>:BaseModel{// 真实数据var data:[T]?// 条数var total:Int!// 页数var pages:Int!// 当前每页显示多少var size:Int!// 当前页var page: Int!// 下一页var next:Int?}class BaseListResponse<T:HandyJSON>:BaseResponse{// 分页元数据var data:Meta<T>!}

详情类请求解析类

import Foundationimport HandyJSONclass BaseDetailResponse<T:HandyJSON>:BaseResponse{/// 真实数据/// 它的类型定义为范型var data: T?init(_ data: T) {self.data = data}required init() {super.init()}}

mapObject()封装,字符串转对象

import Foundationimport HandyJSONimport Moyaimport RxSwift/// 自定义错误////// - objectMapping: 表示JSON解析为对象失败public enum JSONError: Swift.Error {case objectMapping}extension Observable{/// 将字符串解析为对象////// - Parameter type: 要转为的类/// - Returns: 转换后的观察者对象public func mapObject<T:HandyJSON>(_ type: T.Type) -> Observable<T>{map {data in//将参数尝试转为字符串guard let dataString = data as? String else{//data不能转为字符串throw JSONError.objectMapping}guard let result = type.deserialize(from: dataString) else{throw JSONError.objectMapping}//解析成功//返回解析后的对象return result}}}

subscribeSuccess() 成功转换

// MARK: - 扩展ObservableType// 目的是添加两个自定义监听方法// 一个是只观察请求成功的方法// 一个既可以观察请求成功也可以观察请求失败extension ObservableType{/// 观察成功和失败事件////// - Parameter onSuccess: <#onSuccess description#>/// - Returns: <#return value description#>func subscribe(_ success:@escaping ((Element)-> Void),_ error: @escaping ((BaseResponse?,Error?)-> Bool)) -> Disposable {//创建一个Disposablelet disposable = Disposables.create()//创建一个HttpObserverlet observer = HttpObserver<Element>(success,error)//创建并返回一个Disposablesreturn Disposables.create(self.asObservable().subscribe(observer),disposable)}/// 观察成功的事件////// - Parameter onSuccess: <#onSuccess description#>/// - Returns: <#return value description#>func subscribeSuccess( _ success:@escaping ((Element)-> Void) ) -> Disposable {let disposable = Disposables.create()let observer = HttpObserver<Element>(success,nil)return Disposables.create(self.asObservable().subscribe(observer),disposable)}}

HttpObserver() 网络请求观察者

//http网络请求观察者public class HttpObserver<Element>:ObserverType{/// ObserverType协议中用到了泛型E/// 所以说子类中就要指定E这个泛型/// 不然就会报错public typealias E = Elementpublic typealias successCallback = ((E)-> Void)/// 请求成功回调var success:successCallback/// 请求失败回调var error:((BaseResponse?,Error?)-> Bool)?var controller:BaseCommonController?/// 构造方法////// - Parameters:/// - onSuccess: 请求成功的回调/// - onError: 请求失败的回调init(_ success:@escaping successCallback,_ error: ((BaseResponse?,Error?)-> Bool)?) {self.success = successself.error = error}/// 当RxSwift框架里面发送了事件回调////// - Parameter event: <#event description#>public func on(_ event: Event<Element>) {switch event {case .next(let data):// print("HttpObserver next \(data)")//将值尝试转为BaseResponselet baseResponse = data as? BaseResponseif baseResponse?.status != 0 {//状态码不等于0//表示请求出错了handlerResponse(baseResponse:baseResponse)} else {//请求正常success(data)}case .error(let error)://请求失败// print("HttpObserver error:\(error)")handlerResponse(error:error)case .completed://请求完成// print("HttpObserver completed")break}}/// 尝试处理错误网络请求////// - Parameters:/// - baseResponse: 请求返回的对象/// - error: 错误信息func handlerResponse(baseResponse:BaseResponse?=nil,error:Error?=nil) {if self.error != nil && self.error!(baseResponse,error) {//回调失败block//返回true,父类不自动处理错误//子类需要关闭loading,当前也可以父类关闭//暴露给子类的原因是,有些场景会用到//例如:请求失败后,在调用一个接口,如果中途关闭了//用户能看到多次显示loading,体验没那么好} else {//自动处理错误ExceptionHandleUtil.handlerResponse(baseResponse,error)}}}

错误处理工具类

import Foundationimport Moyaimport Alamofireclass ExceptionHandleUtil{/// 处理网络响应/// - Parameters:/// - data: <#data description#>/// - error: <#error description#>static func handlerResponse(_ data:BaseResponse?=nil,_ error:Error?=nil) {if error != nil {//先处理有异常的请求handlerError(error!)} else {if let r = data?.message{//有错误提示ToastPopup.shortShow(r)} else {ToastPopup.shortShow(R.string.localizable.errorUnknown())}}}/// 处理错误/// - Parameter error: <#error description#>static func handlerError(_ error:Error) {if let error = error as? MoyaError {switch error {case .stringMapping(_):ToastPopup.shortShow("响应转为字符串错误")case .statusCode(let response)://响应码let code=response.statusCodehandleHttpError(code)case .underlying(let _ as NSError, _)://这里直接判断nsError.code有问题//目前也没找到好的解决方法,暂时用这个方法解决:/Moya/Moya/issues/2059//NSError错误code对照表:/p/9c9f14d25572if let alamofireError = error.errorUserInfo["NSUnderlyingError"] as? Alamofire.AFError,let underlyingError = alamofireError.underlyingError as? NSError{switch underlyingError.code {case NSURLErrorNotConnectedToInternet://没有网络连接,例如:关闭了网络ToastPopup.shortShow(R.workError())case NSURLErrorTimedOut://连接超时,例如:网络特别慢ToastPopup.shortShow(R.string.localizable.errorNetworkTimeout())case NSURLErrorCannotFindHost://域名无法解析,例如:域名写错了ToastPopup.shortShow(R.string.localizable.errorNetworkUnknownHost())case NSURLErrorCannotConnectToHost://无法连接到主机,例如:解析的ip地址,或者直接写的ip地址无法连接ToastPopup.shortShow(R.string.localizable.errorNetworkUnknownHost())default:ToastPopup.shortShow(R.string.localizable.errorUnknown())}}else{ToastPopup.shortShow(R.string.localizable.errorUnknown())}default:ToastPopup.shortShow(R.string.localizable.errorUnknown())}}}static func handleHttpError(_ data:Int) {switch data {case 401:ToastPopup.shortShow(R.string.localizable.errorNetworkNotAuth())// AppDelegate.shared.logout()case 403:ToastPopup.shortShow(R.string.localizable.errorNetworkNotPermission())case 404:ToastPopup.shortShow(R.string.localizable.errorNetworkNotFound())case 500..<599:ToastPopup.shortShow(R.string.localizable.errorNetworkServer())default:ToastPopup.shortShow(R.string.localizable.errorUnknown())}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。