NerdzNetworking

NerdzNetworking 是对 URLSessionURLRequest 的封装,旨在简化使用 Swift 语言创建和管理网络请求的过程。


示例

您需要定义请求。

class LoginWithFacebookRequest: Request {
    typealias ResponseObjectType = User
    typealias ErrorType = AuthError
    
    let path = "login/facebook"
    let methong = .post
    let body: RequestBody?
    
    init(token: String) {   
        body = .params(
            [
                "token": token
            ]
        )
    }
}

然后直接使用即可。(每个调用都是可选的,可以根据需要跳过

LoginWithFacebookRequest(token: fbToken)
    .execute()
    
    .onSuccess { user in
        ...
    }
    
    .onFail { error in
        ...
    }
    
    .onStart { operation in
        ...
    }
    
    .onProgress { progress in
        ...
    }
    
    .onDebug { info in
        ...
    }
}

理念

结构

创建 NerdzNetworking 库的主要理念是将网络请求最大程度地拆分成小块。理想情况是每个请求都有一个单独的类/结构体。 这应该有助于在文件结构中轻松导航和搜索特定请求的信息。

Requests example


强类型化

该库试图遵循的另一个流程是预定义的选项和使用的简便性。 我们尝试在协议之上使用泛型类型,以及使用枚举而不是原始值。 例如 - 预定义的标头,如 Content-TypeAccept。 我们没有提供任何值的可能性,而是定义了枚举,将可能的输入限制为仅预定义的场景,例如 .application(.json)。 为了简单起见,前面提到的标头已经为您预先选择了 .application(.json) 值,因此如果您使用的是标准 REST API,则一切都已准备就绪。


教程

端点设置

首先,您需要设置将在以后用于执行请求的端点。 为此,您应该使用 Endpoint 类。 Endpoint 类将收集执行请求的所有常规设置。 您可以随时更改任何参数。

let endpoint = Endpoint(baseUrl: myBaseUrl)
endpoint.headers = defaultHeaders // Specifying some default headers like OS, device language, device model, etc.
endpoint.headers.authToken = .bearer(tokenString) // Specifying user token

创建端点后,您可以将其标记为 default,以便每个请求都会自动拾取它。

Endpoint.default = endpoint

您可以根据需要更改 default 端点配置或环境。


请求创建

要创建请求,您应该实现 Request 协议。 您可以为每个请求使用单独的类或 enum


为每个请求使用单独的类

class MyRequest: Request {
    typealias ResponseObjectType = MyExpectedResponse
    typealias ErrorType = MyUnexpectedError
    
    let path = "my/path/to/backend" // Required
    let methong = .get // Optional
    let queryParams = [("key", "value")] // Optional
    let body = .params(["key", "value"]) // Optional
    let headers = [RequestHeaderKey("key"): "value", .contentType: "application/json"] // Optional
    let timeout = 60 // Optional, by defauld will be picked from Endpoint
    let endpoint = myEndpoint // Optional, by default will be a Endpoint.default
}

这只是一个示例,可能您不会拥有所有必需和静态的参数。 要动态创建请求 - 您可以使用初始化器,这些初始化器将采用请求所需的动态参数。 例如 - 一些动态 bodyParams 或动态路径。


默认请求

您可以使用内置类 DefaultRequest 执行请求,而无需创建单独的类。

let myRequest = DefaultRequest<MyExpectedResponse, MyUnexpectedError>(
    path: "my/path/to/backend", 
    method: .get, 
    queryParams: [("key", "value")], 
    body: .params(["key", "value"]), 
    headers: [RequestHeaderKey("key"): "value", .contentType: "application/json"], 
    timeout: 60,
    responseConverter: myResponseConverter,
    errorConverter: myErrorConverter,
    endpoint: myEndpoint)

Multipart 请求

NerdzNetworking 库还提供了一种简单的方法来创建和执行 multipart form-data 请求。 您只需要实现 MultipartFormDataRequest 而不是 Request 协议,或者使用 DefaultMultipartFormDataRequest 类。

除了 Request 字段之外,您还需要提供 files 字段,该字段是 MultipartFile 协议实例。 您可以实现此协议或使用 DefaultMultipartFile 类。

class MyMultipartRequest: MultipartFormDataRequest {
    // Same fields as in Request example
    
