This is the 12th day of my participation in the Gwen Challenge.More article challenges

Technical selection of Swift network request library

In Swift, if you want to make network requests, you will basically choose to use Alamofire. As the Swift version of AFNetworking, which has been famous since OC era, it not only inherits the advantages of AFN, but also sublimates it through the characteristics of Swift language.

It’s hard to imagine the alternative to using Alamofire for network requests in Swift.

Moya? At the end of the day, it’s also based on the Alamofire package.

Or, in the case of less complex network requests, use URLSession?

There are other network request libraries, but they pale in size compared to Alamofire.

Alamofire5

With version iterations, currently Alamofire has followed Swift’s footsteps into the 5.0 era, with the latest stable version 5.4.3.

Most of my time with Alamofire was in the 4.0 era, and version 4.8.2 was integrated in many projects.

According to the Alamofire5 source code I read, 5.0 is quite a big iteration, recombing many classes, rewriting many classes, the feeling is a whole circle of feeling.

Those interested can check out the updated documentation for Alamofire5.

The development document of Alamofire is also very detailed and complete. To learn and use Alamofire well, try to read through the document and see the code is a good choice — official document.

For those who feel that reading English is difficult, I also offer the resources of Alamofire source code interpretation and Chinese annotation inside the Nuggets – Alamofire source code learning directory collection

Talk about two features and one do’s and don ‘ts of using Alamofire5

Consider writing Alamofire article a big push, source analysis is also very much, talk about some I think practical point.

Feature 1: Decodable Responses

Codabl has been available for JSON Model conversion since Swift4 was launched. Codable is easy to use and is handy for template generation using dedicated tools.

Unfortunately, Alamofire4 does not support Codable for callbacks. So when I was writing, I had to do this:

