这是我参与更文挑战的第17天,活动详情查看: 更文挑战
前面一些简单总结:
前面花了些篇幅介绍以下几点:
-
项目需求开始了,iOS开发应该提前做好的准备知识。
-
介绍了一些基础库的用法:R.Swift、SnapKit、Alamofire、Moya。
-
分享了我自己的JSON解析经历:直接从OC搬过来的生搬硬套,到使用ObjectMapper,最后使用原生的Codable协议。
-
插播了Swift与JS交互、Flutter与JS交互。
如果不算插播的与JS的交互,我已经对App的资源管理、UI布局、网络请求做了一定的铺垫与讲解,虽然与JS交互在这次App编写中没有用,不过总有会用上的地方。
其实这个项目,我是使用RxSwift编写的,对于RxSwift也算是新手入门,边写边学习,所以这一块会在写App的时候,边写边探索,所以如果有什么不对和不好的地方,还请各位大佬指点。
好了,既然前面已经花费了这么多笔墨,那么就开始今天的主题吧——网络请求模块的编写。
网络请求模块编写前的注意事项
由于本次我们编写的App是其他网站的开发API,所以在网站上已经有非常不错的接口文档了:
作为App开发人员,在开始编写网络请求模块的注意事项
-
通读后端给你的接口文档。
-
了解每个接口的请求方式,get还是post还是其他。
-
JSON的格式观察,通过观察,可以总结发现规律,编写带有泛型的BaseModel,同时灵活进行复用。
-
如果可以,可以通过Postman这样的工具进行简单的调试工作。
玩安卓中项目相关接口例子:
玩安卓的接口还是不少的,受限于篇幅与接口的相似性,我这里仅将项目的两个接口进行分析与编码。
项目
- 项目分类
https://www.wanandroid.com/project/tree/json
方法: GET
参数: 无
JSON样例:
{
"data":[
{
"children":[
],
"courseId":13,
"id":294,
"name":"完整项目",
"order":145000,
"parentChapterId":293,
"userControlSetTop":false,
"visible":0
},
],
"errorCode":0,
"errorMsg":""
}
- 项目列表数据
https://www.wanandroid.com/project/list/1/json?cid=294
方法:GET
参数:
cid 分类的id,项目分类接口中获取的data数组中单个元素的id
页码:拼接在链接中,从1开始。
JSON样例:
{
"data":{
"curPage":1,
"datas":[
{
"apkLink":"",
"audit":1,
"author":"wo5813288",
"canEdit":false,
"chapterId":294,
"chapterName":"完整项目",
"collect":false,
"courseId":13,
"desc":"更新学习flutter,所以系统的做一款应用来实践一下。这款应用也开发了很多内容了,后续还要继续更新功能。开发这个项目主要也是熟悉flutter的树形结构的写法和UI组件,项目中也用到了flutter比较流行的第三方框架。",
"descMd":"",
"envelopePic":"https://www.wanandroid.com/blogimgs/e092cd25-3e43-42c4-a7eb-b1ebc60ce02a.png",
"fresh":true,
"host":"",
"id":18624,
"link":"https://www.wanandroid.com/blog/show/3020",
"niceDate":"10小时前",
"niceShareDate":"10小时前",
"origin":"",
"prefix":"",
"projectLink":"https://github.com/wo5813288/wan_giao",
"publishTime":1623768707000,
"realSuperChapterId":293,
"selfVisible":0,
"shareDate":1623768707000,
"shareUser":"",
"superChapterId":294,
"superChapterName":"开源项目主Tab",
"tags":[
{
"name":"项目",
"url":"/project/list/1?cid=294"
}
],
"title":"Flutter开发的WanAndroid",
"type":0,
"userId":-1,
"visible":1,
"zan":0
}
],
"offset":0,
"over":false,
"pageCount":18,
"size":15,
"total":261
},
"errorCode":0,
"errorMsg":""
}
通过上面的两个接口与JSON的样例我们可以得出以下的结论:
-
baseUrl
确定为https://www.wanandroid.com/
-
- 两个接口:
project/tree/json
与project/list/页面数/json?cid=传入的id
- 两个接口:
-
- JSON的基本通用范本:
{
"data":业务具体数据
"errorCode":0,
"errorMsg":""
}
于是我们可以在代码里面新建一个Api.swift文件,整理好相关Api:
struct Api {
/// baseUrl
static let baseUrl = "https://www.wanandroid.com/"
/// 私有初始化方法,避免被实例化
private init() {}
/// 项目 均是get请求
enum Project {
static let tags = "project/tree/json"
static let tagList = "project/list/"
}
}
通过Moya来定义Project服务
import Foundation
import Moya
enum ProjectService {
case tags
/// 枚举带参,传递id与page页码
case tagList(_ id: Int, _ page: Int)
}
extension ProjectService: TargetType {
var baseURL: URL {
return URL(string: Api.baseUrl)!
}
var path: String {
switch self {
case .tags:
return Api.Project.tags
case .tagList(_, let page):
return Api.Project.tagList + page.toString + "/json"
}
}
var method: Moya.Method {
return .get
}
var sampleData: Data {
return Data()
}
var task: Task {
switch self {
case .tags:
return .requestParameters(parameters: Dictionary.empty, encoding: URLEncoding.default)
case .tagList(let id, _):
return .requestParameters(parameters: ["cid": id.toString], encoding: URLEncoding.default)
}
}
var headers: [String : String]? {
return nil
}
}
在上面的代码中,我简单的使用了Int与String的两个分类,也附上:
extension Int {
var toString: String { "\(self)" }
}
extension Dictionary {
static var empty: Dictionary { [:] }
}
最后编写一个独立的Provider:
let projectProvider: MoyaProvider<ProjectService> = {
let stubClosure = { (target: ProjectService) -> StubBehavior in
return .never
}
return MoyaProvider<ProjectService>(stubClosure: stubClosure, plugins: [RequestLoadingPlugin()])
}()
RequestLoadingPlugin
我在介绍Moya时已经提到过,是一个插件,这里就不多说了。
根据JSON的格式,写出一个基础的BaseModel:
玩安卓后台返回JSON的基础格式,后面不同的业务只是data中的值不同罢了:
import Foundation
struct BaseModel<T: Codable>: Codable {
/// T为具体业务数据,遵守Codable协议
let data : T?
let errorCode : Int?
let errorMsg : String?
}
Moya的基本调用:
这里编写Moya的原生调用,并没有使用RxMoya,请求返回的是Result<Response, MoyaError>
类型,在成功的结果中我们可以拿到response,通过response.data获取数据的Swift.Data
类型,再通过Codable协议转为对应的模型。
projectProvider.request(ProjectService.tags) { (result: Result<Response, MoyaError>) in
switch result {
case .success(let response):
let data = response.data
case .failure(let error):
break
}
}
总结
本篇说明了写网络请求模块的注意事项。
通过举例玩安卓项目的两个接口,编写了Api、Moya服务和BaseModel,而后续的其他的接口都可以依葫芦画瓢进行编写。
原生Moya调用并不能直接转基于Codable的Model,但是其Moya.Response中有扩展方法可以做转换。后续会通过RxMoya进行优化。
明日继续
下一篇,我会通过玩安卓中的一些模型,抽象一些基本的Model。基本上到了边写代码边总结文章的时候了。
大家加油!