    let files: [MultipartFile] = [
        DefaultMultipartFile(resource: fileData, mime: .image(.png), fileName: "avatar1"),
        DefaultMultipartFile(resource: fileUrl, mime: .audio(.mp4), fileName: "song"),
        DefaultMultipartFile(resource: filePath, mime: .image(.jpeg), fileName: "avatar2")
    ]
}

请求执行

要执行请求,您可以使用以下构造


处理执行过程

要处理执行过程,您可以在调用 execute 方法后使用 future-style 方法。(每个方法都是可选的,因此只使用您真正需要的那些

myRequest
    .execute()
    .responseOn(.main) // Response will be returned in `.main` queue
    .retryOnFail(false) // If this request will fail - system will not try to rerun it again
    
    .onStart { requestOperation in
        // Will be called when request will start and return request operation that allow to control request during the execution
    }
    
    .onProgress { progress in
        // Will provide a progress of request execution. Useful for multipart uploading requests
    }
    
    .onSuccess { response in
        // Will return a response object specified in request under `ResponseType`
    }
    
    .onFail { error in
        // Will return `ErrorResponse` that might contain `ErrorType` specified in request
    }
    
    .onDebug { info in
        // Will return `DebugInfo` that contain a list of useful information for debugging request failure 
    }

映射

目前,NerdzNetworking 库仅支持原生 Codable 映射。 教程


响应转换器

NerdzNetworking 支持响应转换器,可以在映射过程之前转换响应。 如果您需要将响应数据调整为内部模型,或者绕过父对象仅映射子对象,这可能很有用。



ResponseJsonConverter

您还可以提供响应转换器,以便在映射到预期响应开始之前转换一些未正确返回的响应。 此处的负责协议是 ResponseJsonConverter

响应转换器应在 Request 类中的 responseConverter成功)或/和 errorConverter失败)字段下指定。

您可以拥有实现 ResponseJsonConverter 协议的自己的转换器,或者使用内置实现:KeyPathResponseConverterClosureResponseConverter



KeyPathResponseConverter

KeyPathResponseConverter 允许您通过到节点的特定 pathJSON 子节点中提取数据。

class MyRequest: Request {
    var responseConverter: ResponseJsonConverter {
        KeyPathResponseConverter(path: "path/to/node")
    }
    
    var errorConverter: ResponseJsonConverter {
        KeyPathResponseConverter(path: "path/to/error")
    }
}


ClosureResponseConverter

ClosureResponseConverter 允许您通过闭包提供自定义转换。 您将需要提供一个 closure,该闭包接受 Any 并在转换后返回 Any

class MyRequest: Request {
    var responseConverter: ResponseJsonConverter {
        ClosureResponseConverter { response in
            // Return converted response for success response
        }
    }
    
    var errorConverter: ResponseJsonConverter {
        ClosureResponseConverter { response in
            // Return converted response for error response
        }
    }
}


自定义 ResponseJsonConverter

您可以通过实现 ResponseJsonConverter 协议来实现您自己的转换器。

class MyResponseConverter: ResponseJsonConverter {
    func convertedJson(from json: Any) throws -> Any {
        // Provide convertation and return converted code
    }
}

安装

CocoaPods

您可以使用 CocoaPods 依赖管理器来安装 NerdzNetworking。 在您的 Podfile 中指定

pod 'NerdzNetworking', '~> 1.0.1'

Swift Package Manager

要将 NerdzNetworking 添加到基于 Swift Package Manager 的项目中,请添加

.package(url: "https://github.com/nerdzlab/NerdzNetworking")

文档


@ Endpoint

表示端点的类,其中包含执行请求的所有设置。 您需要创建至少一个实例才能执行请求。


属性

名称 类型 可访问性 描述
Endpoint.default Endpoint static read-write 将用于执行请求的端点实例
baseUrl URL readonly 端点基本 URL
decoder JSONDecoder? 如果提供,则将默认用于解码 json 响应的解码器
responseQueue DispatchQueue? 将默认用于调度请求完成的队列
retryingCount Int 在中止请求执行错误之前应发生的重试次数
observation ObservationManager 一个管理器,您应该在其中注册观察者,以观察不同类型的请求/响应
requestRetrying RequestRetryingManager 一个管理器,您应该在其中注册重试器
sessionConfiguration URLSessionConfiguration readonly 用于内部 URLSession 的配置
headers [RequestHeaderKey: String] read-write 将与每个请求一起使用的标头

