开源
Dratini:一个网络抽象层
来源:元经纪     阅读:1216
网站管理员
发布于 2023-04-08 08:24
查看主页

概述

Dratini 是一个简洁的网络抽象层。Dratini 使用协议来定义网络请求、参数和响应,这使您的网络层更具可读性和可测试性。

特征

要求

依赖关系

用法

[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 做更多

有时您需要使用 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]

 

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 开源
《黑神话:悟空》销量霸榜多国!开发商游戏科学员工疯狂被挖
直击新声科技CMEF参展首日,20周年庆典启动仪式点燃新征程
“双11”买家电如何更实惠
港口码头吹来智慧的“风”(大数据观察)
让群众享受到 实实在在的优惠

首页

分类

定制方案

消息

我的