源码分析
(test工程+源码注释工程)https://www.dropbox.com/sh/v7poaxekotxlaf1/AADQb7NbrrfHXJLrBt1Q51vVa?dl=0
整体的流程图
创建Session用来发送请求,可以自定义创建,也可以直接使用AF单例,默认的Session来发送请求
外部调用request()方法,传入URLConvertible,创建Request对象
- 内部开始先用URLConvertible来创建URLRequestConvertible对象
- 用URLRequestConvertible创建Request(这个Request就是返回给外部的对象),保存到Session中,然后开始对Request进行预处理
- 先创建初始URLRequest,用预处理器对其进行预处理,在预处理前后都有使用方法来告知Request,流程变更,用来通知事件监听器,所以看EventMonitor协议中,有一大堆生命周期的回调事件。
- 预处理完成,返回Request对象
外部调用response系列方法,在这些方法中实现对响应的处理
- 内部会先创建对原始响应数据的处理闭包
- 先解析响应(还记录了解析所花的时间)
- 解析失败的重试
- 解析成功执行外部传入的完成回调
- 把这个闭包追加保存到Request的responseSerializers数组中
- 检测下当前Request是不是已经完成了,完成的话重新标记成执行中
- 检测下当前Request是否已经完成了全部响应的解析,如果是的话,就手动执行processNextResponseSerializer()方法开始继续执行responseSerializers数组中的解析闭包
- 检测下当前Request是否需要发送请求,需要的话,就调用resume()方法发送请求
AF.request为例,调用情况
简单的request请求,并对request后的AFDataResponse<String> 不做处理
func requestAction() {
AF.request("https://www.baidu.com").responseString { resp in
switch resp.result {
case let .success(str):
debugPrint("request success:\(str)")
case let .failure(err):
debugPrint("request fail:\(err)")
}
}
}
输入的是String 类型的"https://www.baidu.com" ,输入的网址string是作为URLConvertible协议类型传入的,该协议可以看作是URL类型的协议抽象,需要实现一个asURL 的方法,用来生成请求用的URL。
跳到第一个方法
使用传入的参数调用RequestEncodableConvertible()方法创建了一个convertible局部变量,类型只是个简单的结构体,实现了URLRequestConvertible协议类型
open func request(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
requestModifier: RequestModifier? = nil) -> DataRequest {
let convertible = RequestConvertible(url: convertible,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers,
requestModifier: requestModifier)
return request(convertible, interceptor: interceptor)
}
第二个方法
使用入参的convertible + 拦截器对象
Session所持有的rootQueue(回调执行队列),serializationQueue(响应解析队列),eventMonitor(事件监听器对象),以及把自己作为RequestDelegate对象,构建出了DataRequest对象
open func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> DataRequest {
let request = DataRequest(convertible: convertible,
underlyingQueue: rootQueue,
serializationQueue: serializationQueue,
eventMonitor: eventMonitor,
interceptor: interceptor,
delegate: self)
perform(request)
return request
}
第三个方法
使用了rootQueue队列执行,先判断request要是取消了的话,直接return
func perform(_ request: Request) {
rootQueue.async {
guard !request.isCancelled else { return }
self.activeRequests.insert(request)
self.requestQueue.async {
switch request {
case let r as UploadRequest: self.performUploadRequest(r)
case let r as DataRequest: self.performDataRequest(r)
case let r as DownloadRequest: self.performDownloadRequest(r)
case let r as DataStreamRequest: self.performDataStreamRequest(r)
default: fatalError("Attempted to perform unsupported Request subclass: \(type(of: request))")
}
}
}
}
调用了performDataRequest
case let r as DataRequest: self.performDataRequest(r)
第四个方法
检测是否在RequestQueue 执行,然后继续调用performSetupOperations() 方法继续处理
func performDataRequest(_ request: DataRequest) {
dispatchPrecondition(condition: .onQueue(requestQueue))
performSetupOperations(for: request, convertible: request.convertible)
}
第五个方法
- 构建原始URLRequest对象T
- 对T进行有效行校验
- 告知Request,T已经初始化完成,把T传给Request进行内部处理
- 如果Request中有请求适配器,逐一调用适配器对T进行处理
- 处理完成之后,告知Request已经适配完成
- 最终调用didCreateURLRequest()方法完成
func performSetupOperations(for request: Request,
convertible: URLRequestConvertible,
shouldCreateTask: @escaping () -> Bool = { true })
{
dispatchPrecondition(condition: .onQueue(requestQueue))
let initialRequest: URLRequest
do {
initialRequest = try convertible.asURLRequest()
try initialRequest.validate()
} catch {
rootQueue.async { request.didFailToCreateURLRequest(with: error.asAFError(or: .createURLRequestFailed(error: error))) }
return
}
rootQueue.async { request.didCreateInitialURLRequest(initialRequest) }
guard !request.isCancelled else { return }
guard let adapter = adapter(for: request) else {
guard shouldCreateTask() else { return }
rootQueue.async { self.didCreateURLRequest(initialRequest, for: request) }
return
}
adapter.adapt(initialRequest, for: self) { result in
do {
let adaptedRequest = try result.get()
try adaptedRequest.validate()
self.rootQueue.async { request.didAdaptInitialRequest(initialRequest, to: adaptedRequest) }
guard shouldCreateTask() else { return }
self.rootQueue.async { self.didCreateURLRequest(adaptedRequest, for: request) }
} catch {
self.rootQueue.async { request.didFailToAdaptURLRequest(initialRequest, withError: .requestAdaptationFailed(error: error)) }
}
}
}
因为没有拦截器所以调用了didCreateURLRequest(initialRequest, for: request)
第六个方法
func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) {
dispatchPrecondition(condition: .onQueue(rootQueue))
request.didCreateURLRequest(urlRequest)
guard !request.isCancelled else { return }
let task = request.task(for: urlRequest, using: session)
requestTaskMap[request] = task
request.didCreateTask(task)
updateStatesForTask(task, request: request)
}
request.task(for: urlRequest, using: session)
创建了URLSessionTask ,这就是真正用来发送请求的task,然后把这个task存在了Session的requestTaskMap字典中,key是request,这样可以在任意时候根据request找到它所对应的task。开始发送请求。
AF对请求的处理完成
Response (接受响应)处理
Request() -> response()
因为写的是responseString
第一步
唯一需要必须传入的参数就是完成的回调闭包
completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self
@discardableResult
public func responseString(queue: DispatchQueue = .main,
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
encoding: String.Encoding? = nil,
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods,
completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self {
response(queue: queue,
responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
encoding: encoding,
emptyResponseCodes: emptyResponseCodes,
emptyRequestMethods: emptyRequestMethods),
completionHandler: completionHandler)
}
执行的是response(
第二步
对数据进行一大堆的逻辑、解析处理
@discardableResult
public func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void)
-> Self {
appendResponseSerializer {
let start = ProcessInfo.processInfo.systemUptime
let result: AFResult<Serializer.SerializedObject> = Result {
try responseSerializer.serialize(request: self.request,
response: self.response,
data: self.data,
error: self.error)
}.mapError { error in
error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error)))
}
let end = ProcessInfo.processInfo.systemUptime
self.underlyingQueue.async {
let response = DataResponse(request: self.request,
response: self.response,
data: self.data,
metrics: self.metrics,
serializationDuration: end - start,
result: result)
self.eventMonitor?.request(self, didParseResponse: response)
guard let serializerError = result.failure, let delegate = self.delegate else {
self.responseSerializerDidComplete { queue.async { completionHandler(response) } }
return
}
delegate.retryResult(for: self, dueTo: serializerError) { retryResult in
var didComplete: (() -> Void)?
defer {
if let didComplete = didComplete {
self.responseSerializerDidComplete { queue.async { didComplete() } }
}
}
switch retryResult {
case .doNotRetry:
didComplete = { completionHandler(response) }
case let .doNotRetryWithError(retryError):
let result: AFResult<Serializer.SerializedObject> = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError"))
let response = DataResponse(request: self.request,
response: self.response,
data: self.data,
metrics: self.metrics,
serializationDuration: end - start,
result: result)
didComplete = { completionHandler(response) }
case .retry, .retryWithDelay:
delegate.retryRequest(self, withDelay: retryResult.delay)
}
}
}
}
return self
}
这一大堆的处理,其实就是appendResponseSerializer() 的方法接收一个很多数据处理的闭包参数
最后返回的是 return self
responseString 方法返回的类型是self ,可以连续链式的调用responseString
第三步
解析数据
根据Request当前状态来延迟调用
func appendResponseSerializer(_ closure: @escaping () -> Void) {
$mutableState.write { mutableState in
mutableState.responseSerializers.append(closure)
if mutableState.state == .finished {
mutableState.state = .resumed
}
if mutableState.responseSerializerProcessingFinished {
underlyingQueue.async { self.processNextResponseSerializer() }
}
if mutableState.state.canTransitionTo(.resumed) {
underlyingQueue.async { if self.delegate?.startImmediately == true { self.resume() } }
}
}
}
- 如果当前Request被标记finished,表示这个Request已经请求完成了,现在是正在对他再一次执行response,那就把request标记成**resumed,表示正在执 ** ,后续逻辑就不会发送请求,而是直接调用入解析回调。
- 检测下responseSerializerProcessingFinished字段,该字段是用来标记当前正在执行的某个解析回调是否完成了,如果完成了,就手动调用一下processNextResponseSerializer()方法来执行下一个回调闭包。如果尚未完成,就不需要手动调用。因为解析完成的逻辑会自己检测执行下一个解析。递归的来着。。。
- 最后,需要判断下,当前request能否变成resume,这个操作是因为:只有initialized,suspended,这两个状态,可以变成resume,代表需要对task调用resume方法来发送请求/继续请求。而且,必须request的startImmediately设置为true才会自动调用resume()方法,否则,需要手动调用resume()才能发送请求。
自定义AlamofireManager
实例化session
class AlamofireManager {
private static var session: Session!
class func shareInstance() -> Session {
if session == nil {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 50
session = Session(configuration: configuration)
}
return session
}
}
前提准备BaseBean
Swift的反射功能,的iOS Runtime不一样,Swift的反射用了另一套API,实现机制也完全不一样
反射是一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。
-
- 动态地创建对象和属性,
- 动态地获取一个类里面所有的属性,方法。
- 获取它的父类,或者是实现了什么样的接口(协议)
- 获取这些类和属性的访问限制(Public 或者 Private)
- 动态地获取运行中对象的属性值,同时也能给它赋值(KVC)
- 动态调用实例方法或者类方法
- 动态的给类添加方法或者属性,还可以交换方法(只限于Objective-C)
class BaseBean: NSObject,NSCoding {
required override init() {
}
required init?(coder: NSCoder) {
super.init()
let mir = Mirror(reflecting: self)
for item in mir.children {
if let label = item.label, let cachedValue = coder.decodeObject(forKey: label) {
if let stringValue = cachedValue as? String {
self.setValue(stringValue, forKey: label)
}else if let intValue = cachedValue as? Int {
self.setValue(intValue, forKey: label)
}else {
}
}
}
}
func encode(with coder: NSCoder) {
let mir = Mirror(reflecting: self)
for item in mir.children {
if let label = item.label, let labelValue = self.value(forKey: label) {
coder.encode(labelValue, forKey: label)
}
}
}
}
如果model使用NSObject类型,而不是Codable类型,就需要JSON转NSObject类型的model
为更方便,引入了使用swiftyJSON
https://github.com/SwiftyJSON/SwiftyJSON
import SwiftyJSON
class DataArchiveUtils<D: NSObject>: NSObject {
class func getDatas(json: JSON, dataKey: String) -> [D]? {
let dataData = json[dataKey].description.data(using: .utf8)
let data = Reflect<D>.mapObjects(data: dataData)
return data
}
class func getData(json: JSON, dataKey: String) -> D? {
let dataData = json[dataKey].description.data(using: .utf8)
let data = Reflect<D>.mapObject(data: dataData)
return data
}
}
Any类型的话也可以使用 TTReflect
https://github.com/TifaTsubasa/TTReflect
把有用的抽出来
class Reflect<M: NSObject> {
class func mapObject(json: Any?) -> M {
guard let json = json else { return M() }
guard json is NSDictionary || json is [String: AnyObject] else {
return M()
}
let model = M()
model.mapProperty(json)
return model
}
class func mapObjects(json: Any?) -> [M] {
guard let json = json else {
return [M]()
}
guard json is NSArray || json is [AnyObject] else {
return [M]()
}
guard let arrayJson = json as? [AnyObject] else {
return [M]()
}
let models: [M] = arrayJson.map {
return Reflect<M>.mapObject(json: $0)
}
return models
}
class func mapObject(data: Data?) -> M {
guard let data = data else { return M() }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
return Reflect<M>.mapObject(json: json as AnyObject?)
} catch {
debugPrint("Serializat json error: \(error)")
}
return M()
}
class func mapObjects(data: Data?) -> [M] {
guard let data = data else { return [M]() }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
return Reflect<M>.mapObjects(json: json as AnyObject?)
} catch {
debugPrint("Serializat json error: \(error)")
}
return [M]()
}
class func mapObject(_ plistName: String?) -> M {
let plistPath = Bundle.main.path(forResource: plistName, ofType: "plist")
guard let path = plistPath else {
debugPrint("Reflect error: Error plist name")
return M()
}
let json = NSDictionary(contentsOfFile: path)
return Reflect<M>.mapObject(json: json)
}
class func mapObjects(_ plistName: String?) -> [M] {
let plistPath = Bundle.main.path(forResource: plistName, ofType: "plist")
guard let path = plistPath else {
debugPrint("Reflect error: Error plist name")
return [M]()
}
let json = NSArray(contentsOfFile: path)
return Reflect<M>.mapObjects(json: json)
}
}
@objc
public protocol TTReflectProtocol {
@objc optional func setupMappingReplaceProperty() -> [String: String]
@objc optional func setupMappingObjectClass() -> [String: AnyClass]
@objc optional func setupMappingElementClass() -> [String: AnyClass]
@objc optional func setupMappingIgnorePropertyNames() -> [String]
}
extension NSObject: TTReflectProtocol {
fileprivate func mapProperty(_ json: Any) {
if json is NSNull { return }
let replacePropertyName = self.getMappingReplaceProperty()
let ignorePropertyNames = self.getMappingIgnorePropertyNames()
let mappingObjectClass = self.getMappingObjectClass()
let mappingElementClass = self.getMappingElementClass()
let keys = ergodicObjectKeys()
for key in keys {
let jsonKey = replacePropertyName[key] ?? key
let jsonValue = (json as AnyObject).value(forKey: jsonKey)
guard !ignorePropertyNames.contains(key) else {continue}
guard let value = jsonValue else {continue}
if value is NSNull {
debugPrint("Reflect error: The key \(jsonKey) value is \(value)")
continue
}
setPropertyValue(value as AnyObject, forKey: key)
if let trackObject = mapSubObject(key, jsonKey: jsonKey, mappingObjectClass: mappingObjectClass, value: value as AnyObject){
setValue(trackObject, forKey: key)
}
if let trackObjects = mapSubObjectArray(key, jsonKey: jsonKey, mappingElementClass: mappingElementClass, value: value as AnyObject) {
setValue(trackObjects, forKey: key)
}
}
}
fileprivate func mapSubObject(_ key: String, jsonKey: String, mappingObjectClass: [String: AnyClass], value: AnyObject) -> AnyObject? {
guard mappingObjectClass.keys.contains(jsonKey) else {return nil}
guard let objClass = mappingObjectClass[jsonKey] as? NSObject.Type else {
fatalError("Reflect error: Sub-model is not a subclass of NSObject")
}
let model = objClass.init()
guard value is NSDictionary || value is [String: AnyObject] else {
debugPrint("Reflect error: Error key: \(key) -- mapping sub-model without a dictionary json")
return nil
}
model.mapProperty(value)
return model
}
fileprivate func mapSubObjectArray(_ key: String, jsonKey: String, mappingElementClass: [String: AnyClass], value: AnyObject) -> AnyObject? {
guard mappingElementClass.keys.contains(jsonKey) else {return nil}
guard let objClass = mappingElementClass[jsonKey] as? NSObject.Type else {
fatalError("Reflect error: Sub-model is not a subclass of NSObject")
}
guard let subArrayJson = value as? [AnyObject] else {
debugPrint("Reflect error: Error key: \(key) -- mapping sub-model array without a array json")
return nil
}
let submodelArray: [NSObject] = subArrayJson.map {
let submodel = objClass.init()
if $0 is NSDictionary || $0 is [String: AnyObject] {
submodel.mapProperty($0)
} else {
debugPrint("Reflect error: Error key: \(key) -- mapping sub-model array element without a dictionary json")
}
return submodel
}
return submodelArray as AnyObject
}
fileprivate func setPropertyValue(_ value: AnyObject?, forKey key: String) {
var transFlag: Bool?
var transValue: AnyObject?
let valueTuple = (self.value(forKey: key), value)
switch valueTuple {
case let (objValue as NSNumber, jsonValue as NSString):
transFlag = false
if objValue.isBool {
if jsonValue == "true" {
transValue = true as AnyObject?
transFlag = true
}
} else {
if let res = NumberFormatter().number(from: jsonValue as String) {
transValue = res
transFlag = true
}
}
case let (_ as NSString, jsonValue as NSNumber):
transValue = "\(jsonValue)" as AnyObject?
transFlag = true
default:
setValue(value, forKey: key)
}
if let transFlag = transFlag {
if transFlag {
setValue(transValue, forKey: key)
} else {
}
}
}
func ergodicObjectKeys() -> [String] {
var keys = [String]()
let mirror = Mirror(reflecting: self)
if let objectKeys = reflectObjectKeys(mirror) {
keys = objectKeys
}
return keys
}
func reflectObjectKeys(_ mirror: Mirror?) -> [String]? {
guard let mirror = mirror else { return nil }
var keys = mirror.children.compactMap {$0.label}
if mirror.superclassMirror?.subjectType != NSObject.self {
if let subKeys = reflectObjectKeys(mirror.superclassMirror) {
keys.append(contentsOf: subKeys)
}
}
return keys
}
func getObjectKeys(_ cls: AnyClass) -> [String] {
var keys = [String]()
var propNum: UInt32 = 0
let propList = class_copyPropertyList(cls, &propNum)
for index in 0..<numericCast(propNum) {
let prop: objc_property_t = propList![index]
keys.append(String(validatingUTF8: property_getName(prop))!)
}
if class_getSuperclass(cls) != NSObject.self {
if let k = class_getSuperclass(cls) {
keys.append(contentsOf: getObjectKeys(k))
}
}
return keys
}
fileprivate func getMappingReplaceProperty() -> [String: String] {
var replacePropertyName = [String: String]()
return getProtocolSetting(&replacePropertyName, aSelector: #selector(TTReflectProtocol.setupMappingReplaceProperty))
}
fileprivate func getMappingIgnorePropertyNames() -> [String] {
var ignorePropertyNames = [String]()
return getProtocolSetting(&ignorePropertyNames, aSelector: #selector(TTReflectProtocol.setupMappingIgnorePropertyNames))
}
fileprivate func getMappingObjectClass() -> [String: AnyClass] {
var mappingObjectClass = [String: AnyClass]()
return getProtocolSetting(&mappingObjectClass, aSelector: #selector(TTReflectProtocol.setupMappingObjectClass))
}
fileprivate func getMappingElementClass() -> [String: AnyClass] {
var mappingElementClass = [String: AnyClass]()
return getProtocolSetting(&mappingElementClass, aSelector: #selector(TTReflectProtocol.setupMappingElementClass))
}
fileprivate func getProtocolSetting<T>(_ emptySetting: inout T, aSelector: Selector) -> T {
guard self.responds(to: aSelector) else {return emptySetting}
let res = self.perform(aSelector)
emptySetting = res?.takeUnretainedValue() as! T
return emptySetting
}
}
还有一个就是JSONDecoder,Data类型转换model,model是继承codable
let decoder = JSONDecoder()
let model = try? decoder.decode(NetModel.self, from: jsonData)
if model != nil {
success(model!)
}
自定义request RequestClient<T: BaseBean>: NSObject
T:继承BaseBean,主要用到是callback 第一个参数的请求结果 Reflect<T>.mapObject(data: response.data) ,callback的第二个为请求是否成功,nil为不成功判断,第三个为原始数据
class RequestClient<T: BaseBean>: NSObject {
class func request(method: HTTPMethod, url: String, params: [String: Any], callback:((T?, String?,String?) -> Void)?) {
let newURL = "https://user.52jtg.com/" + url
let p = params
debugPrint(newURL)
debugPrint(p)
let encode = URLEncoding(destination: method == HTTPMethod.post ? .httpBody: .queryString)
AlamofireManager.shareInstance().request(newURL, method: method, parameters: p, encoding: encode, headers: nil).responseString { response in
if response.result.isSuccess {
do {
let json = try JSON(data: response.data!)
debugPrint(json)
if json["successCode"] == "true" || json["successCode"] == true {
callback?(Reflect<T>.mapObject(data: response.data),nil,response.value)
}else {
let message: String = json["message"].stringValue
callback?(nil, message, response.value)
}
} catch (let error) {
let str = String(data: response.data!, encoding: String.Encoding.utf8)
debugPrint(str ?? "")
debugPrint(error.localizedDescription)
callback?(nil,error.localizedDescription,response.value)
}
}else {
if let error = response.error?.localizedDescription {
callback?(nil, "请求失败: \(error)",response.value)
}else {
callback?(nil, "请求失败:未知错误",response.value)
}
}
}
}
上传图片
class func upLoad(imgs: [UIImage],url: String, params: [String: Any],progressClosure: ((Double) -> Void)? = nil, callback: ((T?, String?, String?) -> Void)?) {
写了比较少用到的请求json文件,该JSON为swiftyJSON类
class RequestJsonData {
class func getJSON(url: String, params: [String : String], method: HTTPMethod = .post,completed:((JSON)-> Void)?,failed:((String?)->Void)?) {
运用到实例当中,我用家长端的一个请求数据为例子
https://eolinker.52jtg.com/#/home/project/inside/api/detail?groupID=-1&apiID=1866&projectName=今托管-家长端&projectID=14
class func requestModel(success: @escaping(NetModel)-> Void,
failue: @escaping Failure) {
var params: [String: Any] = [:]
params = ["keyword":"",
"lat":22.99430230034722,
"lon": 113.3336423068576,
"page": 1,
"pageSize":20,
"sortName": "DISTANCE"]
let urlTest = "search/preferred-shop/index-home-keyword-shop/?requestType=json"
RequestClient<NetBean>.request(method: .get, url: urlTest, params: params) { bean, error,result in
if error == nil || error == "" {
if let jsonData = result?.data(using: .utf8) {
let beanModel = bean
print(beanModel?.result)
print(jsonData)
let decoder = JSONDecoder()
let model = try? decoder.decode(NetModel.self, from: jsonData)
if model != nil {
success(model!)
}
}
}else {
print(error ?? "错误信息未知")
}
}
}
可以选择两种形式返回,一个是success闭包返回继承codable的model数据返回,另一个也可以使用继承BaseBean(NSObject)的model数据返回。
func requestAction2() {
NetTool.requestModel { model in
self.model = model
print(model)
} failue: { error in
print(error.localizedDescription)
}
}
如果不是默认格式的数据,也可以自己写一个出来的alamofire请求
func shareInstance() {
var params: [String: Any] = [:]
params = ["keyword":"",
"lat":22.99430230034722,
"lon":113.3336423068576,
"page": 1,
"pageSize":20,
"sortName": "DISTANCE"]
let urlTest = "https://user.52jtg.com/search/preferred-shop/index-home-keyword-shop/?requestType=json"
AlamofireManager.shareInstance().request(urlTest, method: .get, parameters: params).responseString { response in
if response.result.isSuccess {
var json: JSON?
if let dataStr = response.data {
json = JSON(dataStr)
}
if json?["successCode"] == true || json?["successCode"] == "true" {
if let result = json?["result"] {
self.modelList = DataArchiveUtils<NetListBean>.getDatas(json: result, dataKey: "list") ?? []
print(self.modelList)
}
}
}
}
}
取消当前的请求(用URL来判断)
class func cancelRequest(path: String) {
if session != nil {
let urlpath = "https://user.52jtg.com/" + path
AF.session.getAllTasks { sessionTask in
sessionTask.forEach { task in
if task.currentRequest?.url?.path == urlpath {
task.cancel()
}
}
}
}
}
设置cookies
class func setCookies(properties: [HTTPCookiePropertyKey : String]) {
if let cookie = HTTPCookie(properties: properties) {
AF.session.configuration.httpCookieStorage?.setCookie(cookie)
}
}
let cookieProps = [
HTTPCookiePropertyKey.domain: "##put your domain here##",
HTTPCookiePropertyKey.path: "/123",
HTTPCookiePropertyKey.name: "fffff",
HTTPCookiePropertyKey.value: "21312"
]
AlamofireManager.setCookies(properties: cookieProps)
删除cookies
let cstorage = HTTPCookieStorage.shared
if let cookies = cstorage.cookies {
for cookie in cookies {
cstorage.deleteCookie(cookie)
}
}
下载
func downLoadFile(file: String, fileName: String) {
var fileStr = file
if fileStr.isIncludeChinese() {
fileStr = file.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
}
var url = URL(string: fileStr)
let destination: DownloadRequest.Destination = { _, response in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("file1/\(fileName)")
url = fileURL
return (fileURL,[.removePreviousFile,.createIntermediateDirectories])
}
AlamofireManager.shareInstance().download(fileStr,to: destination).downloadProgress { progress in
print(progress)
}.responseData { response in
if let url = url {
self.docController = UIDocumentInteractionController(url: url)
self.docController.presentOptionsMenu(from: self.view.bounds,in: self.view ,animated: true)
}
}
}
多表单
核心语句
AlamofireManager.shareInstance().upload(multipartFormData: { multipartFormData in
for imageData in imageDataArray {
let timeStamp = Int(Date().timeIntervalSince1970)
multipartFormData.append(imageData, withName: "files",fileName: "\(timeStamp)_\(imageData).jpeg",mimeType: "image/jpeg")
}
}, to: url).responseString
multipartFormData
回车换行字符串,内部做好数据的格式
enum EncodingCharacters {
static let crlf = "\r\n"
}
enum BoundaryGenerator {
enum BoundaryType {
case initial
case encapsulated
case final
}
static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
let boundaryText: String
switch boundaryType {
case .initial:
boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
case .encapsulated:
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
case .final:
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
}
return Data(boundaryText.utf8)
}
只需要调用multipartFormData.append(imageData, withName: "files",fileName: "\(timeStamp)_\(imageData).jpeg",mimeType: "image/jpeg") 就能转换为标准格式的表单头(contents)
public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
bodyParts.append(bodyPart)
}
执行uploadrequest的build 数据整合并判断可以upload
func build() throws -> UploadRequest.Uploadable {
let uploadable: UploadRequest.Uploadable
if multipartFormData.contentLength < encodingMemoryThreshold {
let data = try multipartFormData.encode()
uploadable = .data(data)
} else {
let tempDirectoryURL = fileManager.temporaryDirectory
let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
let fileName = UUID().uuidString
let fileURL = directoryURL.appendingPathComponent(fileName)
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
do {
try multipartFormData.writeEncodedData(to: fileURL)
} catch {
try? fileManager.removeItem(at: fileURL)
throw error
}
uploadable = .file(fileURL, shouldRemove: true)
}
return uploadable
}
-
数据就是通过,格式容器初始化 -
然后用户传递需要上传的数据,填充进去 -
包装成一个个 bodyPart,通过一个结合容器收集bodyParts -
全部包装完毕,遍历 bodyParts 进行详细编码 -
首先拼接分隔符,拼接固定格式头信息,然后通过 stream 读取具体! 值, -
通过data 传进,调用 URLSession 响应的方法, -
通过 SessionDelegate 接受上传代理 - 最后下发给UploadTaskDelegate 最终返回上传情况
后面跟Request原理差不多 也是执行perform(request)
.responseString { response in ///响应结果
HTTPS
1、认证服务器:第一阶段服务器会提供经CA机构认证颁发的服务器证书,如果认证该服务器证书的CA机构,存在于浏览器的受信任CA机构列表中,并且服务器证书中的信息与当前正在访问的网站(域名等)一致,那么浏览器就认为服务端是可信的,并从服务器证书中取得服务器公钥,用于后续流程
2、协商会话密钥。客户端在认证完服务器,获得服务器的公钥之后,利用该公钥与服务器进行加密通信,协商出两个会话密钥,分别是用于加密客户端往服务端发送数据的客户端会话密钥,用于加密服务端往客户端发送数据的服务端会话密钥。
3、加密通讯
默认情况下,Alamofire会收到与URLSession相同的自动TLS证书和证书链,同默认情况下,Alamofire会自动帮我们处理身份验证
证书编写: 可以多个证书
public func trustSession() -> Session {
let certificates = AlamofireManager.certitificates()
let keys = AlamofireManager.publicKeys()
let evaluators: [String: ServerTrustEvaluating] = [
"cert.xxxxx.com": PinnedCertificatesTrustEvaluator(certificates: certificates),
"keys.xxxxx.com": PublicKeysTrustEvaluator(keys:keys),
"any": ByIpTrustEvaluator()]
let manager = ServerTrustManager(evaluators: evaluators)
return Session(serverTrustManager: manager)
}
public static func certitificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
var certificates: [SecCertificate] = []
let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
}.joined())
for path in paths {
if let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
let certificate = SecCertificateCreateWithData(nil, certificateData) {
certificates.append(certificate)
}
}
return certificates
}
public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
var publicKeys: [SecKey] = []
for certificate in certitificates(in: bundle) {
if let publicKey = SecCertificateCopyKey(certificate) {
publicKeys.append(publicKey)
}
}
return publicKeys
}
验证方法
try evaluator.evaluate(trust, forHost: host)
interceptor 适配器、拦截器、重试器
继承RequestInterceptor
修改request头
func adapt(
_ urlRequest: URLRequest,
for session: Session,
completion: @escaping (Result<URLRequest, Error>) -> Void
) {
var urlRequest = urlRequest
if let token = TokenManager.shared.fetchAccessToken() {
urlRequest.setValue("token \(token)", forHTTPHeaderField: "Authorization")
}
completion(.success(urlRequest))
}
修改重试次数
let retryLimit = 5
let retryDelay: TimeInterval = 10
func retry(
_ request: Request,
for session: Session,
dueTo error: Error,
completion: @escaping (RetryResult) -> Void
) {
let response = request.task?.response as? HTTPURLResponse
if
let statusCode = response?.statusCode,
(500...599).contains(statusCode),
request.retryCount < retryLimit {
completion(.retryWithDelay(retryDelay))
} else {
return completion(.doNotRetry)
}
}
eventMonitors 通知事件监测器
继承 EventMonitor
请求完成
func requestDidFinish(_ request: Request) {
print(request.description)
}
DataRequest成功创建序列化的DataResponse时回调
func request<Value>(
_ request: DataRequest,
didParseResponse response: DataResponse<Value, AFError>
) {
guard let data = response.data else {
return
}
if let json = try? JSONSerialization
.jsonObject(with: data, options: .mutableContainers) {
print(json)
}
}
cachedResponseHandler
缓存数据
class func GitInstance() -> Session {
let interceptor = GitRequestInterceptor()
let configuration = URLSessionConfiguration.default
let networkLogger = GitNetworkLogger()
configuration.timeoutIntervalForRequest = 50
let responseCacher = ResponseCacher(behavior: .modify { _, response in
let userInfo = ["date": Date()]
return CachedURLResponse(
response: response.response,
data: response.data,
userInfo: userInfo,
storagePolicy: .allowed)
})
return Session(
configuration: configuration,
interceptor: interceptor,
cachedResponseHandler: responseCacher,
eventMonitors: [networkLogger])
}
handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler)
如果是缓存数据.cache
直接返回 completion(response)
|