1.关于moya
如在OC中使用AFNetworking一般,Swift我们用Alamofire来做网络库.而Moya在Alamofire的基础上又封装了一层:
官方说moya
有以下特性(我也就信了):
编译时检查正确的API端点访问.使你定义不同端点枚举值对应相应的用途更加明晰.提高测试地位从而使单元测试更加容易.
2.开始
1.创建枚举API
就像这样:
enum APIManager {case getNewsLatest//获取最新消息case getStartImage// 启动界面图像获取case getVersion(String)//软件版本查询case getThemes//主题日报列表查看case getNewsDetail(Int)//获取新闻详情}
2.实现TargetType
协议
就像这样:
extension APIManager: TargetType {/// The target's base `URL`.var baseURL: URL {return URL.init(string: "http://news-/api/")!}/// The path to be appended to `baseURL` to form the full `URL`.var path: String {switch self {case .getNewsLatest:return "4/news/latest"case .getStartImage://start-image 后为图像分辨率,接受任意的 number*number 格式, number 为任意非负整数,返回值均相同。return "4/start-image/1080*1776"case .getVersion(let version)://URL 最后部分的数字代表所安装『知乎日报』的版本return "4/version/ios/" + versioncase .getThemes:return "4/themes"case .getNewsDetail(let id):return "4/news/\(id)"}}/// The HTTP method used in the request.var method: Moya.Method {return .get}/// The parameters to be incoded in the request.var parameters: [String: Any]? {return nil}/// The method used for parameter encoding.var parameterEncoding: ParameterEncoding {return URLEncoding.default}/// Provides stub data for use in testing.var sampleData: Data {return "".data(using: String.Encoding.utf8)!}/// The type of HTTP task to be performed.var task: Task {return .request}/// Whether or not to perform Alamofire validation. Defaults to `false`.var validate: Bool {return false}}
在这里,可以设置请求的参数,例如url……method……para等.
3.使用
Moya
的使用非常简单,通过TargetType
协议定义好每个target
之后,就可以直接使用Moya
开始发送网络请求了。就像这样:
let provider = MoyaProvider<APIManager>()provider.request(.getNewsLatest) { result in// do something with result}
3.配合RxSwift
Moya
本身已经是一个使用起来非常方便,能够写出非常简洁优雅的代码的网络封装库,但是让Moya
变得更加强大的原因之一还因为它对于Functional Reactive Programming
的扩展,具体说就是对于RxSwift
和ReactiveCocoa
的扩展,通过与这两个库的结合,能让Moya
变得更加强大。我选择RxSwift
的原因有两个,一个是RxSwift
的库相对来说比较轻量级,语法更新相对来说比较少,我之前用过ReactiveCocoa
,一些大版本的更新需求重写很多代码,第二个更重要的原因是因为RxSwift
背后有整个ReactiveX
的支持,里面包括Java
,JS
,.Net
,Swift
,Scala
,它们内部都用了ReactiveX
的逻辑思想,这意味着你一旦学会了其中的一个,以后可以很快的上手ReactiveX
中的其他语言。
Moya
提供了非常方面的RxSwift
扩展:
let provider = RxMoyaProvider<APIManager>()provider.request(.getNewsLatest).filterSuccessfulStatusCodes().mapJSON().subscribe(onNext: { (json) in//do something with postsprint(json)}).addDisposableTo(disposeBag)
解释一下:
RxMoyaProvider
是MoyaProvider
的子类,是对RxSwift
的扩展
filterSuccessfulStatusCodes()
是Moya
为RxSwift
提供的扩展方法,顾名思义,可以得到成功地网络请求,忽略其他的
mapJSON()
也是Moya RxSwift
的扩展方法,可以把返回的数据解析成JSON
格式
subscribe
是一个RxSwift
的方法,对经过一层一层处理的Observable
订阅一个onNext
的observer
,一旦得到JSON
格式的数据,就会经行相应的处理
addDisposableTo(disposeBag)
是RxSwift
的一个自动内存处理机制,跟ARC
有点类似,会自动清理不需要的对象。
4.配合HandyJSON
在实际应用过程中网络请求往往紧密连接着数据层(Model
),具体地说,在我们的这个例子中,一般我们需要建立一个类用来统一管理数据,然后把得到的JSON
数据映射到数据层(Model
)。
struct MenuModel: HandyJSON {var others: [ThemeModel]?}struct ThemeModel: HandyJSON {var color: String?var thumbnail: String?var id: Int?var description: String?var name: String?}
然后创建ViewModel类,创建具体请求方法:
class MenuViewModel {private let provider = RxMoyaProvider<APIManager>()var dispose = DisposeBag()func getThemes(completed: @escaping (_ menuModel: MenuModel) -> ()){provider.request(.getThemes).mapModel(MenuModel.self).subscribe(onNext: { (model) incompleted(model)}, onError: { (error) in}, onCompleted: nil, onDisposed: nil).addDisposableTo(dispose)}}
这里解释一下:
我这里是将请求的数据通过闭包传了出去,当然也可以不那么做.个人喜好问题..
这里是为RxSwift
中的ObservableType
和Response
写一个简单的扩展方法mapModel
,利用我们写好的Model
类,一步就把JSON
数据映射成model
。
extension ObservableType where E == Response {public func mapModel<T: HandyJSON>(_ type: T.Type) -> Observable<T> {return flatMap { response -> Observable<T> inreturn Observable.just(response.mapModel(T.self))}}}extension Response {func mapModel<T: HandyJSON>(_ type: T.Type) -> T {let jsonString = String.init(data: data, encoding: .utf8)return JSONDeserializer<T>.deserializeFrom(json: jsonString)!}}
5.配合ObjectMapper
毕竟将json数据转换成model的库那么多 ….,所以……,用哪个很随意…..这里再介绍一下ObjectMapper
1.创建model类
class DetailModel: Mappable {var body = String()var image_source: String?var title = String()var image: String?var share_url = String()var js = String()var recommenders = [[String: String]]()var ga_prefix = String()var section: DetailSectionModel?var type = Int()var id = Int()var css = [String]()func mapping(map: Map) {body <- map["body"]image_source <- map["image_source"]title <- map["title"]image <- map["image"]share_url <- map["share_url"]js <- map["js"]recommenders <- map["recommenders"]ga_prefix <- map["ga_prefix"]section <- map["section"]type <- map["type"]id <- map["id"]css <- map["css"]}required init?(map: Map) {}}
使用ObjectMapper
,需要让自己的Model
类使用Mappable
协议,这个协议包括两个方法:
required init?(map: Map) {}func mapping(map: Map) {}
在mapping
方法中,用<-
操作符来处理和映射你的JSON
数据。
数据类建立好之后,我们还需要为RxSwift
中的Observable
写一个简单的扩展方法mapObject
,利用我们写好的model
类,一步就把JSON
数据映射成一个个model
。
extension Observable {func mapObject<T: Mappable>(type: T.Type) -> Observable<T> {return self.map { response in//if response is a dictionary, then use ObjectMapper to map the dictionary//if not throw an errorguard let dict = response as? [String: Any] else {throw RxSwiftMoyaError.ParseJSONError}return Mapper<T>().map(JSON: dict)!}}func mapArray<T: Mappable>(type: T.Type) -> Observable<[T]> {return self.map { response in//if response is an array of dictionaries, then use ObjectMapper to map the dictionary//if not, throw an errorguard let array = response as? [Any] else {throw RxSwiftMoyaError.ParseJSONError}guard let dicts = array as? [[String: Any]] else {throw RxSwiftMoyaError.ParseJSONError}return Mapper<T>().mapArray(JSONArray: dicts)!}}}enum RxSwiftMoyaError: String {case ParseJSONErrorcase OtherError}extension RxSwiftMoyaError: Swift.Error { }
mapObject
方法处理单个对象,mapArray
方法处理对象数组。
如果传进来的数据response
是一个dictionary
,那么就利用ObjectMapper
的map
方法映射这些数据,这个方法会调用你之前在mapping
方法里面定义的逻辑。
如果response
不是一个dictionary
, 那么就抛出一个错误。
在底部自定义了简单的Error
,继承了Swift
的Error
类,在实际应用过程中可以根据需要提供自己想要的Error
。
然后运行请求方法:
class DetailViewModel {private let provider = RxMoyaProvider<APIManager>()func getNewsDetail(id: Int) -> Observable<DetailModel> {return provider.request(.getNewsDetail(id)).filterSuccessfulStatusCodes().mapJSON().mapObject(type: DetailModel.self)}}
有没有感觉很爽呢!————源码地址,共同学习!
原文地址
有不对之处,,,,还望各路大神不吝指正!