Как отследить состояние сетевого соединения в Swift? Привет, нативная реализация, пока, Reachability

от автора

Отслеживание сетевого соединения в Swift
Отслеживание сетевого соединения в Swift

Рассмотрим нативное решение для мониторинга сетевого подключения на iOS с помощью Swift 5 и использования Network Link Conditioner.

Примечание

Если вы найдёте статью интересной, то в этом канале я пишу об iOS-разработке.

Большинство реализаций, которые вы можете найти для мониторинга сетевого подключения вашего iOS-устройства, основаны на использовании сторонних зависимостей, таких как Reachability, NetworkReachabilityManager в Alamofire, или же утилит, которые периодически шлют HTTP-запросы для определения статуса сетевого подключения.

Вместо этого я хотел бы представить альтернативный подход, который использует нативный фреймворк, представленный в iOS 12.

Для этой реализации нам понадобится только фреймворк Network. Хотя вы обычно используете его, когда вам нужен прямой доступ к таким протоколам, как TLS, TCP и UDP, мы не будем делать здесь ничего слишком сложного.

Начальная реализация

Давайте начнем создание нашей утилиты NetworkMonitor:

import Network  final class NetworkMonitor {   static let shared = NetworkMonitor()   private let monitor: NWPathMonitor    private init() {       monitor = NWPathMonitor()   }  }

Здесь NWPathMonitor является наблюдателем, который будет отслеживать состояние сетевого соединения и реагировать на изменения, которые могут произойти.

Далее, мы создадим несколько свойств для хранения текущего состояния сетевого соединения:

