From the previous understanding and use of Alamofire, we can see that many features are designed around Request throughout the framework, so the Request Request is the core foundation of all requests. So let’s take a look at the important module Request.
Request process
First, let’s take a look at this code:
Alamofire.request("https://httpbin.org/get")
Copy the code
This is a very simple network Request. Let’s look at what’s going on in this Request. Enter the source code to view:
@discardableResult
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
Copy the code
The request function also calls the request method of SessionManager, which indicates that the starting point of network request is from SessionManager. Obviously, Alamofire. Swift should be the top-level wrapper. The underlying file sessionManager.swift is then called. Ok, let’s move on:
@discardableResult
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
originalRequest = try URLRequest(url: url, method: method, headers: headers)
let encodedURLRequest = tryencoding.encode(originalRequest! , with: parameters)return request(encodedURLRequest)
} catch {
return request(originalRequest, failedWith: error)
}
}
Copy the code
This function creates a Request object internally, encodes parameters to the Request, and then calls an internal Request function with parameters to the Request object. Here we go:
@discardableResult
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
var originalRequest: URLRequest?
do {
originalRequest = try urlRequest.asURLRequest()
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
delegate[task] = request
if startRequestsImmediately { request.resume() }
return request
} catch {
return request(originalRequest, failedWith: error)
}
}
Copy the code
- The permission of this function is
open
Therefore, it can be usedSessionManager.default.request
To initiate the request, however, the argument is_ urlRequest: URLRequestConvertible
.URLRequestConvertible
The purpose of the agreement is toURLRequest
Perform custom conversions, therefore, after obtaining convertedURLRequest
After, need to useURLRequest
generatetask
In order to initiate a network requestAlamofire
In, but alwaysrequest
All of the functions at the beginning, by default, areDataRequest
Type, now you have itURLRequest
Not enough. We need to see if she can produce its counterparttask
.- In the function up here, it does
DataRequest.Requestable
.Requestable
It’s actually a structure, and he implemented itTaskConvertible
Protocol, so it can be usedURLRequest
Generate the correspondingtask
. Next we initializeDataRequest
“And then actually start making requests.
OK, here’s a quick look at what the request process looks like:
Request
Request dependencies
The Request class is used to send network requests, receive response, associate data returned by the server, and manage tasks.
Request is the base class of DataRequest, DownloadRequest, UploadRequest, and StreamRequest.
// MARK: Properties
/// The delegate for the underlying task.
open internal(set) var delegate: TaskDelegate {
get {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
return taskDelegate
}
set {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
taskDelegate = newValue
}
}
/// The underlying task.
open var task: URLSessionTask? { return delegate.task }
/// The session belonging to the underlying task.
public let session: URLSession
/// The request sent or to be sent to the server.
open var request: URLRequest? { returntask? .originalRequest }/// The response received from the server, if any.
open var response: HTTPURLResponse? { returntask? .responseas? HTTPURLResponse }
/// The number of times the request has been retried.
open internal(set) var retryCount: UInt = 0
let originalTask: TaskConvertible?
var startTime: CFAbsoluteTime?
var endTime: CFAbsoluteTime?
var validations: [() -> Void] = []
private var taskDelegate: TaskDelegate
private var taskDelegateLock = NSLock(a)Copy the code
These properties are simple and well understood. Most importantly, let’s focus on the Request initialization method:
// MARK: Lifecycle
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
self.session = session
switch requestTask {
case .data(let originalTask, let task):
taskDelegate = DataTaskDelegate(task: task)
self.originalTask = originalTask
case .download(let originalTask, let task):
taskDelegate = DownloadTaskDelegate(task: task)
self.originalTask = originalTask
case .upload(let originalTask, let task):
taskDelegate = UploadTaskDelegate(task: task)
self.originalTask = originalTask
case .stream(let originalTask, let task):
taskDelegate = TaskDelegate(task: task)
self.originalTask = originalTask
}
delegate.error = error
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent()}}Copy the code
As you can see, to initiate a Request, we only need one task.
- The first parameter
session
Used chiefly ofCustomStringConvertible
andCustomDebugStringConvertible
The implementation of both protocols takes specific data. - The second parameter is
requestTask
This is an enumeration type. Let’s see:
enum RequestTask {
case data(TaskConvertible? .URLSessionTask?).case download(TaskConvertible? .URLSessionTask?).case upload(TaskConvertible? .URLSessionTask?).case stream(TaskConvertible? .URLSessionTask?). }Copy the code
- We’re initializing
Request
When, just need to passrequestTask
With this enumerated value, we get two important pieces of data:Request
Type and correspondingtask
. The use of this technique greatly improves the quality of the code. RequestTask
There are two data bindings for enumerations and options,TaskConvertible
Represents the original object that is implementedTaskConvertible
Protocol, which can be converted totask
.URLSessionTask
Is the original object convertedtask
. Hence the possibility of an advanced usage method that can be customized for a class to implementTaskConvertible
Agreement, you can manipulatetask
The conversion process is very flexible.
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent()}Copy the code
This line of code adds an operation to the agent’s queue. The queue is first-in-first-out, but operations inside the queue can be suspended via isSuspended.
About Parameter Coding
First, Alamofire supports the following encoding formats:
URLEncoding
JSONEncoding
PropertyListEncoding
Copy the code
Take a look at this code:
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters else { return urlRequest }
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
guard let url = urlRequest.url else {
throw AFError.parameterEncodingFailed(reason: .missingURL)
}
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map{$0 + "&"}????"") + query(parameters)
urlComponents.percentEncodedQuery = percentEncodedQuery
urlRequest.url = urlComponents.url
}
} else {
if urlRequest.value(forHTTPHeaderField: "Content-Type") = =nil {
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)}return urlRequest
}
Copy the code
- Take out the request method, according to different request methods, parameter encoding is different,
.get
..head
..delete
The three methods are to concatenate the parameters directly after the URL, and the other methods are to concatenate the parameters directly after the URL.httpBody
)- Our request is to pass
ASCII
Coded, so to do percent coding, the first step is to percent code all routes currently requested- Finally, parameters facilitate coding, splicing out
Look at:
private func query(_ parameters: [String: Any]) -> String {
var components: [(String.String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\ [$0)=\ [$1)" }.joined(separator: "&")}Copy the code
- through
ASCII
I sort from small to large,queryComponents
In this method, the recursive parameters inside, and thekey
andvalue
Take out, and then carry on the percent code, put into the tuple to save, form the parameter pair- Outside adds the tuple to the array
map
mapping($0) = \ ($1)
- Type a separator between the elements
&
** Finally note: ** Ordinary methods are concatenated directly after the URL. For example, the POST method puts these encoded parameter pairs into the request body, including the Content-Type header.
conclusion
The more you read and write, the more you realize that the functions in Alamofire are designed so well that they don’t all cascade together at once. Due to my limited knowledge level, I would like to point out any mistakes. Finally, I recommend Cooci’s article regularly.