Swifty 1.3.0

Swifty 1.3.0

测试已测试
语言语言 SwiftSwift
许可证 Apache-2.0
发布最后发布2019年5月
SPM支持 SPM

Siddharth GuptaSiddharth 维护。



Swifty 1.3.0

  • Siddharth Gupta

Swifty Logo

Build Status Version Platform Swift Docs License

Swifty 是对 iOS 应用网络方式的一种现代思考。它使用 Swift 编写,提供一种 声明式 的方式来编写网络请求和组织它们,将网络抽象化,从而从调用位置中抽出网络通信,同时让您能够完全控制每一个网络通信的方面。

Swifty 主要是为了回答开发者在构建现代应用时提出的三个常见问题而设计的

  1. 我的网络请求应该放在哪里?
  2. 我在哪里编写自定义的 OAuth/身份验证/会话逻辑?或者我怎么管理请求之间的会话?
  3. 我应该怎么做实际的网络请求?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 方法。该方法是同步的,且线程安全的,需要返回 truefalse
    • 如果你的返回值是 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
	}

}

关于拦截器的注意事项

  • 你的每个网络请求都会通过所有拦截器,包括RequestInterceptorsResponseInterceptors
  • 请求通过拦截器的顺序是您提供给Swifty的顺序。

我应该怎样进行实际的网络操作?使用URLSession吗?

Swifty建立在URLSession之上,并且为所有上述构造提供了实际的网络通信。

Swifty封装了URLSession的细节,同时仍然在关键之处给予你细致的控制。

记得您的WebService中的networkInterface属性吗?当你在WebService中编写请求,并在Constraints & Interceptors中放入业务逻辑后,你通过在WebServiceSwifty的初始化器中添加自定义来统一所有这些。

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的定制管道(ConstraintsInterceptors)执行。

API 文档

Swifty 的完整文档可在此获取。[点击访问]

安装

CocoaPods

要使用 CocoaPods 将 Swifty 集成到您的 Xcode 项目中,请在您的 Podfile 中指定它

pod 'Swifty'

然后,运行以下命令

$ pod install

需求

  • iOS 8.0+
  • Swift 4.2

Readme Score