Struct Item: Codable {var topicOrder: Int? var id: Int? var topicDesc: String? var topicTittle: String? var upTime: String? var topicImageUrl: String? var topicStatus: Int? } Alamofire. Request (" your request A url ", method: .get).response {response in guard let data = response.data else {return} /// Data is converted to guard let model through JSONDecoder  = try? JSONDecoder().decode(Item.self, from: data) else { return } print(model) }Copy the code

That’s right, because the 4.0 era didn’t reference the <T: Codabel> generic to the Response method, so you had to do it every time the write interface fetched data. It’s possible to encapsulate methods, but overall it’s not very friendly.

So I had to masturbate a classification of Alamofire and even write a Podspecs to upload to my private library just for this:

import Foundation import Alamofire // MARK: -codable extension Request {/// return Result type for Codable extension: /// /// -parameters: /// -response: // -data: /// - error: AFError /// - keyPath: model keyPath can parse deep JSON data /// - Returns: Result<T> public static func serializeResponseCodable<T: Codable>(response: HTTPURLResponse? , data: Data? , error: Error? , keyPath: String?) -> Result<T> { if let error = error { return .failure(error) } if let response = response, emptyDataStatusCodes.contains(response.statusCode) { do { let value = try JSONDecoder().decode(T.self, from: Data()) return .success(value) } catch { return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))) } } guard let validData = data else { return .failure(AFError.responseSerializationFailed(reason: .inputDataNil)) } if let keyPath = keyPath, ! keyPath.isEmpty { var keyPaths = keyPath.components(separatedBy: "/") return keyPathForCodable(keyPaths: &keyPaths, data: validData) }else { do { let value = try JSONDecoder().decode(T.self, from: validData) return .success(value) } catch { return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))}}} /// /// - Parameters: // -keypaths: /// - Returns: Result<T> private static func keyPathForCodable<T: Codable>(keyPaths: inout [String], data: Data) -> Result<T> { if let firstKeyPath = keyPaths.first, keyPaths.count > 1 { keyPaths.remove(at: 0) if let JSONObject = try? JSONSerialization.jsonObject(with: data, options: .allowFragments), let keyPathJSONObject = (JSONObject as AnyObject?)?.value(forKeyPath: firstKeyPath), let keyPathData = try? JSONSerialization.data(withJSONObject: keyPathJSONObject) { return keyPathForCodable(keyPaths: &keyPaths, data: keyPathData) } }else if let lastKeyPath = keyPaths.last, keyPaths.count == 1 { keyPaths.remove(at: 0) if let JSONObject = try? JSONSerialization.jsonObject(with: data, options: .allowFragments), let keyPathJSONObject = (JSONObject as AnyObject?)?.value(forKeyPath: lastKeyPath), let keyPathData = try? JSONSerialization.data(withJSONObject: keyPathJSONObject) { do { let value = try JSONDecoder().decode(T.self, from: keyPathData) return .success(value) } catch { return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error))) } } } return .failure(AFError.responseSerializationFailed(reason: .inputDatanil))}} Extension DataRequest {/// Create a response Serializer that complies with Codable protocol /// /// -parameter keyPath: Key path / / / - Returns: observe the response of Codable agreement serializer public static func codableResponseSerializer < T: Codable > (keyPath: String?) -> DataResponseSerializer<T> { return DataResponseSerializer { _, response, data, error in return Request.serializeResponseCodable(response: response, data: data, error: error, keyPath: KeyPath)}} /// add a handle to the request completion /// /// -parameters: /// -queue: callback thread /// -keypath: key value path /// -CompletionHandler: handle /// - Returns: DataRequest @discardableResult public func responseCodable<T: Codable>( queue: DispatchQueue? = nil, keyPath: String? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.codableResponseSerializer(keyPath: keyPath), completionHandler: completionHandler ) } }Copy the code

After passing this classification, I can happily return to the model:

Alamofire. Request (" your Api", method:.get).responsecodable {(response: DataResponse<Item>) in guard let value = response.value else { return } print(value) }Copy the code

Nothing too special, just for simplicity.

All this has changed in Alamofire5, because Alamofire has officially added this function in its source code, and can use Result to call back data, so there is no need for any classification can be happy JSON model:

AF.request(baseURL,
           method: .post)
           .responseDecodable { (response: AFDataResponse<Item>) in
               switch response.result {
               case .success(let model):
                   break
               case .failure(let error):
                   break
               }
           }
Copy the code

Isn’t it much simpler?

Feature 2: Encodable Parameters

Before Alamofire5, we used dictionaries for Parameters of web requests.

This is also defined in Alamofire:

Public TypeAlias Parameters = [String: Any] public typeAlias Parameters = [String: Any] public func request(_ url: URLConvertible, method: HTTPMethod = .get, parameters: Parameters? = nil, encoding: ParameterEncoding = URLEncoding.default, headers: HTTPHeaders? = nil) -> DataRequestCopy the code

After Alamofire5, however, it is possible to pass a Codable protocol-based model as a request parameter!

Personally, I think this feature is long overdue! Before Alamofire didn’t have this function, I would also use the model to follow the dictionary with a lot of special parameters and then make the request after converting it into a dictionary.

Request parameters are transferred to the model, in the request to unify the model into a dictionary and then request, reduce the hard coding errors caused by handwritten dictionary! Ordinary and important.

Api:

open func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                             method: HTTPMethod = .get,
                                             parameters: Parameters? = nil,
                                             encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                             headers: HTTPHeaders? = nil,
                                             interceptor: RequestInterceptor? = nil,
                                             requestModifier: RequestModifier? = nil) -> DataRequest
Copy the code

Example:

Struct VinEntity: Codable {var vin: String} struct VinEntity: Codable {var vin: String} let vin = VinEntity(vin: AF. Request (baseURL, method:. Post, parameters: vin, encoder: JSONParameterEncoder.default) .responseDecodable { (response: AFDataResponse<Token>) in switch response.result { case .success(let model): break case .failure(let error): break } }Copy the code

Note: encoding: ParameterEncoding and encoder: ParameterEncoder

The encoding argument is the encoding of the request argument for which the input parameter is a dictionary. The default is urlencoding.default.

Encoder parameters for the ginseng is keep Codable agreement request parameter coding way, the default mode is URLEncodedFormParameterEncoder. The default.

If you can’t get the correct data sent to you from the background, consider changing this parameter.

encoding: JSONEncoding.default

encoder: JSONParameterEncoder.default

Copy the code

Tomorrow to continue

After Alamofire is based on its packaging layer Moya, Dragon Boat Festival is more difficult, everyone come on!