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 方法枚举包括
post
get
put
patch
delete
head
connect
options
trace
none
如果您不想包含 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) -> Self
func set(stringBody: String, encoding: String.Encoding = .utf8) -> Self
func set(jsonBody: [String: Any]) -> Self
func set(arrayJsonBody: [Any]) -> Self
func set<EObject: Encodable>(jsonEncodable: EObject) -> Self
func 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 到 1dropped
idle
completed(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
,这是一个枚举
resumed
failToResume
要解析 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 -> String
func parseJSONBody() throws -> [String: Any]
func parseArrayJSONBody() throws -> [Any]
func parseJSONBody<DObject: Decodable>() throws -> DObject
func parseArrayJSONBody<DObject: Decodable>() throws -> [DObject]
func parseJSONBody<DOBject: Decodable>(forType type: DOBject.Type) throws -> DOBject
func 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) -> Self
validate(statusCodes: Range<Int>) -> Self
validate(shouldHaveHeaders headers: [String:String]) -> Self
validate(_ validation: HeaderValidator.Validation, _ headers: [String: String]) -> Self
您可以添加自定义验证器来验证 http 响应。验证器的类型是 URLValidator
public protocol ResponseValidator {
func validate(for response: URLResponse) -> ResponseValidatorResult
}
ResponseValidatorResult
是一个枚举,其中包含
valid
invalid
invalidWithReason(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
对象并返回它,或在请求之前执行任何操作并返回当前的 URLRequest
ness(_: , 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