方法

init(
    baseUrl             : URL,
    decoder             : JSONDecoder? = nil,
    responseQueue       : DispatchQueue? = nil,
    sessionConfiguration: URLSessionConfiguration = .default,
    retryingCount       : Int = 1,
    headers             : [RequestHeaderKey: String] = [:]
)

使用所有参数进行初始化

名称 类型 默认值 描述
baseUrl URL 端点基本 URL
decoder JSONDecoder? nil JSON 响应的默认解码器
responseQueue DispatchQueue nil 完成的默认响应队列
sessionConfiguration URLSessionConfiguration .default 将用于内部 URLSession 的配置
retryingCount Int 1 请求失败的重试次数
headers [RequestHeaderKey: String] [:] 将与每个请求一起使用的标头


func execute<T: Request>(_ request: T) -> ResponseInfoBuilder<T>

在当前端点上执行请求

名称 类型 默认值 描述
request Request 要执行的请求


func cURL<T: Request>(for request: T) throws -> String

返回所提供请求的 cURL 字符串表示形式

对于比较来自 Postman 或 Proxyman 等应用程序和代码的请求,或从终端快速执行请求非常有用。



func useAsDefault()

将当前实例设置为未来执行的 default


@ ExecutionOperation<T>

泛型T: Request

一个表示请求操作执行参数(如 queue)和完成(如 onSuccess)的类。


属性

名称 类型 可访问性 描述
request T readonly 在操作中使用的请求

方法

func response(on queue: DispatchQueue) -> Self

设置将调用所有完成的队列。 默认情况下将使用 main 队列

名称 类型 默认值 描述
queue DispatchQueue 将调用完成的队列


func decode(with decoder: JSONDecoder) -> Self

设置将用于解码 JSON 响应或错误的解码器

名称 类型 默认值 描述
decoder JSONDecoder 用于响应 JSON 解码的解码器


func retryOnFail(_ retryingCount: Int) -> Self

为请求失败设置重试计数。 默认情况下等于 1

名称 类型 默认值 描述
retryingCount Int 重试计数数字


func onSuccess(_ closure: @escaping ResponseSuccessCallback) -> Self

ResponseSuccessCallback = (T.ResponseObjectType) -> Void

注册如果请求成功将调用的闭包

您可以多次调用此方法,并且将触发每个注册的闭包

名称 类型 默认值 描述
closure (T.ResponseObjectType) -> Void 触发的闭包


func onFail(_ closure: @escaping FailCallback) -> Self

FailCallback = (ErrorResponse<T.ErrorType>) -> Void

注册如果请求失败将调用的闭包

您可以多次调用此方法,并且将触发每个注册的闭包

名称 类型 默认值 描述
closure (ErrorResponse<T.ErrorType>) -> Void 触发的闭包


func onProgress(_ closure: @escaping ProgressCallback) -> Self

ProgressCallback = (Double) -> Void

注册将返回请求处理进度的闭包

您可以多次调用此方法,并且将触发每个注册的闭包

名称 类型 默认值 描述
closure (Double) -> Void 触发的闭包


func onDebug(_ closure: @escaping DebugCallback) -> Self

DebugCallback = (DebugInfo) -> Void

注册将返回请求和响应信息以进行调试的闭包

您可以多次调用此方法,并且将触发每个注册的闭包

名称 类型 默认值 描述
closure (DebugInfo) -> Void 触发的闭包


func onStart(_ closure: @escaping StartCallback) -> Self

StartCallback = () -> Void

注册将在请求开始处理时触发的闭包

您可以多次调用此方法,并且将触发每个注册的闭包

名称 类型 默认值 描述
closure () -> Void 触发的闭包


func calncel()

取消请求处理


@ Request 协议

类型protocol

表示单个请求的协议。 您可以实现此协议,然后执行它。 您可以使用已实现此协议的 DefaultRequest 结构来执行请求。


associatedtype

名称 类型 可访问性 描述
ResponseObjectType ResponseObject 来自服务器的预期响应的类型。 它应该实现 ResponseObject 协议
ErrorType ServerError 来自服务器的预期错误的类型。 它应该实现 ServerError 协议

