714cd28397969151db652ecd73a2d039
Alamofire(8)— 终章(网络监控&通知&下载器封装)

😊😊😊Alamofire专题目录,欢迎及时反馈交流 😊😊😊


Alamofire 目录直通车 --- 和谐学习,不急不躁!


非常高兴,这个 Alamofire 篇章马上也结束了!那么这也作为 Alamofire 的终章,给大家介绍整个 Alamofire 剩余的内容,以及下载器封装,最后总结一下!

一、NetworkReachabilityManager

这个类主要对 SystemConfiguration.framework 中的 SCNetworkReachability 相关的东西进行封装的,主要用来管理和监听网络状态的变化

1️⃣:首先我们来使用监听网络状态

let networkManager = NetworkReachabilityManager(host: "www.apple.com")

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    /// 网络监控
    networkManager!.listener = {
        status in
        var message = ""
        switch status {
        case .unknown:
            message = "未知网络,请检查..."
        case .notReachable:
            message = "无法连接网络,请检查..."
        case .reachable(.wwan):
            message = "蜂窝移动网络,注意节省流量..."
        case .reachable(.ethernetOrWiFi):
            message = "WIFI-网络,使劲造吧..."
        }
        print("***********\(message)*********")
        let alertVC = UIAlertController(title: "网络状况提示", message: message, preferredStyle: .alert)
        alertVC.addAction(UIAlertAction(title: "我知道了", style: .default, handler: nil))
        self.window?.rootViewController?.present(alertVC, animated: true, completion: nil)
    }
    networkManager!.startListening()

    return true
}
  • 用法非常简单,因为考虑到全局监听,一般都会写在didFinishLaunchingWithOptions
  • 创建 NetworkReachabilityManager 对象
  • 设置回调,通过回调的 status 来处理事务
  • 最后一定要记得开启监听(内部重点封装)

2️⃣:底层源码分析

1:我们首先来看看 NetworkReachabilityManager 的初始化

public convenience init?(host: String) {
    guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
    self.init(reachability: reachability)
}

private init(reachability: SCNetworkReachability) {
    self.reachability = reachability
    // 将前面的标志设置为无保留值,以表示未知状态
    self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30)
}
  • 底层源码里面调用 SCNetworkReachabilityCreateWithName 创建了 reachability 对象,这也是我们 SystemConfiguration 下非常非常重要的类!
  • 保存在这个 reachability 对象,方便后面持续使用
  • 将前面的标志设置为无保留值,以表示未知状态
  • 其中初始化方法中,也提供了默认创建,该实例监视地址 0.0.0.0
  • 可达性将 0.0.0.0地址 视为一个特殊的 token,它可以监视设备的一般路由状态,包括 IPv4和IPv6。

2:open var listener: Listener?

  • 这里也就是对外提供的状态回调闭包

3:networkManager!.startListening() 开启监听

这里也是这个内容点的重点所在

open func startListening() -> Bool {
    // 获取上下文结构信息
    var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
    context.info = Unmanaged.passUnretained(self).toOpaque()
    // 将客户端分配给目标,当目标的可达性发生更改时,目标将接收回调
    let callbackEnabled = SCNetworkReachabilitySetCallback(
        reachability,
        { (_, flags, info) in
            let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
            reachability.notifyListener(flags)
        },
        &context
    )
    // 在给定分派队列上为给定目标调度或取消调度回调
    let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
    // 异步执行状态,以及通知
    listenerQueue.async {
        guard let flags = self.flags else { return }
        self.notifyListener(flags)
    }
    return callbackEnabled && queueEnabled
}
  • 调用SCNetworkReachabilityContext的初始化,这个结构体包含用户指定的数据和回调函数.
  • Unmanaged.passUnretained(self).toOpaque()就是将非托管类引用转换为指针
  • SCNetworkReachabilitySetCallback:将客户端分配给目标,当目标的可达性发生更改时,目标将接收回调。(这也是只要我们的网络状态发生改变时,就会响应的原因)
  • 在给定分派队列上为给定目标调度或取消调度回调
  • 异步执行状态信息处理,并发出通知

4:self.notifyListener(flags) 我们看看状态处理以及回调

  • 调用了listener?(networkReachabilityStatusForFlags(flags)) 在回调的时候还内部处理了 flags
  • 这也是可以理解的,我们需要不是一个标志位,而是蜂窝网络、WIFI、无网络!
func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
    guard isNetworkReachable(with: flags) else { return .notReachable }

    var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)

#if os(iOS)
    if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
#endif
    return networkStatus
}
  • 通过 isNetworkReachable 判断有无网络
  • 通过 .reachable(.ethernetOrWiFi) 是否存在 WIFI 网络
  • iOS端 还增加了 .reachable(.wwan) 判断蜂窝网络

3️⃣:小结

网络监听处理,还是非常简单的!代码的思路也没有太恶心,就是通过 SCNetworkReachabilityRef 这个一个内部类去处理网络状态,然后通过对 flags 分情况处理,确定是无网络、还是WIFI、还是蜂窝

三、AFError错误处理

AFError中将错误定义成了五个大类型

// 当“URLConvertible”类型无法创建有效的“URL”时返回。
case invalidURL(url: URLConvertible)
// 当参数编码对象在编码过程中抛出错误时返回。
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
// 当多部分编码过程中的某个步骤失败时返回。
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
// 当“validate()”调用失败时返回。
case responseValidationFailed(reason: ResponseValidationFailureReason)
// 当响应序列化程序在序列化过程中遇到错误时返回。
case responseSerializationFailed(reason: ResponseSerializationFailureReason)

这里通过对枚举拓展了计算属性,来直接对错误类型进行 if判断,不用在 switch 一个一个判断了

extension AFError {
    // 返回AFError是否为无效URL错误
    public var isInvalidURLError: Bool {
        if case .invalidURL = self { return true }
        return false
    }
    // 返回AFError是否是参数编码错误。
    // 当“true”时,“underlyingError”属性将包含关联的值。
    public var isParameterEncodingError: Bool {
        if case .parameterEncodingFailed = self { return true }
        return false
    }
    // 返回AFError是否是多部分编码错误。
    // 当“true”时,“url”和“underlyingError”属性将包含相关的值。
    public var isMultipartEncodingError: Bool {
        if case .multipartEncodingFailed = self { return true }
        return false
    }
    // 返回“AFError”是否为响应验证错误。
    // 当“true”时,“acceptableContentTypes”、“responseContentType”和“responseCode”属性将包含相关的值。
    public var isResponseValidationError: Bool {
        if case .responseValidationFailed = self { return true }
        return false
    }
    // 返回“AFError”是否为响应序列化错误。
    // 当“true”时,“failedStringEncoding”和“underlyingError”属性将包含相关的值。
    public var isResponseSerializationError: Bool {
        if case .responseSerializationFailed = self { return true }
        return false
    }
}

小结

AFError 错误处理,这个类的代码也是非常简单的!大家自行阅读以下应该没有太多疑问,这里也就不花篇幅去啰嗦了!

四、Notifications & Validation

Notifications 核心重点

``Swift extension Notification.Name { /// Used as a namespace for allURLSessionTask` related notifications.

top Created with Sketch.