Swifty 是对 iOS 应用网络方式的一种现代思考。它使用 Swift 编写,提供一种 声明式 的方式来编写网络请求和组织它们,将网络抽象化,从而从调用位置中抽出网络通信,同时让您能够完全控制每一个网络通信的方面。
Swifty 主要是为了回答开发者在构建现代应用时提出的三个常见问题而设计的
- 我的网络请求应该放在哪里?
- 我在哪里编写自定义的 OAuth/身份验证/会话逻辑?或者我怎么管理请求之间的会话?
- 我应该怎么做实际的网络请求?URLSession?
我的网络请求应该放在哪里?
Swifty 提供了一个名为 WebService
的协议,帮助您以类型安全且富有表达性的方式编写网络请求。
您首先通过 创建一个类、放入您的 服务器的基本 URL 和网络接口,开始将 网络请求作为函数编写
class GithubAPI: WebService {
/* Your Server's Base URL */
static var serverURL = "https://api.github.com"
/* What this WebService will use to actually make the network calls */
static var networkInterface: WebServiceNetworkInterface = Swifty.shared
/* Your network requests, as type-safe functions: */
static func getAPIStatus() -> NetworkResource {
return server.get("status")
}
static func getRespositories(for user: String) -> NetworkResource {
return server.get("repositories")
.query("user": user)
}
static func createGist(with body: String) -> NetworkResource {
return server.post("gists")
.json("body": body)
}
}
上述内容需要注意的几点
- 您的每一个网络请求函数都会返回一个
NetworkResource
。这基本上是URLRequest
的一个包装,增加了支持这种酷炫语法和其他特性的额外功能。 - 您需要从
server
变量开始编写每个请求(它是您上面定义的服务器 URL 转换为NetworkResource
),然后通过链式调用.get()
、.post
和.query()
等方法来创建实际的请求。这些修饰符的完整列表可以在下面找到:这里。 - 变量
networkInterface
是告诉这个WebService
使用哪个库进行 实际的网络请求 的方式。在本例中,我们直接使用 Swifty。
超级酷炫的功能:这些链式方法是编译时检查,例如,您不能将
.json()
链接到 GET 请求,因为它不支持正文有效负载。😎
用法
在 WebService 中编写的请求既可以从 Swift 调用,也可以从 Objective-C 调用!
Swift
class ViewController: UIViewController {
override viewDidLoad(){
GithubAPI.getStatus().load(){ (response, data, error) in
// Do something with the response
}
}
}
Objective-C
@implementation ViewController: UIViewController {
- (void) viewDidLoad {
[GithubAPI getStatus] load:^(NSURLResponse *response, id data, NSError *error){
// Do something with the response
}];
}
@end
这些都是示例,请在您的视图控制器中直接编写网络代码!😂
我在哪里编写我的自定义 OAuth/Authentication/Session 逻辑?
现代应用程序通常访问位于身份验证或速率限制系统之后的 API,它们需要在每个请求中发送这些令牌。
这通常是一个复杂的过程:首先,检查我们是否已经有了有效的令牌。如果没有,我们需要获取一个,然后将其附加到我们发送的每个请求上。当然,还需要处理所有这些情况中的错误条件。如果做得不恰当,这个过程可能会导致代码重复和回调地狱。
Swifty 理解这个需求,并提供构造函数,通过使用 Constraints
& Interceptors
将其封装成线程安全的进程。
约束
约束是确保网络请求在满足条件之前不能开始的任务。
约束可以是任何任务,而不仅仅是网络请求:它们甚至可以是在发送请求之前请求位置访问权限这类简单事项。
使用约束的一个常见例子是 OAuth 约束,它确保在开始请求之前你有 OAuth 令牌。
要创建约束,只需继承 Constraint
并覆盖两个必需的方法。
class OAuthConstraint: Constraint {
override func isConstraintSatisfied(for resource: NetworkResource) -> Bool {
// return false if we don't have the OAuth Token
// return true if we already have the OAuth Token
}
override func satisfyConstraint(for resource: NetworkResource) {
// Get the OAuth token from the server
// Make sure to call finish() when done
finish()
}
}
它如何工作?
- Swifty 会自动调用每个通过它的资源的
isConstraintSatisfied
方法。该方法是同步的,且线程安全的,需要返回true
或false
。- 如果你的返回值是
true
,则请求将恢复,前提是满足其他约束。 - 如果你返回
false
,Swifty 会异步调用你的satisfyConstraint
方法,在这里你可以执行所需的任何操作。只需确保完成时调用finish
,以便 Swifty 可以恢复等待你约束的请求。- 你甚至可以以一个
error
完成。如果你这样做了,等待你约束的请求将自动以相同的错误失败。
- 你甚至可以以一个
- 如果你的返回值是
- 由于在每次调用这些方法时都会传递一个网络资源作为参数,因此你可以根据请求有选择性地决定在这两个方法中执行什么操作。
拦截器
拦截器是在每个请求前后调用的方法。有两种类型:请求拦截器
和 响应拦截器
。
请求拦截器
请求拦截器在请求即将通过网络发送之前被调用。请求拦截器在请求满足所有约束之后、但在即将发送请求之前被调用。这使得它们特别有用,可以将必要的参数添加到请求中。
例如,可以使用拦截器来附加OAuth令牌,该令牌可能是刚刚从一个服务器接收到的。
要创建一个RequestInterceptor
,只需创建一个符合RequestInterceptor
协议的类/结构体,并实现一个要求的方法。
class OAuthTokenAddingInterceptor: RequestInterceptor {
func intercept(resource: NetworkResource) -> NetworkResource {
// Get the token from where your Constraint might have saved it, this is just an example here:
let token = Keychain.string(key: "OAuth")
// Attach it to the resource:
resource.header(key: "Token", value: token)
// Return the modified resource
return resource
}
}
响应拦截器
在响应将被返回给调用者的之前调用响应拦截器。
这里可以做很多事情,例如
- 通过计数错误数来收集/记录响应失败率的统计数据
- 如果你有的话,从每个响应中更新你的会话信息
- 您甚至可以在响应拦截器中强制使响应成功或失败
为了示范,如果你的API认为204响应是一个失败,我们可以创建一个响应拦截器来检查每个响应中的此状态码,并强制失败遇到这种情况的响应。
class ErrorCheckingInterceptor: ResponseInterceptor {
func intercept(response: NetworkResponse) -> NetworkResponse {
// Check for the 204 status code in the response
if let statusCode = response.response?.statusCode, statusCode == 204 {
// Fail the response with a responseValidation error
response.fail(error: SwiftyError.responseValidation()) // Now this response will invoke the failureBlock, instead of the successBlock of the caller.
}
return response
}
}
关于拦截器的注意事项
- 你的每个网络请求都会通过所有拦截器,包括
RequestInterceptors
和ResponseInterceptors
。 - 请求通过拦截器的顺序是您提供给Swifty的顺序。
我应该怎样进行实际的网络操作?使用URLSession吗?
Swifty建立在URLSession
之上,并且为所有上述构造提供了实际的网络通信。
Swifty封装了URLSession的细节,同时仍然在关键之处给予你细致的控制。
记得您的WebService
中的networkInterface
属性吗?当你在WebService
中编写请求,并在Constraints
& Interceptors
中放入业务逻辑后,你通过在WebService
中Swifty
的初始化器中添加自定义来统一所有这些。
class GithubAPI: WebService {
...
/* What this WebService will use to actually make the network calls */
static var networkInterface: WebServiceNetworkInterface = Swifty(constraints: [OAuthConstraint()],
requestInterceptors: [OAuthTokenAddingInterceptor()],
responseInterceptors: [ErrorCheckingInterceptor()])
...
}
到此结束!所有内容组合在一起,当你调用它们的load()
时,所有的WebService
请求都通过Swifty
的定制管道(Constraints
和Interceptors
)执行。
API 文档
Swifty 的完整文档可在此获取。[点击访问]
安装
CocoaPods
要使用 CocoaPods 将 Swifty 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
pod 'Swifty'
然后,运行以下命令
$ pod install
需求
- iOS 8.0+
- Swift 4.2