Avatar ·

Проверка наличия интернет-соединения в Swift — простое и надёжное решение

Всем привет! Я новичок в Swift и iOS-разработке. Пишу приложение, которое загружает данные из сети, но столкнулся с проблемой: если интернет отключён, приложение просто зависает или падает с ошибкой. Мне нужно проверять наличие интернет-соединения перед тем, как делать запрос. Я нашёл старую библиотеку Reachability от Apple, но она на Objective-C и, как я понял, уже устарела. Пытался использовать её через bridging header, но запутался. Также слышал про NWPathMonitor из Network.framework, но не понимаю, как правильно его внедрить в своё приложение. Вот пример моего кода, где я пытаюсь загрузить данные без проверки: ```swift import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() fetchData() } func fetchData() { let url = URL(string: "https://api.example.com/data")! let task = URLSession.shared.dataTask(with: url) { data, response, error in if let data = data { print("Data received: \(data)") } else if let error = error { print("Error: \(error.localizedDescription)") } } task.resume() } } ``` Проблема в том, что если интернет отключён, completion handler вызывается с ошибкой, но это происходит не сразу, и пользователь видит пустой экран. Хочу показывать алерт или сообщение «Нет интернета» до выполнения запроса. Как правильно реализовать проверку интернет-соединения в современном Swift (iOS 12+)? Желательно без сторонних библиотек, только нативные средства. <<>>

Использование NWPathMonitor для проверки интернет-соединения

Начиная с iOS 12, Apple рекомендует использовать NWPathMonitor из фреймворка Network для мониторинга сетевого соединения. Это современная и простая в использовании замена устаревшей Reachability. Вот пошаговое решение.

1. Создаём менеджер сетевого мониторинга

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

import Foundation
import Network

final class NetworkMonitor {
    static let shared = NetworkMonitor()
    
    private let monitor = NWPathMonitor()
    private let queue = DispatchQueue.global(qos: .background)
    
    private(set) var isConnected: Bool = false
    private(set) var connectionType: ConnectionType = .unknown
    
    enum ConnectionType {
        case wifi
        case cellular
        case ethernet
        case unknown
    }
    
    private init() {
        startMonitoring()
    }
    
    func startMonitoring() {
        monitor.pathUpdateHandler = { [weak self] path in
            self?.isConnected = path.status == .satisfied
            self?.getConnectionType(path)
        }
        monitor.start(queue: queue)
    }
    
    func stopMonitoring() {
        monitor.cancel()
    }
    
    private func getConnectionType(_ path: NWPath) {
        if path.usesInterfaceType(.wifi) {
            connectionType = .wifi
        } else if path.usesInterfaceType(.cellular) {
            connectionType = .cellular
        } else if path.usesInterfaceType(.wiredEthernet) {
            connectionType = .ethernet
        } else {
            connectionType = .unknown
        }
    }
}

2. Используем в ViewController

Теперь можно проверять соединение перед запросом:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Начинаем мониторинг (обычно в AppDelegate или SceneDelegate)
        // Но можно и здесь, если нужно только для этого экрана
    }
    
    @IBAction func fetchDataTapped() {
        guard NetworkMonitor.shared.isConnected else {
            showNoInternetAlert()
            return
        }
        fetchData()
    }
    
    func fetchData() {
        let url = URL(string: "https://api.example.com/data")!
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            DispatchQueue.main.async {
                if let data = data {
                    print("Data received: \(data)")
                } else if let error = error {
                    print("Error: \(error.localizedDescription)")
                }
            }
        }
        task.resume()
    }
    
    private func showNoInternetAlert() {
        let alert = UIAlertController(
            title: "Нет интернета",
            message: "Пожалуйста, проверьте подключение к сети и попробуйте снова.",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

3. Настройка при запуске приложения

Лучше всего запускать мониторинг один раз при старте приложения, например, в AppDelegate:

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Активируем мониторинг сети
        _ = NetworkMonitor.shared
        return true
    }
}

4. Дополнительные возможности

  • Реагирование на изменения: Подпишитесь на уведомления через NotificationCenter или используйте Combine для обновления UI в реальном времени.
  • Проверка типа соединения: Вы можете адаптировать поведение приложения (например, отключить загрузку больших файлов на сотовой сети).
  • Производительность: NWPathMonitor работает в фоновом потоке и не блокирует главный поток.

Это решение полностью нативное, не требует сторонних библиотек и работает на iOS 12+. Если вам нужно поддерживать более старые версии, придётся использовать устаревшую Reachability, но для современных проектов NWPathMonitor — лучший выбор.

<<>>

Альтернативный подход: простая проверка перед каждым запросом

Если вам не нужен постоянный мониторинг сети, а только проверка перед конкретным запросом, можно использовать простую функцию с NWPathMonitor в синхронном режиме. Однако помните, что это может заблокировать поток на короткое время.

import Foundation
import Network

func isInternetAvailable() -> Bool {
    let monitor = NWPathMonitor()
    let queue = DispatchQueue(label: "NetworkMonitor")
    var available = false
    let semaphore = DispatchSemaphore(value: 0)
    
    monitor.pathUpdateHandler = { path in
        available = path.status == .satisfied
        semaphore.signal()
    }
    monitor.start(queue: queue)
    semaphore.wait()
    monitor.cancel()
    return available
}

// Использование
if isInternetAvailable() {
    print("Интернет есть")
} else {
    print("Нет интернета")
}

Этот подход проще, но имеет недостатки:

  • Блокирует текущий поток (используйте только в фоновых потоках).
  • Не подходит для асинхронных операций.
  • Создаёт новый монитор при каждом вызове, что менее эффективно.

Рекомендую использовать первый вариант с синглтоном для production-кода, а этот — только для быстрых прототипов или тестирования.