iONess (iOS 网络会话) 是 Home Credit Indonesia iOS 应用程序使用的 iOS 平台的 HTTP 请求助手。它使用 Ergo 作为并发助手和 Promise 管道化工具。
要运行示例项目,请克隆仓库,并首先从 Example 目录运行 pod install。
iONess 可通过 CocoaPods 获得。要安装它,只需将以下行添加到您的 Podfile
适用于 iOS 10 或更高版本
pod 'iONess', '~> 2.0'
或适用于 iOS 8 或更高版本
pod 'iONess', '~> 1.2.5'
在 Package.swift 中作为目标依赖项添加。为 iOS 10 或更高版本使用 2.0.2 作为其版本,或为 iOS 8 或更高版本使用 1.2.5
dependencies: [
.package(url: "https://github.com/oss-homecredit-id/iONess.git", .upToNextMajor(from: "2.0.2"))
]
在您的目标中使用它,如 iONess
.target(
name: "MyModule",
dependencies: ["iONess"]
)
iONess 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。
iONess 旨在简化 HTTP 请求的请求过程。您只需使用 Ness / NetworkSessionManager 类创建请求即可
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
.dataRequest()
.then { result in
// do something with result this will not executed when request failed
}
或者完全不使用 completion
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
.dataRequest()
当调用 dataRequest() 数据时,无论它是否有 completion,它总是会立即执行请求。dataRequest() 实际上从 Ergo 返回 Promise 对象,因此您可以始终执行 Ergo Promise 可以执行的所有操作
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
.dataRequest()
.then { result in
// do something with result this will not executed when request failed
}.handle { error in
// do something if error occurs
}.finally { result, error in
// do something regarding of error or not after request completed
}
您可以随时 在此处 查看 Ergo 的 Promise 可以执行的操作。
要创建请求,您可以执行类似以下操作
Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
.set(urlParameters: ["param1": "value1", "param2": "value2"])
.set(headers: ["Authorization": myToken])
.set(body: dataBody)
..
..
或使用自定义 URLSession
// create session
var session = URLSession()
session.configuration = myCustomConfig
session.delegateQueue = myOperationQueue
..
..
// create Ness instance
let ness = Ness(with: session)
// create request
ness.httpRequest(.get, withUrl: "https://myurl.com")
.set(urlParameters: ["param1": "value1", "param2": "value2"])
.set(headers: ["Authorization": myToken])
.set(body: dataBody)
..
..
最好保存 Ness 的实例并重复使用它,因为它将仅使用相同的 URLSession 创建请求,除非您想为另一个请求使用任何其他 URLSession。
可用的 HTTP 方法枚举包括
postgetputpatchdeleteheadconnectoptionstracenone 如果您不想包含 HTTP 方法标头custom(String)要设置自定义类型的 body,您需要传递实现 HTTPBodyEncoder 对象的自定义类型编码器,以将对象编码为数据
Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
.set(body: myObject, with encoder: myEndoder) -> Self
..
..
HTTPBodyEncoder 的声明是
public protocol HTTPBodyEncoder {
var relatedHeaders: [String: String]? { get }
func encoder(_ any: Any) throws -> Data
}
relatedHeaders 是与此编码关联的标头,它将自动分配给请求标头。此变量是可选的,因为默认实现返回 nil
有一些不同的默认方法可以使用 iONess 默认 body 编码器设置 body,这些方法是
func set(body: Data) -> Selffunc set(stringBody: String, encoding: String.Encoding = .utf8) -> Selffunc set(jsonBody: [String: Any]) -> Selffunc set(arrayJsonBody: [Any]) -> Selffunc set<EObject: Encodable>(jsonEncodable: EObject) -> Selffunc set<EObject: Encodable>(arrayJsonEncodable: [EObject]) -> Self请求准备就绪后,准备请求,这将返回 Thenable
Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
.set(urlParameters: ["param1": "value1", "param2": "value2"])
.set(headers: ["Authorization": myToken])
.set(body: dataBody)
..
..
.dataRequest()
或者对于下载,您需要提供目标位置 URL,您希望将下载的数据保存在那里
Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
.set(urlParameters: ["param1": "value1", "param2": "value2"])
.set(headers: ["Authorization": myToken])
.set(body: dataBody)
..
..
.downloadRequest(forSavedLocation: myTargetUrl)
或者对于上传,您需要提供文件位置 URL,您想要上传的文件位于那里
Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
.set(urlParameters: ["param1": "value1", "param2": "value2"])
.set(headers: ["Authorization": myToken])
.set(body: dataBody)
..
..
.uploadRequest(forFileLocation: myTargetUrl)
创建数据请求后,您只需使用 then 方法执行请求即可
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.dataRequest()
.then { result in
// do something with result
}
结果是 URLResult 对象,其中包含
urlResponse: URLResponse? 这是原始响应,您可以在 此处 阅读文档error: Error? 如果发生错误,则为错误。在成功响应时,它将为 nilresponseData: Data? 这是响应 body 的原始数据isFailed: Bool 如果请求失败,则为 trueisSucceed: Bool 如果请求成功,则为 truehttpMessage: HTTPResultMessage? 这是请求的响应消息。如果结果不是 HTTP 结果,则为 nilHTTPResultMessage 是来自 URLResult 的详细 HTTP 响应
url: HTTPURLCompatible 这是响应的原始 URLheaders: Header 这是响应的标头body: Data? 这是响应的 bodystatusCode: Int 这是响应的状态代码您可以获取 promise 对象或忽略它。它将返回 DataPromise,其中包含请求的状态
let requestPromise = Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.dataRequest()
let status = requestPromise.status
状态包括
running(Float) 其中包含请求进度的百分比,从 0 到 1droppedidlecompleted(HTTPURLResponse) 其中包含已完成的响应error(Error) 其中包含发生的错误您可以使用 drop() 函数取消请求
requestPromise.drop()
由于 promise 基于 Ergo Promise,因此它包含请求的结果(如果已完成)以及发生的错误
// will be nil if the request is not finished yet or if the error occurs
let result = requestPromise.result
// will be nil if an error did not occur or the request is not finished yet
let error = requestPromise.error
// will be true if request completed
print(requestPromise.isCompleted)
就 Promise 而言,上传请求与数据请求相同。
下载请求与数据请求或上传请求略有不同。下载请求可以暂停和恢复,并且结果也不同
结果是 DownloadResult 对象,其中包含
urlResponse: URLResponse? 这是原始响应,您可以在 此处 阅读文档error: Error? 如果发生错误,则为错误。在成功响应时,它将为 nildataLocalURL: URL? 这是下载数据的位置isFailed: Bool 如果请求失败,则为 trueisSucceed: Bool 如果请求成功,则为 true您可以暂停下载并恢复
request.pause()
let resumeStatus = request.resume()
resume 将返回 ResumeStatus,这是一个枚举
resumedfailToResume要解析 body,您可以执行
let decodedBody = try? result.message.parseBody(using: myDecoder)
parseBody 接受任何实现 ResponseDecoder 的对象。 ResponseDecoder 协议的声明如下所示
public protocol ResponseDecoder {
associatedtype Decoded
func decode(from data: Data) throws -> Decoded
}
所以您可以执行类似以下操作
class MyResponseDecoder: ResponseDecoder {
typealias Decoded = MyObject
func decode(from data: Data) throws -> MyObject {
// do something to decode data into MyObject
}
}
如果您不想从 Data 解析,可以使用默认的基础解码器
class MyJSONResponseDecoder: BaseJSONDecoder<MyObject> {
typealias Decoded = MyObject
override func decode(from json: [String: Any]) throws -> MyObject {
// do something to decode json into MyObject
}
}
class MyStringResponseDecoder: BaseStringDecoder<MyObject> {
typealias Decoded = MyObject
override func decode(from string: String) throws -> MyObject {
// do something to decode string into MyObject
}
}
HTTPResultMessage 具有自动解析 body 的默认函数,即
func parseBody(toStringEndcoded encoding: String.Encoding = .utf8) throws -> Stringfunc parseJSONBody() throws -> [String: Any]func parseArrayJSONBody() throws -> [Any]func parseJSONBody<DObject: Decodable>() throws -> DObjectfunc parseArrayJSONBody<DObject: Decodable>() throws -> [DObject]func parseJSONBody<DOBject: Decodable>(forType type: DOBject.Type) throws -> DOBjectfunc parseArrayJSONBody<DObject: Decodable>(forType type: DObject.Type) throws -> [DObject]您可以像这样为响应添加验证
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.validate(statusCodes: 0..<300)
.validate(shouldHaveHeaders: ["Content-Type": "application/json"])
.dataRequest()
如果响应无效,则它将出现错误或使用错误分派到 handle 闭包中。
提供的 validate 方法包括
validate(statusCode: Int) -> Selfvalidate(statusCodes: Range<Int>) -> Selfvalidate(shouldHaveHeaders headers: [String:String]) -> Selfvalidate(_ validation: HeaderValidator.Validation, _ headers: [String: String]) -> Self您可以添加自定义验证器来验证 http 响应。验证器的类型是 URLValidator
public protocol ResponseValidator {
func validate(for response: URLResponse) -> ResponseValidatorResult
}
ResponseValidatorResult 是一个枚举,其中包含
validinvalidinvalidWithReason(String) 无效,并带有自定义原因,这将是 NetworkSessionError 错误上的描述并像这样放置您的自定义 ResponseValidator
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.validate(using: MyCustomValidator())
.dataRequest()
如果您只想验证 HTTPURLResponse 并自动使其他响应无效,则可以使用 HTTPValidator
public protocol HTTPValidator: URLValidator {
func validate(forHttp response: HTTPURLResponse) -> URLValidatorResult
}
请记住,您可以根据需要放置任意数量的验证器,这些验证器将从第一个到最后一个或直到一个验证器返回 invalid 为止,使用所有这些验证器验证响应。如果您不提供任何 URLValidator,则如果有错误或没有来自服务器的响应,则会认为它是无效的,否则,所有响应都将被认为是有效的
您可以使用 NetworkSessionManagerDelegate 在会话级别全局操作请求或操作
public protocol NetworkSessionManagerDelegate: class {
func ness(_ manager: Ness, willRequest request: URLRequest) -> URLRequest
func ness(_ manager: Ness, didRequest request: URLRequest) -> Void
}
这两种方法都是可选的。这些方法将运行并适用于
ness(_: , willRequest: ) 将在执行任何请求之前运行。您可以在此处操作 URLRequest 对象并返回它,或在请求之前执行任何操作并返回当前的 URLRequestness(_: , didRequest: ) 将在执行任何请求之后运行,但不是在请求完成之后。如果您的请求失败,您可以使用 RetryControl 协议控制何时重试
public protocol RetryControl {
func shouldRetry(
for request: URLRequest,
response: URLResponse?,
error: Error,
didHaveDecision: (RetryControlDecision) -> Void) -> Void
}
该方法将在请求失败时运行。您唯一需要做的是将 RetryControlDecision 传递到 didHaveDecision 闭包中,这是一个带有成员的枚举
noRetry 这将自动使请求失败retryAfter(TimeInterval) 这将在 TimeInterval 后重试相同的请求retry 这将立即重试相同的请求您可以在准备请求时分配 RetryControl
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.dataRequest(with: myRetryControl)
它也适用于下载或上传请求。
iONess 具有一些默认的 RetryControl,即 CounterRetryControl,其基本算法只是计算失败次数,并在计数器达到 maxCount 时停止重试。要使用它,只需在准备时使用您的 maxCount 或可选地使用 TimeInterval(在重试之前)初始化 CounterRetryControl。例如,如果您想自动重试最多 3 次,每次重试延迟 1 秒
Ness.default
.httpRequest(.get, withUrl: "https://myurl.com")
..
..
.dataRequest(
with: CounterRetryControl(
maxRetryCount: 3,
timeIntervalBeforeTryToRetry: 1
)
)
如果发生多个重复请求,您可以使用 DuplicatedHandler 处理要执行的操作
public protocol DuplicatedHandler {
func duplicatedDownload(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<URL>, currentCompletion: @escaping URLCompletion<URL>) -> RequestDuplicatedDecision<URL>
func duplicatedUpload(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<Data>, currentCompletion: @escaping URLCompletion<Data>) -> RequestDuplicatedDecision<Data>
func duplicatedData(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<Data>, currentCompletion: @escaping URLCompletion<Data>) -> RequestDuplicatedDecision<Data>
}
它将根据重复请求的类型请求 RequestDuplicatedDecision。 RequestDuplicatedDecision 是一个带有成员的枚举
dropAndRequestAgain 这将删除之前的请求,并使用当前的 completion 执行新请求dropAndRequestAgainWithCompletion((Param?, URLResponse?, Error?) -> Void) 这将删除之前的请求,并使用自定义 completion 执行新请求ignoreCurrentCompletion 这将忽略当前的 completion,因此当请求完成时,它只会运行第一个请求 completionuseCurrentCompletion 这将忽略之前的 completion,因此当请求完成时,它只会运行最新的请求 completionuseBothCompletion 这将保留之前的 completion,因此当请求完成时,它只会运行所有请求 completionuseCompletion((Param?, URLResponse?, Error?) -> Void) 这将忽略所有 completion 并使用自定义的 completionduplicatedHandler 依附于 Ness \ NetworkSessionManager,因此如果您有使用不同 Ness \ NetworkSessionManager 的重复请求,则不应调用它。
要分配 RequestDuplicatedDecision,您只需将其分配给 duplicatedHandler 属性,或者在初始化时添加它即可
// just handler
let ness = Ness(duplicatedHandler: myHandler)
// with session
let ness = Ness(session: mySession, duplicatedHandler: myHandler)
// using property
ness.duplicatedHandler = myHandler
或者您也可以只使用一些默认的处理程序
// just handler
let ness = Ness(onDuplicated: .keepAllCompletion)
// with session
let ness = Ness(session: mySession, onDuplicated: .keepFirstCompletion)
// using property
ness.duplicatedHandler = DefaultDuplicatedHandler.keepLatestCompletion
有 4 个 DefaultDuplicatedHandler
dropPreviousRequest 这将删除之前的请求,并使用当前的 completion 执行新请求keepAllCompletion 将保留之前的 completion,因此当请求完成时,它只会运行所有请求 completionkeepFirstCompletion 这将忽略当前的 completion,因此当请求完成时,它只会运行第一个请求 completionkeepLatestCompletion 这将忽略之前的 completion,因此当请求完成时,它只会运行最新的请求 completion您知道如何操作,只需克隆并进行 pull request