属性

名称 类型 可访问性 描述
path String get required 请求路径
method HTTPMethod get required 请求方法
queryParams [(String, String)] get optional 请求查询参数,表示为元组数组以保存顺序
body RequestBody? get optional 请求正文
headers [RequestHeaderKey: String] get optional 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval get optional 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? get optional 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? get optional 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? get optional 将用于执行的端点
decoder JSONDecoder? get optional 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器

方法

func execute(on endpoint: Endpoint) -> ResponseInfoBuilder<Self>

在提供的端点上执行当前请求

名称 类型 默认值 描述
endpoint Endpoint 当前请求将在其上执行的端点。


func execute() -> ResponseInfoBuilder<Self>

Endpoint.default 实例上执行当前请求


@ DefaultRequest 结构

类型struct

实现Request

Request 协议的默认实现,可用于执行请求而无需创建额外的类


泛型

名称 类型 描述
响应 ResponseObject 成功响应对象的类型
错误 ServerError 错误响应对象的类型

属性

名称 类型 可访问性 描述
path String read-write 请求路径
method HTTPMethod read-write 请求方法
queryParams [(String, String)] read-write 请求查询参数,表示为元组数组以保存顺序
body RequestBody? read-write 请求正文
headers [RequestHeaderKey: String] read-write 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval read-write 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? read-write 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? read-write 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? read-write 将用于执行的端点
decoder JSONDecoder? read-write 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器

方法

init(
    path                : String, 
    method              : HTTPMethod, 
    queryParams         : [(String, String)] = [], 
    body                : RequestBody? = nil, 
    headers             : [RequestHeaderKey: String] = [:], 
    timeout             : TimeInterval? = nil,
    responseConverter   : ResponseJsonConverter? = nil,
    errorConverter      : ResponseJsonConverter? = nil,
    endpoint            : Endpoint? = nil,
    decoder             : JSONDecoder? = nil
)

使用所有可能的参数初始化 DefaultRequest 对象

名称 类型 默认值 描述
path String - 请求路径
method HTTPMethod - 请求方法
queryParams [(String, String)] [] 请求查询参数,表示为元组数组以保存顺序
bodyParams [String: Any] [:] 请求体参数
headers [RequestHeaderKey: String] [:] 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval nil 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? nil 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? nil 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? nil 将用于执行的端点
decoder JSONDecoder? nil 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器

@ MultipartFormDataRequest 协议

类型protocol

继承自: Request 协议

表示 multipart form-data 请求的协议。该协议继承了 Request 协议,并在其基础上添加了 files 属性。因此,它与 Request 协议基本相同。您可以使用 DefaultMultipartFormDataRequest 结构体,该结构体已经实现了此协议,来执行 multipart 请求。


associatedtype

名称 类型 可访问性 描述
ResponseObjectType ResponseObject 来自服务器的预期响应的类型。 它应该实现 ResponseObject 协议
ErrorType ServerError 来自服务器的预期错误的类型。 它应该实现 ServerError 协议

属性

名称 类型 可访问性 描述
path String get required 请求路径
method HTTPMethod get required 请求方法
queryParams [(String, String)] get optional 请求查询参数,表示为元组数组以保存顺序
body RequestBody? get optional 请求正文
headers [RequestHeaderKey: String] get optional 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval get optional 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? get optional 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? get optional 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? get optional 将用于执行的端点
decoder JSONDecoder? get optional 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器
files [MultipartFile] get required 需要随请求一起处理的文件列表

方法

func execute(on endpoint: Endpoint) -> ResponseInfoBuilder<Self>

在提供的端点上执行当前请求

名称 类型 默认值 描述
endpoint Endpoint 当前请求将在其上执行的端点。


func execute() -> ResponseInfoBuilder<Self>

Endpoint.default 实例上执行当前请求


@ DefaultMultipartFormDataRequest 结构体

类型struct

实现: MultipartFormDataRequest

MultipartFormDataRequest 协议的默认实现,可用于执行 multipart 请求而无需创建额外的类


泛型

名称 类型 描述
响应 ResponseObject 成功响应对象的类型
错误 ServerError 错误响应对象的类型

属性

