最近回学校了,终于有些空下来的时间,有机会说说前段时间所做的项目,在项目中遇到的问题和我所能想到的解决方案。
最近,我司开了一个新的项目,由于某些原因这个项目前期是我主导的。简单概括一下项目,项目的只用 MVC 模式开发,语言使用 swift3.0,由于项目时间比较赶,框架是延用了我司现有的框架,由于前期的工作主要是做总体框架的搭建,而在这个框架中主要的是网络模块的设计(在之后可能会需要加入持久化方案等等)。
接下来讲讲网络模块是如何构建的。该网络层主要封装了 Alamofire 的第三方网络库和 AlamofireObjectMapper 数据解析。

1
2
3
4
5
6
7
8
9
10
enum XXXAPI {
case getUsers

func path() -> String {
switch self {
case .getUsers:
return "getUsers"
}
}
}

通过 swift 中 enum 枚举类型来管理网络请求中 endpoint。

1
2
3
4
5
6
struct APIClient {
static let baseUrl = "http://xxx.xxx.xxx/api/"
static var headers: Dictionary<String, String> {
return ["Content-Type":"json/text"]
}
}

该 struct 是封装 Alamofire 的主体,定义了网络请求的 host 和请求头信息。

1
2
3
4
5
6
static func request<T: Mappable>(_ API: XXXAPI, ...) {
let path = API.path()
let url = baseUrl + path
let req = request(...)
handleResponse(...)
}

以上是一个请求的主体,所有的 getpostputdelete 都最终需要调用该方法。在这个 struct 的基础上扩展出我们自己封装的相应的 getput 等方法。
这里使用了泛型 <T: Mappable> 并且要求 T 遵循 Mappable 协议,该协议用来数据解析,与 Alamofire 配套的数据解析。
在 request 的方法中 handleResponse 来处理请求返回的 json,做出相应的成功和错误的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static func handleResponse<T: Mappable>(...
completion: @escaping (T?, String?) -> Void) {
request.responseObject { (response: DataResponse<Result<T>>) in
guard let result = response.result.value else {
completion(nil, nil, "网络错误")
return
}

if let code = result.code {
// TODO
} else {
completion(nil, result.msg ?? "服务器错误")
}
}
}

以上就是项目中使用的网络请求的思路,他的核心 APIClient,在使用的时候,我们需要提供 XXXAPI、params、encoding、closure 这几个变量,调用相应的方法,值得注意的是,解析的数据模型与网络数据 json 要一致。
在使用的过程中有以下的优点:

  1. enum 统一管理请求的 endpoint 使代码的可维护性增强,阅读和查询起来也比较方便。

当然在该过程中提出一点建议:

  1. enum 管理应当扩大,其实 endpoint 所对应的 parameters、请求方法、encoding 都是已经确定的,所以在确定 endpoint 的同时其实我们也已经确定了它的参数、请求方法、编码方式等信息。
  2. 对于在多 host 的请求(也许我们所需要获取的数据在不同服务器上),当前的这个解决方案需要去重新定义一个 APIClient。我认为更好的解决方案是我们定一个这样的协议:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
protocol Requestable {
var baseUrl: String { get }
var endpoint: String { get }
var method: HTTPMethod { get }
var parameters: [String: AnyObject]? { get }
var headers: [String: String] { get }
}

extension API: Requestable {
var endpoint: String {
switch self {
case .once(_):
return "/"
}
}
var method: HTTPMethod {
switch self {
case .once(_):
return .get
}
}
var parameters: [String: AnyObject]? {
switch self {
case .once(let id):
return ["id" : id as AnyObject]
}
}
}

而 enum API 遵循该协议,需要实现其中的 get 方法。这时对外我们只需要一个 API 的 enum 就够了,确定了 enum 的值,相应的 parameters 等等都确定了。

通过这样处理,我们将网络层的 endpoint、parameters、HTTPMethod 等都统一的管理,而对外调用的时候只需要传递一个 enum API 的值和相应的参数即可。保持了比较好的封装性,固定的 parameters 也减少了在填写时的错误发生。

以上属于个人见解,如有错误,欢迎拍砖