我正在 Kiri 这个库里尝试一种新的网络请求方式。
Kiri 是一个受 Moya 启发的网络抽象层。Moya 的架构给我留下了非常深的印象,为了从中学到最多的东西,我决定不复制粘贴,而是亲手把同样的架构写一遍。:D
我们需要把客户端库拆成三个部分。
Request
通常情况下,你每次都得自己构造网络请求。
而在 Kiri 中,网络配置集中写在一个地方,比如 class、enum。
并且这个 class 或 enum 必须遵循 RequestContainer 协议。
示例代码如下。
enum TestAPIRequest {
case getitems
case postitem
case updateitem
case deleteitem
}
enum TestAPIRequest: RequestContainer {
var baseURL: URL {
return URL(string: "https://www.example.com/api/v1")!
}
var path: String {
switch self {
case .getitems:
return "/getitems"
.....
}
}
....
}
从设计 API Client 的角度来看,这样做更好。
在开始编写客户端模块之前,你就能一眼看清整体结构。
你也不需要把常量字符串传给每一个类——因为 Kiri 可以持有像 baseURL 这样的公共部分,在你通过提取方法来封装这些东西时,也就避免了应用里的重复代码。
不过,如果你的 API 每个接口的 endpoint 都不一样,或者情况类似,事情就没这么简单了。这种情况下你可能会想为每个 API 单独建一个类,这也说得通。如果你(不幸)遇到了这样的 API,最好还是去看看 APIKit。它本来就是按每个 API 拆分类的设计,而在响应处理上,它也比 Kiri 或 Moya 更加泛型化。
Networking
iOS 有自己的网络类 URLSession。但现在再去封装 URLSession 已经没有意义了。我们有 Alamofire。
Alamofire 会自动校验请求和响应,还能直接返回 JSON。不过我更倾向于让响应保持为 Data,因为网络库应该支持多种响应格式。
Kiri 使用 Alamofire 发起网络请求,并把构建请求的工作委托给它。
在大多数情况下 Alamofire 都表现得很好,但如果你需要更底层的网络能力,比如自己拼装参数之类,它可能就不太适合你了。
Response
我发现这里存在一个权衡:如果把响应做成泛型,请求就会被拆散到不同的类里;反过来,如果把请求做成泛型,响应就会被拆散。
我读了 Moya 和 APIKit 两者的代码。在我看来,它们是 Swift 网络编程中最精巧的两种架构,当然也各有利弊。
我还没有找到网络请求的最佳方案,但就目前而言,根据你的应用需求做出明智的选择,也许就是最好的答案。
参考链接
Kiri 比 Moya 小得多,但在我的个人项目里运行良好。不妨试试看!