[hidecontent type="logged" desc="隐藏内容:登录后可查看"]
CocoaPods
pod 'Dratini'
Carthage
github "kevin0571/Dratini"
Swift包管理器
dependencies: [
.Package(url: "https://github.com/kevin0571/Dratini.git", majorVersion: 1)
]
以下是发送请求并观察其响应的一些基本步骤。
设置请求队列:
let requestQueue = RequestQueue(baseURL: URL(string: "http://example.com"))
// Delegate and configuration are not required.
// Set the delegate(RequestQueueDelegate) if you wish to get callbacks for each step.
// RequestQueueConfiguration.default is used if configuration is not specified.
建议保留一个共享的 RequestQueue:
extension RequestQueue {
static let shared = RequestQueue(baseURL: URL(string: "http://example.com"))
}
描述您的请求、参数和响应:
struct LogInRequest: Request {
typealias ParametersType = LogInParameters
typealias ResponseType = LogInResponse
var parameters: LogInParameters
func path() -> String {
return "/login"
}
func method() -> HTTPMethod {
return .post
}
}
// There are several built-in Parameters types:
// - DefaultQueryString for query string, it will mostly be used in GET request.
// - URLEncodedBodyData for URL encoded body data.
// - JSONBodyData for JSON format body data.
// - MultipartFormData for multipart form data, it will mostly be used for uploading file.
//
// In order to allow you to keep the naming convention of different platforms,
// property name of DefaultQueryString, URLEncodedBodyData and JSONBodyData will be mapped to other naming convention.
// By default property will be converted to lowercase name separated by underscore,
// e.g. accessToken will be converted to access_token.
// You can set the mapping by overriding "serializableMapping" function.
// See more details in Ditto project's README.
struct LogInParameters: URLEncodedBodyData {
let username: String
let password: String
}
struct LogInResponse: Response {
let username: String
let name: String
init?(data: ResponseData, response: URLResponse) {
// - Use data.data to access raw response data.
// - Use data.jsonObject to access JSON format dictionary.
// - Use data.jsonArray to access JSON format array.
// - Use data.string to access UTF8 string.
guard let username = data.jsonObject["username"] as? String,
let name = data.jsonObject["name"] as? String else {
return nil
}
self.username = username
self.name = name
}
}
发送请求并观察响应:
let request = LogInRequest(parameters: LogInParameters(username: username,
password: password))
let requestID = RequestQueue.shared.add(request)
// Observe by using requestID.
// The observer will be removed by RequestQueue after the request is finished.
requestQueue.addObserver(for: requestID) { (result: Result<LogInResponse>) in
guard let response = result.response else {
// Show error message
return
}
// Update UI by using response.username and response.name
}
// Observe a specific response type.
// The observer is owned by an owner. The owner is held weakly by RequestQueue,
// thus the observer will be removed if owner is released.
requestQueue.addObserver(ownedBy: self) { [weak self] (result: Result<LogInResponse>) in
// ...
}
// NOTE: observer callback is called in main thread.
有时您需要使用 Dratini 做更多的事情,这里有一些您可能需要的功能,例如上传文件、拦截请求和响应的不同状态。
上传文件:
let data = MultipartFormData()
// Append file with fileURL
data.append(fileURL: fileURL, withName: name, fileName: fileName, mimeType: "application/x-plist")
// Append raw file data
data.append(data: fileData, withName: name, fileName: fileName, mimeType: "application/x-plist")
// Assume we've created UploadFileRequest
let request = UploadFileRequest(parameters: data)
// Send out request
// ...
拦截请求状态:
// Conform to Request with RequestDelegate to get callbacks of different states.
struct LogInRequest: Request, RequestDelegate {
// ...
func requestWillSend(_ urlRequest: inout URLRequest) {
// Called before request is sent out.
// You are able to modify the URLRequest: update HTTP header for example.
}
func requestDidSend(_ urlRequest: URLRequest) {
// Called after request is sent out.
}
func request(_ urlRequest: URLRequest, didFailWith error: DRError) {
// Called when request is failed to be sent out or response is failed to be created.
}
}
在创建响应和拦截响应状态之前验证响应:
struct LogInResponse: Response, ResponseDelegate {
// ...
// Validate the response before it's created.
static func validate(_ response: URLResponse) -> Bool {
guard let httpResponse = response as? HTTPURLResponse else {
return true
}
return httpResponse.statusCode >= 200 &&
httpResponse.statusCode < 300 &&
httpResponse.allHeaderFields["Token"] != nil
}
// Called after response is created.
func responseDidReceive(_ response: URLResponse) {
guard let httpResponse = response as? HTTPURLResponse,
let token = httpResponse.allHeaderFields["Token"] else {
return nil
}
// Save your token
}
}
有时需要所有请求和响应的通用逻辑,RequestQueueDelegate 在这里为您服务:
class MyRequestQueueDelegate: RequestQueueDelegate {
public func requestQueue(_ requestQueue: RequestQueue, willSend request: inout URLRequest) {
// Called before each request is sent out.
}
public func requestQueue(_ requestQueue: RequestQueue, didSend request: URLRequest) {
// Called after each request is sent out.
}
public func requestQueue(_ requestQueue: RequestQueue, didFailWith request: URLRequest, error: DRError) {
// Called when request is failed to be sent out or response is failed to be created.
}
public func requestQueue(_ requestQueue: RequestQueue, didReceive response: URLResponse) {
// Called after response is created.
}
}
extension RequestQueue {
// Set delegate when creating RequestQueue.
static let shared = RequestQueue(delegate: MyRequestQueueDelegate(), baseURL: URL(string: "http://example.com")!)
}
检查请求是否完成并取消它:
let isFinished = RequestQueue.shared.isFinished(requestID)
RequestQueue.shared.cancel(requestID)
当您真的不需要参数或响应时,您可以使用:
EmptyParameters
EmptyResponse
如果您希望自定义查询字符串或正文数据编码,您可以通过采用 QueryString 或 BodyData 协议来实现自己的编码。
struct MyBodyData: BodyData {
let string: String
var contentType: String {
return "my-content-type"
}
func encode() throws -> Data {
return string.data(using: .utf8)!
}
}
[/hidecontent]