QwikHttp
QwikHttp是一个强大、轻量级且易于使用的HTTP网络库。它允许您通过单行代码自定义每一点http请求,使用Builder样式语法来保持代码的极度简洁。
QwikHttp与其他网络库的不同之处在于其以下特点:
- 轻量级实施
- 简单易用的语法
- 使用泛型和QwikJson进行强大序列化和反序列化
- 广泛的多平台支持
- 简单而强大的加载指示器支持
- 响应拦截器,提供处理未授权响应和轻松刷新令牌的方法。
- Swift并发支持
QwikHttp是用Swift 5编写的,并与Swift配合得很好。它即使是与Objective-C(不使用泛型)配合也表现良好。它利用了最新的iOS网络API,NSURLSession。QwikHttp与iOS 9+、tvOS、WatchOS 2和macOS 10.9+兼容。对于与Swift 2和Objective-C兼容的版本,请参阅1.6.11版。
使用方法
以下是一些使用QwikHttp的示例,展示了其易用性。
一个简单请求
QwikHttp("https://api.com", httpMethod: .get).send()
Swift 并发支持
Task {
let request = QwikHttp("https://resttest2016.herokuapp.com/restaurants", httpMethod: .get).addUrlParams(["format" : "json"])
let response = await request.getArrayResponse(Restaurant.self)
switch response {
case .success(let result):
UIAlertController.showAlert(withTitle: "Success", andMessage: String(format: "We Found %li with async, response code: %li",result.count, request.responseStatusCode), from: self)
case .failure(let error):
UIAlertController.showAlert(withTitle: "Failure", andMessage: String(format: "Load error \(error)"), from: self)
}
}
参数和头信息
您可以设置 json, url 或表单编码的参数
let params = ["awesome" : "true"]
//url parameters
QwikHttp("https://api.com", httpMethod: .get)
.addUrlParameters(params)
.send()
//form parameters
QwikHttp("https://api.com", httpMethod: .get)
.addParameters(params)
.setParameterType(.urlEncoded)
.send()
//json parameters
QwikHttp("https://api.com", httpMethod: .get)
.addParameters(params)
.setParameterType(.json)
.send()
您可以直接设置正文并添加自己的头信息
let data = UIImagePNGRepresentation(someImage);
let headers = ["Content-Type": "image/png"]
QwikHttp("https://api.com", httpMethod: .post)
.setBody(data)
.addHeaders(headers)
.send()
泛型
请注意,QwikHttp 使用泛型完成处理器。告诉它你期望在响应中返回的数据类型,它会处理转换。
以下泛型类型默认支持。可以通过实现 QwikDataConversion 协议来添加新类型,该协议将 NSData 转换为其他类型。
- NSDictionary:从 JSON 响应中解析
- NSString
- Bool:如果请求成功,则为真
- NSNumber
- NSData
- 数组:这通过使用数组内容的类型并调用下述描述的数组完成处理程序而支持。默认支持数组的类型有字符串、字典和 QwikJson 对象
- Codable:Swift 的新 JSON 编码协议,Codable 在 QwikHttp 中得到了支持。要使用 Codable 类型,您的对象应实现 QwikHttp.QwikCodable 协议,它是 Codable 的一个扩展。
- 对于复杂类型,可以扩展 QwikJson 以轻松地在字典和复杂对象及数组之间转换(详情请见下文)
类型结果处理器
根据您的需求,您可能希望调用对象处理器如果您期望单个对象,或者数纽处理器。如果您不在乎,甚至可以使用简单的(可选的)布尔类型结果处理器。
获取对象
QwikHttp(url: "https://api.com", httpMethod: .get)
.getResponse(NSDictionary.self, { (result, error, request) -> Void in
if let resultDictionary = result
{
//have fun with your JSON Parsed into a dictionary!
}
else if let resultError = error
{
//handle that error ASAP
}
)}
获取数组
QwikHttp("https://api.com", httpMethod: .get)
.getResponseArray(NSDictionary.self, { (result, error, request) -> Void in
if let resultArray = result
{
//have fun with your JSON Parsed into an array of dictionaries
}
else if let resultError = error
{
//handle that error ASAP
}
)}
通过 / 失败布尔响应处理器
您还可以使用简单的"是/否"成功响应处理器。
QwikHttp("https://api.com", httpMethod: .get)
.send { (success) -> Void in
//if success do x
}
更详细的响应
响应对象保存到请求对象中,并可用于更底层的处理。
QwikHttp("https://api.com", httpMethod: .get)
.getResponse(NSString.self, { (result, error, request) -> Void in
if let responseCode = request.response.responseCode
{
//check for 403 responses or whatever
}
if let responseString = request.responseString
{
//handle the response as a string directly
}
if let responseData = request.responseData
{
//handle the response as nsdata directly
}
)}
线程
您可以告诉QwikHttp您是否希望响应处理器在主线程或后台线程上执行。默认情况下,所有响应处理器都将调用主线程,但是您可以轻松更改此默认值或将其设置为针对单个请求。
QwikHttpConfig.setDefaultResponseThread(.Background)
QwikHttp("https://api.com", httpMethod: .get)
.setResponseThread(.Main)
.send()
QwikJson
我们用于Json序列化的库QwikJson现在已直接集成到QwikHttp中。这意味着对于各种复杂模型对象,有内置支持。
有关QwikJson的完整文档,请参阅我的GitHub仓库:https://github.com/logansease/QwikJson
基本上,只需在复杂的模型对象中继承QwikJson,您就可以使用QwikHttp自动序列化和反序列化这些模型对象。
//declare your complex class with whatever properties
public Class MyModel : QwikJson
{
@objc var myProperty = "sweet"
}
- 注意:当使用Swift 4时,所有属性都必须使用@objc前缀,以便进行序列化和反序列化。
现在您可以轻松地将QwikJson对象传递和返回至您的RESTful API中!
let model = MyModel()
QwikHttp("https://api.com", httpMethod: .post)
.setObject(model)
.getResponse(MyModel.self, { (result, error, request) -> Void in
if let result as? Model
{
//you got a model back, with no parsing code!
}
})
它甚至适用于数组
let model = MyModel()
let models = [model]
QwikHttp("https://api.com", httpMethod: .post)
.setObjects(models)
.getArrayResponse(MyModel.self, { (results, error, request) -> Void in
if let modelArray = results as? [Model]
{
//you got an array of models back, with no parsing code!
}
})
加载指示器
通过使用QwikHttpLoadingIndicatorDelegate协议,您可以为QwikHttp提供在HTTP请求过程中显示和隐藏加载指示器的接口,允许您使用任何指示器,同时由QwikHttp来设置和处理。
一旦将默认指示器代理设置为QwikHttpConfig,只需在您的QwikHttp对象上调用setLoadingTitle方法,请求运行时指示器会自动显示,请求完成后会自动隐藏。
QwikHttp("https://api.com", httpMethod: .get)
.setLoadingTitle("Loading")
.send()
您可以设置加载指示器的默认标题,传递nil标题将使其保持隐藏(这是默认行为),传递字符串(即使是空字符串)将使指示器默认自动显示和隐藏。
//hide the indicator by default
QwikHttpConfig.setDefaultLoadingTitle(nil)
//show the indicator with no title by default
QwikHttpConfig.setDefaultLoadingTitle("")
QwikHttpLoadingIndicatorDelegate
通过在QwikHttpConfig上设置QwikHttpLoadingIndicatorDelegate,QwikHttp将允许您轻松使用自己的加载指示器类。您可以使用这款方案来使用类似的指示器如MBProgressHUD或SwiftSpinner,但由QwikHttp来处理显示和隐藏它们,您无需亲自操作。
以下示例展示了使用单例辅助类来处理指示器,但这也可以在您应用代理、数据服务类或视图中完成。
import SwiftSpinner
public class QwikHelper : QwikHttpLoadingIndicatorDelegate
{
public class func shared() -> QwikHelper {
struct Singleton {
static let instance = QwikHelper()
}
QwikHttpConfig.loadingIndicatorDelegate = Singleton.instance
return Singleton.instance
}
@objc public func showIndicator(title: String!)
{
SwiftSpinner.show(title)
}
@objc public func hideIndicator()
{
SwiftSpinner.hide()
}
}
标准头部
配置请求以发送一组标准头部,无需在每个请求中显式发送它们。
QwikHttpConfig.standardHeaders = ["api_key" : "123123" ]
轻松从特定请求中移除标准头部。
QwikHttp("http://test.com", httpMethod: .get)
.setAvoidStandardHeaders(true).run()
响应与请求拦截器
QwikHttp 允许您设置响应拦截器,这些拦截器可以在每个响应返回之前被选择性地调用。使用此拦截器,您可以对您的响应进行一些修改,甚至可以干净地处理未经授权的响应,在特定条件下允许您刷新 OAuth 令牌或显示登录界面。
您也可以使用请求拦截器来拦截发送之前的请求。这可以允许您在发送请求之前检测令牌是否已过期或操作是否未经授权。
请注意,拦截器可以在每个请求的基础上禁用。
public class QwikHelper : QwikHttpResponseInterceptor, QwikHttpRequestInterceptor
{
public class func shared() -> QwikHelper {
struct Singleton {
static let instance = QwikHelper()
}
QwikHttpConfig.responseInterceptor = Singleton.instance
return Singleton.instance
}
@objc public func shouldInterceptResponse(response: NSURLResponse!) -> Bool
{
//TODO check for an unautorized response and return true if so
return false
}
@objc public func interceptResponse(request : QwikHttp!, handler: (NSData?, NSURLResponse?, NSError?) -> Void)
{
//TODO check to see if response means that the token must be refreshed
//if the token needs refreshing, refresh it- then save the new token to your auth service
//now update the auth header in the QwikHttp request and reset and run it again.
//call the handler with the results of the new request.
}
public func shouldInterceptRequest(request: QwikHttp!) -> Bool
{
//check for an expired token date on your current token
return true
}
public func interceptRequest(request : QwikHttp!, handler: (NSData?, NSURLResponse?, NSError?) -> Void)
{
//TODO refresh your token, restart the request
//update the auth headers with the new token
request.getResponse(NSData.self) { (data, error, request) -> Void! in
handler(data,request.response,error)
}
}
日志记录
您可以使用 request.printDebugInfo()
命令轻松看到您 HTTP 请求的请求级别信息。这将在您的调试器中产生类似以下内容:
----- QwikHttp Request -----
POST to https://www.server.com/api/oauth/token/
HEADERS:
Content-Type: application/x-www-form-urlencoded
BODY:
grant_type=password&password=Password
RESPONSE: 200
{"access_token": "AXr4YoEAqwvrFz3BeAJZPKWf4z7Zkz"}
您还可以在 QwikHttpConfig 中设置默认日志级别,以便默认情况下打印调试信息。级别如下:
- 错误(默认):在出现错误时打印调试语句
- 请求:为每个请求打印调试语句
- 调试:执行上述操作并于请求过程中打印调试和低级别信息
- 无:关闭日志
这可以在全局级别通过 QwikHttpConfig 设置,并使用 QwikHttp.setLoggingLevel() 在请求级别进行覆盖。
保留并重新运行
由于 QwikHttp 是一个对象,您可以保留它,传递它并再次运行它!
var qwikHttp : QwikHttp
func setup()
{
let self.qwikHttp = QwikHttp("https://api.com", httpMethod: .get)
run(self.qwikHttp)
//run it again after some delay
NSThread.sleep(1000)
self.qwikHttp.reset()
run(self.qwikHttp)
}
func run(qwikHttp: QwikHttp)
{
qwikHttp.send()
}
这也意味着如果您不想使用内联、构建器风格语法,您不必这样做!
let self.qwikHttp = QwikHttp("https://api.com", httpMethod: .get)
self.qwikHttp.addParams([:])
self.qwikHttp.addHeaders([:])
self.qwikHttp.run()
更多可选配置
针对每个请求设置超时和缓存策略
qwikHttp.setTimeOut(200)
qwikHttp.setCachePolicy(NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData)
qwikHttp.setResponseThread(.Background)
设置默认行为
除了被重写外,该设置适用于您所有的请求
QwikHttpConfig.defaultTimeOut = 300
QwikHttpConfig.defaultParameterType = .FormEncoded
QwikHttpConfig.defaultLoadingTitle = "Loading"
QwikHttpConfig.defaultCachePolicy = .ReloadIgnoringLocalCacheData
QwikHttpConfig.setDefaultResponseThread(.Background)
自定义URL会话
自定义URL会话,为您的URL会话提供自己的URL会话代理。这对于处理代理的URLSession:didReceiveChallenge方法非常有用。如果没有指定,将使用UrlSession.shared。
QwikHttpConfig.urlSession = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}
您可以通过创建一个使用自定义URL会话的新请求发送器来按请求设置。
QwikHttp("https://api.com", httpMethod: .get)
.setRequestSender(UrlSessionRequestSender(urlSession: URLSession.shared))
.send()
自定义请求发送器
默认情况下,请求是用UrlSessionRequestSender和共享的URL会话发送的,但您可以创建新的类来执行QwikHttpRequestSender协议,以使用其他机制(如UrlConnection)发送请求,或者模拟调用。
// this is our protocol definition
@objc public protocol QwikHttpRequestSender {
@objc func sendRequest(_ request: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void)
}
// create your new request sender class
class MockRequestSender: QwikHttpRequestSender {
public func sendRequest(_ request: URLRequest, handler: @escaping (Data?, URLResponse?, Error?) -> Void)
{
let mockResult = "Mock. Yeah!".data(using: .utf8)
handler(mockResult, nil, nil)
}
}
// pass your request sender during request creation
QwikHttp("https://api.com", httpMethod: .get)
.setRequestSender(MockRequestSender())
.send()
Objective-C
QwikHttp通过导入其Objective-C类文件与Objective-C兼容。QwikHttp的Objective-C版本支持Swift版本的大多数功能,但Generics除外。您不使用泛型类型处理程序,而使用布尔值处理程序或字符串、数据、字典或字典数组处理程序,并在必要时利用QwikJson来反序列化您的对象。
#import "QwikHttp-Swift.h"
@implementation ObjcViewController
-(IBAction)sendRequest:(id)sender
{
[[[[QwikHttp alloc]init:@"https://resttest2016.herokuapp.com/restaurants"
httpMethod:HttpRequestMethodGet]
addUrlParams:@{@"format" : @"json"}]
getArrayResponse:^(NSArray * results, NSError * error, QwikHttp * request) {
if(results)
{
NSArray * restaurants = [Restaurant arrayForJsonArray:data ofClass:[Restaurant class]];
[UIAlertController showAlertWithTitle:@"Success" andMessage:[NSString stringWithFormat:@"Got %li",(long)restaurants.count] from:self];
}
}];
}
安装
Pods
您可以通过 CocoaPods 查找 QwikHttp。要安装它,只需将以下行添加到您的 Podfile 即可。
pod "QwikHttp"
Swift 兼容性错误
如果您遇到了这个构建错误,并且已经运行过“编辑 -> 转换 -> 更新到当前 Swift 语法”,请尝试在 podfile 中添加以下内容:“使用旧版 Swift 语言版本”(SWIFT_VERSION)需要正确配置...
- 在 XCode 的资源管理器中选择 Pods 项目
- 选择 QwikHttp 目标
- 在项目设置下,找到 LEGACY SWIFT VERSION,将其设置为否。即使它已经设置,也需要重新设置。
其他注意事项
此外,请查看 SeaseAssist pod,它提供了一系列有用的辅助工具,可以让编写 iOS 代码变得更加简单!https://github.com/logansease/SeaseAssist
作者
Logan Sease, [email protected]
如果您使用并喜欢 QwikHttp,我很乐意听到您的反馈。我对这个项目非常激动,并期待获得一些反馈。
许可证
QwikHttp 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。