final class NetworkMonitor {   static let shared = NetworkMonitor()   private let monitor: NWPathMonitor    private(set) var isConnected = false    /// Следующее свойство нужно для проверки, что сетевое соединение   /// будет дорогим в плане потребления трафика   ///   /// Сотовые интерфейсы считаются дорогими. WiFi точки доступа   /// от других девайсов также могут быть дорогими. Другие интерфейсы   /// могут оказаться дорогими в будущем   private(set) var isExpensive = false    /// curentConnectionType указывает на тип текущего соединения   /// в сети, к которой мы подключены   ///   /// Возможные состояния могут быть `other`, `wifi`, `cellular`,    /// `wiredEthernet`, or `loopback`   private(set) var currentConnectionType: NWInterface.InterfaceType?    private init() {       monitor = NWPathMonitor()   }  }

Поскольку эти свойства могут быть только read-only, используется private(set).

Мы явно не хотим, чтобы эта длительная задача выполнялась в главном потоке нашего приложения, поэтому создадим новую очередь DispatchQueue для управления этой задачей:

private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")

Сетевой фреймворк определяет перечисление NWInterface.InterfaceType, которое содержит все типы медиа, которые может поддерживать наше устройство (WiFi, сотовая связь, проводной ethernet и т.д.).

Поскольку это перечисление объявлено в ObjC, у нас нет доступа к свойству allCases, как, например, в случае с перечислениями в Swift. Поэтому добавим соответствие протоколу CaseIterable и реализуем allCases. В результате этого дополнительного шага остальная часть нашей реализации будет намного проще и гораздо более читабельнее.

extension NWInterface.InterfaceType: CaseIterable {   public static var allCases: [NWInterface.InterfaceType] = [   .other,   .wifi,   .cellular,   .loopback,   .wiredEthernet   ] }

Последним шагом в нашей реализации является создание функций, отвечающих за запуск и остановку процесса мониторинга:

func startMonitoring() {   monitor.pathUpdateHandler = { [weak self] path in   self?.isConnected = path.status != .unsatisfied   self?.isExpensive = path.isExpensive   // Identifies the current connection type from the   // list of potential network link types   self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first   } monitor.start(queue: queue) }  func stopMonitoring() { monitor.cancel() } 

NetworkMonitor в действии

Отслеживание можно запустить из любой точки кода, просто вызвав NetworkMonitor.shared.startMonitoring(), хотя в большинстве случаев вы захотите инициировать этот процесс в AppDelegate. Затем мы можем использовать NetworkMonitor.shared.isConnected для проверки состояния нашего сетевого соединения в режиме реального времени.

Вот наша реализация на данный момент:

import Network  extension NWInterface.InterfaceType: CaseIterable {     public static var allCases: [NWInterface.InterfaceType] = [         .other,         .wifi,         .cellular,         .loopback,         .wiredEthernet     ] }  final class NetworkMonitor {     static let shared = NetworkMonitor()      private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")     private let monitor: NWPathMonitor  private(set) var isConnected = false private(set) var isExpensive = false private(set) var currentConnectionType: NWInterface.InterfaceType?      private init() {         monitor = NWPathMonitor()     }      func startMonitoring() {         monitor.pathUpdateHandler = { [weak self] path in             self?.isConnected = path.status != .unsatisfied             self?.isExpensive = path.isExpensive             self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first         }         monitor.start(queue: queue)     }      func stopMonitoring() {         monitor.cancel()     } } 

Добавление поддержки NotificationCenter

Поведение современных приложений для iOS резко меняется при отключении сетевого соединения устройства — на некоторых экранах может появиться уведомление о том, что устройство потеряло соединение, может измениться поведение кэширования приложения или даже нарушатся некоторые пользовательские сценарии.

Для поддержки такого типа поведения нам необходимо расширить нашу реализацию, чтобы отправлять уведомления по всему приложению при изменении статуса подключения.

import Foundation import Network  extension Notification.Name {     static let connectivityStatus = Notification.Name(rawValue: "connectivityStatusChanged") }  extension NWInterface.InterfaceType: CaseIterable {     public static var allCases: [NWInterface.InterfaceType] = [         .other,         .wifi,         .cellular,         .loopback,         .wiredEthernet     ] }  final class NetworkMonitor {     static let shared = NetworkMonitor()      private let queue = DispatchQueue(label: "NetworkConnectivityMonitor")     private let monitor: NWPathMonitor      private(set) var isConnected = false     private(set) var isExpensive = false     private(set) var currentConnectionType: NWInterface.InterfaceType?      private init() {         monitor = NWPathMonitor()     }      func startMonitoring() {         monitor.pathUpdateHandler = { [weak self] path in             self?.isConnected = path.status != .unsatisfied             self?.isExpensive = path.isExpensive             self?.currentConnectionType = NWInterface.InterfaceType.allCases.filter { path.usesInterfaceType($0) }.first                          NotificationCenter.default.post(name: .connectivityStatus, object: nil)         }         monitor.start(queue: queue)     }      func stopMonitoring() {         monitor.cancel()     } }  // ViewController.swift class ViewController: UIViewController {      override func viewDidLoad() {         super.viewDidLoad()         NotificationCenter.default.addObserver(self, selector: #selector(showOfflineDeviceUI(notification:)), name: NSNotification.Name.connectivityStatus, object: nil)     }      @objc func showOfflineDeviceUI(notification: Notification) {         if NetworkMonitor.shared.isConnected {             print("Connected")         } else {             print("Not connected")         }     } }

Весь исходный код находится здесь.

Network Link Conditioner

Раз уж мы заговорили о сетевых технологиях и отладке проблем с подключением, самое время упомянуть о Network Link Conditioner.

С помощью этого инструмента можно имитировать различные сетевые условия на компьютере и, соответственно, в симуляторе iOS. С помощью этого инструмента мы можем не только отслеживать экстремальные ситуации, когда мы полностью онлайн или оффлайн, но и тестировать поведение нашего приложения в различных сетевых условиях.

Вы можете загрузить его с сайта разработчиков Apple или по этой ссылке.

Сама утилита будет находиться в папке Hardware, для установки просто кликните по ней.

Установка Network Link Conditioner
Установка Network Link Conditioner

После чего вы сможете настраивать нужные вам параметры, как и на реальном устройстве здесь:

Пример настройки для Network Link Conditioner
Пример настройки для Network Link Conditioner

Полезные ресурсы

  • Статья с разбором примера приложения для отслеживания сетевого соединения.

  • Пример работы для Network Link Conditioner

  • Страница с дополнительными утилитами от Apple.


Больше историй, подходов к реализации и инструментов для iOS-разработчика можно найти в авторском канале об iOS-разработке.

Канал об iOS-разработке
Канал об iOS-разработке


ссылка на оригинал статьи https://habr.com/ru/post/661549/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *