首页 存档 技术 查看内容

iOS 开发实践:如何构建一个网络层?

2018-3-30 13:00 |来自: 互联网 281 0

摘要: 原文:How do I build a Network Layer作者:Tomasz Szulc(@tomkowz),资深 iOS 软件工程师译者:孙薇,欢迎技术投稿、约稿、给文章纠错,请发送邮件至 [email protected]。版权声明:本文为作者授权 CSDN 翻译, ...

原文:How do I build a Network Layer
作者:Tomasz Szulc(@tomkowz),资深 iOS 软件工程师
译者:孙薇,欢迎技术投稿、约稿、给文章纠错,请发送邮件至 [email protected]
版权声明:本文为作者授权 CSDN 翻译,未经允许,请勿转载。

【导语】本文作者 Tomasz Szulc 曾同时带领着两个项目的研发工作,由此为他提供了一次很好的对于应用架构进行深度尝试的机会,本文即是他根据实践经验所总结的网络层构建方法,大家或许有兴趣一读。


如今的移动应用大多是“客户端-服务器”模式,某个应用中很可能就包含或大或小的网络层结构。迄今为止笔者见过的许多实现均有一些缺陷,最新构建的这个或许仍有缺陷,但在手边的这两个项目中效果都很不错,而且测试覆盖率几乎达到100%。本文只讨论与单个后台通讯、发送 JSON 编码请求的网络层,这个网络层会与 AWS 通讯,发送一些文件,整体结构并不复杂,不过相应功能的扩展也应当十分简单。

思维流程

在构建相应网络层之前,我先提出一些问题:

  • 将包含有后台 URL 相关内容的代码放在哪里?

  • 将包含端点相关的代码放在哪里?

  • 将包含如何构建请求信息的代码放在哪里?

  • 将与为请求准备参数的相关代码放在哪里?

  • 应当将身份验证 token 存在哪里?

  • 如何执行请求?

  • 何时、在何处执行请求?

  • 是否关注取消请求的问题?

  • 是否需要关注错误的后台响应或者一些后台 Bug?

  • 是否需要使用第三方的框架?应当使用什么框架?

  • 是否存在相关的Core Data?

  • 如何测试解决方案?

存储后端 URL

首先,我们要了解后端 URL 应当放在哪里?系统的其它部分怎么知道向哪里发送请求?这里我们更偏好创建存储这类信息的 BackendConfiguration 类。

import Foundation
public final class BackendConfiguration {
  let baseURL: NSURL
  public init(baseURL: NSURL) {
    self.baseURL = baseURL
  }
  public static var shared: BackendConfiguration!
}

这种类易于测试,也易于配置,设定共享静态变量之后,我们就能从网络层的任意位置对其进行访问,不需将这个变量发送到其它位置。

let backendURL = NSURL(string: "https://szulctomasz.com")!
BackendConfiguration.shared = BackendConfiguration(baseURL: backendURL)

端点

在找到解决方案前,笔者在这个问题上做了颇有一阵子的实验,在配置 NSURLSession 时曾尝试对端点执行硬编码的方式,并尝试了一些了解端点、便于实例化与注入的虚拟资源类对象,但并未找到需要的方案。然后得出了设想:创建知道要接入哪个端点,使用什么方法,该是 GETPOSTPUT 还是其它什么的 *Request 对象,它要了解如何配置请求主体,以及要 pass 什么头文件。

于是我得出了这样的代码:

protocol BackendAPIRequest {
  var endpoint: String { get }
  var method: NetworkService.Method { get }
  var parameters: [String: AnyObject]? { get }
  var headers: [String: String]? { get }
}

实现这个协议的类能够提供构建请求所需的基本信息,NetworkService.Method 只是个带有 GETPOSTPUTDELETE案例的enum函数。

映射一个端点的请求示例如下:

final class SignUpRequest: BackendAPIRequest {

  private let firstName: String
  private let lastName: String
  private let email: String
  private let password: String

  init(firstName: String, lastName: String, email: String, password: String) {
    self.firstName = firstName
    self.lastName = lastName
    self.email = email
    self.password = password
  }

  var endpoint: String {
    return "/users"
  }

  var method: NetworkService.Method {
    return .POST
  }

  var parameters: [String: AnyObject]? {
    return [
      "first_name": firstName,
      "last_name": lastName,
      "email": email,
      "password": password
    ]
  }

  var headers: [String: String]? {
    return ["Content-Type": "application/json"]
  }
}

为了避免给每个 header 创建 dictionary,我们可以为 BackendAPIRequest 定义扩展。

extension BackendAPIRequest {

  func defaultJSONHeaders() -
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部