名称 类型 可访问性 描述
path String read-write 请求路径
method HTTPMethod read-write 请求方法
queryParams [(String, String)] read-write 请求查询参数,表示为元组数组以保存顺序
body RequestBody? read-write 请求正文
headers [RequestHeaderKey: String] read-write 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval read-write 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? read-write 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? read-write 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? read-write 将用于执行的端点
decoder JSONDecoder? read-write 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器
files [MultipartFile] read-write 需要在请求中处理的文件列表

方法

init(
    path                : String, 
    method              : HTTPMethod, 
    queryParams         : [(String, String)] = [], 
    body                : RequestBody? = nil, 
    headers             : [RequestHeaderKey: String] = [:], 
    timeout             : TimeInterval? = nil,
    responseConverter   : ResponseJsonConverter? = nil,
    errorConverter      : ResponseJsonConverter? = nil,
    endpoint            : Endpoint? = nil,
    decoder             : JSONDecoder? = nil,
    files               : [MultipartFile] = []
)

使用所有可能的参数初始化 DefaultMultipartFormDataRequest 对象

名称 类型 默认值 描述
path String - 请求路径
method HTTPMethod - 请求方法
queryParams [(String, String)] [] 请求查询参数,表示为元组数组以保存顺序
bodyParams [String: Any] [:] 请求体参数
headers [RequestHeaderKey: String] [:] 特定于请求的标头。 将添加到来自 Endpoint 的标头中
timeout TimeInterval nil 请求超时。 如果未指定,将使用来自 Endpoint 的默认值
responseConverter ResponseJsonConverter? nil 成功的响应转换器。 将在映射到 ResponseObjectType 之前进行转换
errorConverter ResponseJsonConverter? nil 错误响应转换器。 将在映射到 ErrorType 之前进行转换
endpoint Endpoint? nil 将用于执行的端点
decoder JSONDecoder? nil 将用于解码响应的 JSON 响应解码器。 如果未提供,将使用来自 Endpoint 的解码器
files [MultipartFile] [] 需要在请求中处理的文件列表

@ DefaultMultipartFile 结构体

MultipartFile 协议的默认实现,如果您不想为发送 multipart 请求创建额外的类,可以使用它


属性

名称 类型 可访问性 描述
fileName String read-write 文件名
mime MimeType read-write 文件 mime 类型
resource MultipartResourceConvertable read-write 文件数据的表示形式。可能是 StringDataURLInputStream

方法

init(
    resource: MultipartResourceConvertable, 
    mime    : MimeType, 
    fileName: String
)

使用所有可能的参数初始化 DefaultMultipartFile 对象

名称 类型 默认值 描述
resource MultipartResourceConvertable 文件名
mime MimeType 文件 mime 类型
fileName String 文件数据的表示形式。可能是 StringDataURLInputStream

@ ServerError 协议

表示从服务器返回的错误的协议


属性

名称 类型 可访问性 描述
message String get required 错误消息

支持的类型


@ HTTPMethod 枚举

类型: enum (枚举)

继承自: String

表示请求 http 方法的枚举。

名称 描述
.get GET http 方法
.post POST http 方法
.put PUT http 方法
.delete DELETE http 方法
.path PATH http 方法

@ RequestBody 枚举

类型: enum (枚举)

表示不同类型的请求体的枚举

名称 参数 描述
.raw value: Data 原始数据
.string value: String 字符串体
.params value: [String: Any] 使用参数形成的正文

@ DebugInfo 结构体

类型struct

表示用于调试网络请求的信息。

可以从 ExecutionOperation 类接收。


属性

名称 类型 可访问性 描述
sessionConfiguration URLSessionConfiguration readonly 当前请求的 URLSession 配置
request URLRequest readonly 为执行而构建的 URLRequest
dataResponse Data? readonly Data 格式的响应
urlResponse HTTPURLResponse readonly 系统返回的响应
errorResponse Error? readonly 系统返回的响应错误
requestDuration TimeInterval readonly 请求执行持续时间
cURL String? readonly cURL 格式的请求表示
stringResponse String? readonly String 格式的响应
jsonResponse Any? readonly JSON 格式的响应

API 待续...


下一步


许可证

此代码在 MIT 许可证下分发。 有关更多信息,请参见 LICENSE 文件。